メモリリークを出力させてみましょう(2)
前回は、VC++で提供されているメモリリークのチェックを用いました。
ただ、このメモリリークのチェックは、Qtとは、基本的に、整合性がとれないために
正しく、出力でされるとは限らないことは、前回、少し、記述しました。
では、他にやり方はないでしょうか。
Windowsで実施する場合、WinDbgでメモリリークを見つける方法は、無料で行うための
手段として、よく知られるところだと思います。
ただ、VC++に慣れてしまった人間としては、WinDbgのコマンドからスクリプトまで
(コマンドもスクリプトも同じようなものですが)を行うのに、少々、時代に逆行しているようで
なんとも組したくないものでもあります。
では、WinDbg以外で何か良い方法はないものでしょうか。
実は、参考記事のリンク先のメモリリーク検出ライブラリが、それです。
私が使った感じでは、特別、不適切な出力はありませんでしたので、少し、紹介したいと思います。
(もちろん、バグはあると思いますが・・・。)
前置きが長くなりましたが、早速、試してみましょう。
まず、関連記事のWEBサイトからデモ用ソースコードをダウンロードしましょう。
先の参考記事の前者(codeproject)が、C++用です。後者(codeguru)が、C用です。
それぞれ、異なる作者ですので、それぞれやり方が違います。しかし、いずれも、ソースコードの変更は、ほぼありません。
(環境を取り込むための#includeぐらいです)
また、C++用(codeproject)は、DLL版をダウンロードします。
(VC++2008+Qtの環境では、スタティックリンクでは、エラーが多発します。)
ダウンロードした traceallocations.cpp の371行目を以下のように変更します。
(恐らくバグだと思います)
371
372
| // if (index <= MAXSTACK) {
if (index < MAXSTACK) {
|
VC++2008で、サンプルをコンパイルの上、実行してみてください。
以下のようなメッセージが出力されればOKです。
(ソースコードへのファイルパスは、それぞれの環境によって異なります。)
Leak of 4 bytes detected (452 bytes with headers and no mans land)
...\memleakfinder4dll\tracealloc.cpp(8) : operator new
...\memleakfinder4dll\main.cpp(12) : generate_memoryleak
...\memleakfinder4dll\main.cpp(19) : main
...\crt_bld\self_x86\crt\src\crt0.c(266) : __tmainCRTStartup
...\crt_bld\self_x86\crt\src\crt0.c(182) : mainCRTStartup
0x0012B7A4 : _BaseProcessStart@4
では、実際に前回の記事で扱ったソースコードを確認してみましょう。
Qtアプリ側のVC++プロジェクトを変更してしまうと、Qtプロジェクトとの整合性をなくしてしまいますので
ここでは、ソースコードを以下のように変更して、対応します。
Qtアプリ側へ、tracealloc.cpp を取り込み、MemLeakDll.libをLIBリンクします。
このとき、先のDLLのプロジェクトから、以下のファイルをQtアプリ側のVC++プロジェクトディレクトリの配下へコピーします。
- tracealloc.cpp
- MemLeakFindDll.h
- MemLeakDll.lib
- MemLeakDll.dll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| #if !defined(QT_NO_DEBUG) && defined(WIN32)
#include <crtdbg.h>
#endif
#include "QApplication"
#include "QPushButton"
int main(int argc, char *argv[])
{
#if !defined(QT_NO_DEBUG) && defined(WIN32)
_CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF );
#endif
QApplication app(argc, argv);
QPushButton hello("Hello world");
hello.resize(100, 30);
// メモリリーク
int *pint=new int[10];
hello.show();
return app.exec();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| #if !defined(QT_NO_DEBUG) && defined(WIN32)
#define DETECT_LEAKS
#include "tracealloc.cpp"
#pragma comment(lib, "MemLeakDll.lib")
#endif
#include "QApplication"
#include "QPushButton"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton hello("Hello world");
hello.resize(100, 30);
// メモリリーク
int *pint=new int[10];
hello.show();
return app.exec();
}
|
準備ができたら、コンパイルして実行してみてください。
先のデモと同様、出力エリアに"Leak of 40 bytes detected "のメッセージが出力されたことと思います。
同じように、先の記事の以下の3つのファイルからなるサンプルでも同様に確認してみましょう。
- MyWidget.h
-- メイン画面(ヘッダ)で、終了するボタンを一つだけ持ちます。
- MyWidget.cpp
-- メイン画面(ソース)で、終了するボタンを一つだけ持ちます。
- sample.cpp
-- メイン画面を表示して、終了するだけです。
手を加えるのは、先のサンプルと同様 sample.cpp のみです。
[MyWidget.h]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| #ifndef _MYWIDGET_H_
#define _MYWIDGET_H_
#include "QApplication"
#include "QPushButton"
#include "QVBoxLayout"
////////////////////////////////////////
// メモリーリーク検証のためのデータクラス
class LocalData
{
public:
LocalData();
int m_ndata;
};
////////////////////////////////////////
// メモリーリーク検証のための画面クラス
class MyWidget : public QWidget
{
public:
MyWidget( QWidget *parent=0 ) ;
QPushButton *m_pquit; // ボタンをnewする
QVBoxLayout *m_playout; // レイアウトをnewする
QPushButton *m_pquit2; // ボタンをnewする
LocalData *m_plocaldata; // ローカルデータをnewする
};
#endif
|
10-15行目は、メモリリーク検証用にQtに依存しないクラスを定義しています。
[MyWidget.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| #include "MyWidget.h"
////////////////////////////////////////
// メモリーリーク検証のためのデータクラス
LocalData::LocalData ()
{
m_ndata=99;
}
////////////////////////////////////////
// メモリーリーク検証のための画面クラス
MyWidget::MyWidget( QWidget *parent) : QWidget( parent, 0 )
{
setMinimumSize( 200, 100 );
setMaximumSize( 400, 400 );
// ボタンをnewする
m_pquit = new QPushButton( "Quit", this );
// レイアウトをnewする
m_playout = new QVBoxLayout;
m_playout->addWidget(m_pquit);
setLayout(m_playout);
// 関連のないボタンをnewする
// *****このオブジェクトは、メモリーリークと判断される
m_pquit2 = new QPushButton( "Quit2");
// ローカルデータをnewする
// *****このオブジェクトは、メモリーリークと判断される
m_plocaldata = new LocalData;
Q_ASSERT(m_plocaldata->m_ndata==99);
connect( m_pquit, SIGNAL(clicked()), qApp, SLOT(quit()) );
setWindowTitle(QString ("test dialog"));
}
|
19行目は、実際にこの画面に貼り付ける"Quit"ボタンを作成(new)しています。
28行目は、この画面に貼り付けない"Quit2"ボタンを作成(new)しています。
32行目は、Qtに依存しませんが、この画面のメンバーとして個別のクラスを生成(new)しています。
[sample.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #if !defined(QT_NO_DEBUG) && defined(WIN32)
#include <crtdbg.h>
#endif
#include "QApplication"
#include "QPushButton"
#include "MyWidget.h"
int main(int argc, char *argv[])
{
#if !defined(QT_NO_DEBUG) && defined(WIN32)
_CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF );
#endif
QApplication app(argc, argv);
MyWidget hello;
hello.show();
return app.exec();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #if !defined(QT_NO_DEBUG) && defined(WIN32)
#define DETECT_LEAKS
#include "tracealloc.cpp"
#pragma comment(lib, "MemLeakDll.lib")
#endif
#include "QApplication"
#include "QPushButton"
#include "MyWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget hello;
hello.show();
return app.exec();
}
|
17,19行目は、画面を表示しているだけです。
準備ができたら、コンパイルして実行してみてください。
先のデモと同様、出力エリアに"Leak of 40 bytes detected "のメッセージが出力されたことと思います。
また、
[MyWidget.cpp]の26-33行目までをコメントアウトして、同様にデバッグ実行してみると結果は、今度はメモリリークが何も
出力されなくなります。
上々のできだと思います。先の記事でも問題としていましたアイコンのメニューやツールバーでの表示でも
同じようにうまくいきます。(サンプルのdebugmemory3を参照してください)
このサンプルで、前回の記事の方法では、メモリーリークが検出されていましたが、今回の方法では、正しくエラーなしとなります。
現在でも、いろんな形でこのメモリリーク検出ライブラリを使っていますが、正しく動作しているように思います。
ただ、このライブラリで検出できるのは、new,deleteを使ってのメモリ管理したできません。
実際にCのmalloc,freeなどを使った検出を行うには、参考記事(codeguru)を使って行うことができます。ただ、このライブラリは、ファイルに出力しますので、
デバッグ情報の出力エリアへ、先に試したライブラリを元に変更を加えて利用すれば、ほぼ、MFCの環境と同じくらいのメモリリーク検出が行えるようになると思います。
※C側は、やり方が少し異なります。malloc,freeなどを使うところで、ヘッダをincludeしてあげる必要があります。
※CPP側は、一度、取り込んでおけば、同一プロジェクト内では、全て同じように検出してくれます。
いかがだったでしょうか。これで、デバッグも、無料の環境が、ほぼできましたので、Qtアプリを作成できますね。
もっと、Qt関連について詳しく知りたい方は、以下の本なども良いと思います。
Qtに関する日本語の本が少ないですね。「入門書」は、さすがに、このページを読まれるくらいの方は不要だと思います。
やっぱり、本+ネット+試してみる!!の3本柱でやっていく以外にないように思います。
コメントをどうぞ