1

4 年前の VC++ 6.0 プログラムに拡張機能を追加しようとしています。デバッグ ビルドはコマンド ラインから実行されますが、デバッガーでは実行されません。printf() 内のアクセス違反でクラッシュします。printf をスキップすると、malloc() (fopen() 内から呼び出される) でクラッシュし、それをスキップできません。

これは、デバッガーで実行できず、何が起こっているかを確認するために古い printf ステートメントに頼らなければならないことを意味します。これは明らかにそれをはるかに難しくします。

VC++ デバッガーで実行すると printf() と malloc() が失敗する理由は何ですか? 私はこの低レベルのことは得意ではありません !

アクセス違反後のコール スタックは次のとおりです。

_heap_alloc_dbg(unsigned int 24, int 2, const char * 0x0046b3d8 `string', int 225) line 394 + 8 bytes
_nh_malloc_dbg(unsigned int 24, int 0, int 2, const char * 0x0046b3d8 `string', int 225) line 242 + 21 bytes
_malloc_dbg(unsigned int 24, int 2, const char * 0x0046b3d8 `string', int 225) line 163 + 27 bytes
_lock(int 2) line 225 + 19 bytes
_getstream() line 55 + 7 bytes
_fsopen(const char * 0x00468000 `string', const char * 0x00466280 `string', int 64) line 61 + 5 bytes
fopen(const char * 0x00468000 `string', const char * 0x00466280 `string') line 104 + 15 bytes
open_new_log(const char * 0x00468000 `string') line 66 + 14 bytes
log_open(const char * 0x00468000 `string', int 0) line 106 + 9 bytes
Xlog_open(const char * 0x00468000 `string', int 0) line 51 + 13 bytes
service_start(unsigned long 1, char * * 0x009a0e50) line 3152 + 12 bytes
service_init2(char * 0x00471fcc char * NTPROGRAM, char * 0x004723c4 char * NTSERVICE, char * 0x00466540 `string', unsigned long 1, char * * 0x009a0e50) line 508 + 13 bytes
service_init(char * 0x00471fcc char * NTPROGRAM, char * 0x004723c4 char * NTSERVICE, unsigned long 2, char * * 0x009a0e50) line 548
main(unsigned long 2, char * * 0x009a0e50) line 3131
mainCRTStartup() line 206 + 25 bytes
KERNEL32! 7c817067()

失敗する操作までのデバッグ逆アセンブリを次に示します。

0041EA7E   jmp         _heap_alloc_dbg+2B3h (0041eb23)
0041EA83   mov         edx,dword ptr [_lTotalAlloc (004b4294)]
0041EA89   add         edx,dword ptr [nSize]
0041EA8C   mov         dword ptr [_lTotalAlloc (004b4294)],edx
0041EA92   mov         eax,[_lCurAlloc (004b429c)]
0041EA97   add         eax,dword ptr [nSize]
0041EA9A   mov         [_lCurAlloc (004b429c)],eax
0041EA9F   mov         ecx,dword ptr [_lCurAlloc (004b429c)]
0041EAA5   cmp         ecx,dword ptr [_lMaxAlloc (004b42a0)]
0041EAAB   jbe         _heap_alloc_dbg+249h (0041eab9)
0041EAAD   mov         edx,dword ptr [_lCurAlloc (004b429c)]
0041EAB3   mov         dword ptr [_lMaxAlloc (004b42a0)],edx
0041EAB9   cmp         dword ptr [_pFirstBlock (004b4298)],0
0041EAC0   je          _heap_alloc_dbg+25Fh (0041eacf)
0041EAC2   mov         eax,[_pFirstBlock (004b4298)]
0041EAC7   mov         ecx,dword ptr [pHead]
0041EACA   mov         dword ptr [eax+4],ecx

fopen() を呼び出して malloc() で失敗するソースは次のとおりです。

FILE *open_new_log( const char *logfile )
{
    FILE *fp;
    int retry = 0;

    while( ( fp = fopen( logfile, "w" ) ) == NULL && ++retry < 300 )
        Sleep( 1000 );

    return( fp );
}

私が得るエラーは

Unhandled exception inPISCOOP.exe: 0xC00000005: Access Violation

よろしく、

--- アリステア。

4

6 に答える 6

3

を使用_CrtSetDbgFlag()して、便利なヒープデバッグ手法を有効にすることができます。問題がどこにあるかを追跡するのに役立つ他のCRTデバッグ機能が多数あります。

于 2008-12-17T15:34:47.973 に答える
3

デバッガーから実行する場合、別のヒープが使用されます。これは、デバッグヒープと呼ばれます。これは、デバッガーの外部で使用されるヒープとは異なる動作をし、このような問題をキャッチするのに役立ちます。

Win32の「デバッグヒープ」はVC++の「デバッグヒープ」とは異なることに注意してください。ただし、どちらもほぼ同じことを行うことを目的としています。デバッガーでアプリを実行したときの動作の違いについて説明しているこの記事を参照してください。

この場合、ヒープブロックの最後または最初を書き留めることにより、この関数を呼び出す前にヒープが破損している可能性があります。

于 2008-12-17T15:05:16.817 に答える
2

最も簡単な方法は (アプリケーションがメモリを過度に使用していない場合)、フル ページ ヒープ チェックを有効にすることです (これにより、割り当て元のメモリ ページの後にいわゆるガード ページが配置され、正確な場所が特定されます)。破損が発生するコード内の場所)。

Windows デバッグ ツールが手元にある場合は、次の gflags コマンドを実行してフル ページ ヒープを構成します。

gflags[.exe] /p /enable yourapp.exe /full

実行可能ファイル名のみを指定する必要があることに注意してください(つまり、パスの接頭辞なしで!)。
それから、デバッガーで実行します。ヒープを破壊しようとする最初の試みで壊れます。ここでの違いは、ヒープの破損はほとんどが遅延した欠陥であり、(おそらく) 有効なヒープ操作が有効になっているときに後で現れるということです。

次の点にも注意してください。

gflags[.exe] /p /enable yourapp.exe /full /backwards

は、割り当ての前にガード ページを追加で配置します。

/p スイッチのみを指定して実行すると、現在有効なヒープ ページ オプションが表示されます。

于 2008-12-17T16:26:56.840 に答える
1

jmattiasは正しいと思います。問題の根本的な原因は、クラッシュしている場所以外の場所にある可能性があります。

多くのことがヒープの破損を引き起こす可能性があります。

  • 割り当てられたメモリブロックの終わり(または始まり)を超えて書き込みます。
  • ポインターを2回解放するか、割り当てられていないポインターを解放します。
  • プログラムをマルチスレッド化し、シングルスレッド(スレッドセーフではない)RTLとリンクします。
  • あるヒープからメモリを割り当て、別のヒープでそれを解放します。
  • などなど、

Visual C ++を使用しているため、デバッグヒープの_CrtSetDbgFlag()機能を使用して、エラーチェックをオンにすることができます。これを設定して、mallocまたはfreeを呼び出すたびにヒープの整合性をチェックできます。実行速度は非常に遅くなりますが、バグがどこにあるかを特定する必要があります。

コンパイラのドキュメントで_CrtSetDbgFlagを検索します。

于 2008-12-17T15:37:14.867 に答える
1

ヒープ破損のバグがある可能性があります。open_new_log()が呼び出される前に、アプリケーションがヒープを破損している可能性があります。

于 2008-12-17T15:05:25.833 に答える