ホーム

OFF-SOFT.net

OFF-SOFT.net

ウェブやソフトウェアに関するサポート&情報サイトです。サイト構築からソフトウェアの作成、利用まであなたの助けになるかも・・・・しれません。たぶん・・。

QtアプリをVC++ 2008 Expressでデバッグする(2)

公開日| 2009年05月14日 | コメントはまだありません。
概要 :
 Qtアプリのデバッグ方法について、簡単ながら、先日、記事を書きました。(以降の関連記事を参照)
 ただ、その中で、やはりメモリリークに関する記事内容については、一部、補足する必要があろうかと思いましたので (2)の記事を書くことにしました。
 Qtで、正確なメモリリークの確認を行うには、(少なくともWindowsでは、)市販のツールを使うのが最も良い選択であろうかと思います。 ただ、市販ツールも高価で手が出せない方(私もそうですが)は、何か良い方法はないものか・・・という思いもあり、 私が、最近、やっていることについて、記述してみようよ思います。

関連記事: QtアプリをVC++ 2008 Expressでデバッグする

参考記事: http://www.codeproject.com/KB/debug/Memory_leak_finder.aspx
参考記事: http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

ここで使用したサンプルソースコード:


メモリリークを出力させてみましょう(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本柱でやっていく以外にないように思います。


コメント

コメントをどうぞ







  • はてなブックマークへ追加する
  • Facebookでシェアする
  • twitter でつぶやく
  • Google Plusでシェアする
  • Pocketでシェアする
ページトップへ