views:

1669

answers:

6

I am trying to add enhancements to a 4 year old VC++ 6.0 program. The debug build runs from the command line but not in the debugger: it crashes with an access violation inside printf(). If I skip the printf, then it crashes in malloc() (called from within fopen()) and I can't skip over that.

This means I cannot run in the debugger and have to rely on the old printf statements to see what's going on. This obviously makes it a lot harder.

Any idea why printf() and malloc() would fail when running under the VC++ debugger? I am no good at this low level stuff!

Here is the call stack after the access violation:

_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()

Here is the debug dissassembly up to the operation that fails:

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

Here is our source for that calls fopen() and fails in 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 );
}

The error I get is

Unhandled exception inPISCOOP.exe: 0xC00000005: Access Violation

Regards,

--- Alistair.

+3  A: 

When run from the debugger, a different heap is used; this is referred to as the debug heap. This has different behaviour from the heap used outside the debugger, and is there to help you catch problems like this one.

Note that the Win32 "debug heap" is distinct from the VC++ "debug heap"; both are intended to do more or less the same thing, however. See this article which describes the difference in behaviour when you run the app under the debugger.

In this case, you have probably corrupted the heap before calling this function, either by writing off the end or off the start of a heap block.

Sunlight
The use of the debug heap vs the relase heap is determined by the type of C runtime library that the application is linked with not the fact that the code is being run in the debugger i.e. running the debug build of the app from the command line will use the debug heap.
jmatthias
AFAIK, you can toggle between debug heap and non-debug heap using the API, so it well may be that the debugger calls an API to switch it to the degug version. Other than that - yes, you probably corrupt the heap somewhere.
Cd-MaN
+1  A: 

You may have a heap corruption bug. Your application may have corrupted the heap before open_new_log() is called.

jmatthias
+3  A: 

You can use _CrtSetDbgFlag() to enable a bunch of useful heap debugging techniques. There's a host of other CRT debugging functions available that should help you track down where your problem is.

Adam Rosenfield
Many thanks for all these answers. I will try to have a look at this later. I think I have resolved the issue caused me to want to run it in the debugger so the prioity of this has gone down. Although I am obviously concerned that there may be hidden corruption going on in the release code.
+1  A: 

I suspect jmattias is right. The root cause of the problem is likely somewhere else than where it is crashing.

A lot of things can cause heap corruption.

  • writing past the end (or beginning) of the memory block that was allocated.
  • freeing a pointer twice, or freeing a pointer that wasn't allocated.
  • multithreading your program and linking with the single-threaded (not thread safe) RTL.
  • allocating memory from one heap and freeing it on another heap.
  • etc., etc.,

Since you're using Visual C++, you can use the _CrtSetDbgFlag() feature of the debug heap to turn on error checking. You can set it to check the integrity of the heap on each call to malloc or free. That will run very slowly, but it should pinpoint where the bug is for you.

Search for _CrtSetDbgFlag in the compiler docs.

Die in Sente
+1  A: 
A: 

I have a suspicion that there is a DLL compiled with a different version of the C++ runtime than the rest of the application. This will often result in "memory at address XXX could not be 'read'/'written'" violations.

Max Lybbert