Solved

Breakpoint reached in the _heap_alloc_dbg function

Posted on 2004-08-10
16
2,841 Views
Last Modified: 2013-11-20

Hi Experts,

I have a fairly complicated application that stops working after 2-3 days of continuous run. The place where it stops is always the same: the _heap_alloc_dbg function in the DBGHEAP.C file. We run the Debug version because we suspect another bug in the code that brings the Release version down after a much longer period of running.
The small Message Box tells me that "User breakpoint called from code at 0x10212ad0".
The code for this function is:

void * __cdecl _heap_alloc_dbg(
        size_t nSize,
        int nBlockUse,
        const char * szFileName,
        int nLine
        )
{
        long lRequest;
        size_t blockSize;
        int fIgnore = FALSE;
        _CrtMemBlockHeader * pHead;

        /* verify heap before allocation */
        if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF)
            _ASSERTE(_CrtCheckMemory());

        lRequest = _lRequestCurr;

        /* break into debugger at specific memory allocation */
        if (lRequest == _crtBreakAlloc)
            _CrtDbgBreak();
// here is the place were the app stops

// ... function continuous

Because both lRequest and _crtBreakAlloc are -1, the _CrtDbgBreak() is called and brings that small Message Box.
Looking at the context, I can see that everything happens during a static allocation for the same type. This is a complex type that embeds other complex types. The last constructor that brings me to the _heap_alloc_dbg belongs to a type that has a member that is defined as

std::list<_component_type *> _lstStoredList;

I have the feeling that the framework uses the wrong allocation routines and this causes all the trouble.
At this time I really don’t know what else to try. Any suggestions would be greatly appreciated.

Regards,
Eddie

0
Comment
Question by:Mensana
  • 7
  • 6
  • 2
  • +1
16 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 11763253
>>Because both lRequest and _crtBreakAlloc are -1, the _CrtDbgBreak() is called

Sounds like a 'signed long' overflow to me. Is your application doing a lot of allocations? And, have you tested the release build (which should work OK)?
0
 
LVL 13

Expert Comment

by:SteH
ID: 11763311
Is there enough space on the stack? In debug mode local storage (on the stack) takes more memory due to memory guards placed between allocated memory blocks.
0
 
LVL 1

Author Comment

by:Mensana
ID: 11763998

Hi jkr and SteH,

To answer you both:

(1) In Release this problem doesn't manifest itself, but the App crashes after a longer period of time and we suspect is because of a different bug. In order to find it, we wanted to run the debug version all the way to the point where it crashes. What makes you think the "singned long" overflow should be blamed?

(2) At the crash moment, in Task Manager I could see that the app has 16MB of allocated memory whereas the Peak Memory Usage is slightly above 17MB. I have other apps that take way more so I simply don't think that I run out of memory.
0
 
LVL 86

Expert Comment

by:jkr
ID: 11764161
>>What makes you think the "singned long" overflow should be blamed?

_crtBreakAlloc is initialized to -1 in dbgheap.c

_CRTIMP long _crtBreakAlloc = -1L;

and _lRequestCurr is incremented in every call of '_heap_alloc_dbg()'.
0
 
LVL 1

Author Comment

by:Mensana
ID: 11765644

jkr,

OK, I got what you are trying to say. In other words, the _heap_alloc_dbg is called too many times, right? Should we try to limit (decrease) the number of static allocations?

Cheers,
Eddie
0
 
LVL 86

Expert Comment

by:jkr
ID: 11765681
>>In other words, the _heap_alloc_dbg is called too many times, right?

Not too many times, but more often than they expected it to be called.

>>Should we try to limit (decrease) the number of static allocations?

This is always a good idea, since unless you are allocating/freeing objects of the same size over and over again, the heap becomes fragmented.
0
 
LVL 1

Author Comment

by:Mensana
ID: 11765864
Hold on,

I was confusing in my questions! It's not a matter of limiting the static allocations, but the dynamic ones. I kept saying "static" because the problem appears while static allocating an object. Like I said in my original posting, there are a lot of nested classes and I guess at some point there might be some dynamic allocation. It has to be, that's why we end up visiting the _heap_alloc_dbg function.
Thanks for the tip. I'll look into it and will let you know about my findings. This might be the case.

Cheers,
Eddie
0
 
LVL 86

Expert Comment

by:jkr
ID: 11765905
BTW, you clearly have found a bug in the CRT here - that should not happen.
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 1

Author Comment

by:Mensana
ID: 11766064
Hey jkr,

I checked and I don't think the scenario you thought about can really happen. Here is why:

I created a small test program where I declared a long variable and I initialized with value 0x7FFFFFFF. This is the biggest value that you can store into a signed long (2,147,483,648). Then I incremented it to simulate the rollover you mentioned. The variable went to 0x80000000 but this is -2,147,483,648 and not -1.
Even so, I don't think my program requests over 2 billion allocation blocks. That's insane.

It must be something else.

Regards,
Eddie
0
 
LVL 86

Accepted Solution

by:
jkr earned 500 total points
ID: 11766701
>>Even so, I don't think my program requests over 2 billion allocation blocks. That's insane

Well, over the time...

But, there indeed seems to be something else going wrong, a simple test using

#include <limits.h>

void main () {

    char* p;

    for ( unsigned int i = 0; i < UINT_MAX; ++i) p = new char [8], delete [] p;
}

vouldn't reproduce that behaviour...
0
 
LVL 1

Author Comment

by:Mensana
ID: 11766927

Right on,

I should have thought of that.
Here is a link that describes a similar problem with a solution I can't find any reference to on the MSDN site:

http://www.mico.org/pipermail/mico-devel/2002-January/003491.html

Cheers,
Eddie
0
 
LVL 86

Expert Comment

by:jkr
ID: 11767064
LOL! As I said, you clearly have found a bug in the CRT here...
0
 
LVL 13

Expert Comment

by:SteH
ID: 11770481
The stack has a fixed amount of memory allocated set via project options. When it is used up you are overwriting code or data portions. Are you allocating all your variables/objects on the stack (static allocation)?
0
 
LVL 1

Author Comment

by:Mensana
ID: 11772724

>>But, there indeed seems to be something else going wrong, a simple test using
>>#include <limits.h>
>>void main () {
>>
>>    char* p;
>>
>>    for ( unsigned int i = 0; i < UINT_MAX; ++i) p = new char [8], delete [] p;
>>}
>>
>>vouldn't reproduce that behaviour...

Are you sure your project settings are so that the _heap_alloc_dbg function was called? I am just trying your sample on my machine as I type and I can see the _lRequestCurr counter incrementing. It takes approximately 4 hours to reach the -1 value. I traced inside the _heap_alloc_dbg function and I saw the counter reaching 200 millions. Eventually it will get up to 2 billions and than it will start from -2 billions all the way to -1. Have you tried it all the way to the end? Like I said, it takes about 4 hours to complete, according to my calculations.

>>The stack has a fixed amount of memory allocated set via project options. When it is used up >>you are overwriting code or data portions. Are you allocating all your variables/objects on the >>stack (static allocation)?

Most of the time there are static allocations, but the memory occupied by the process only goes to about 16MB which is a far cry from other processes that keep running w/o problems even at 200MB. I think we just reached a limitation imposed in the Debug version of the Microsoft CRT libraries.
0
 
LVL 1

Author Comment

by:Mensana
ID: 11775864
OK,

I let jkr's code to run for 3 hours and 38 minutes and the app stopped, letting me know that a break point was reached.
At this moment it is clear that this is a limitation in the debug library.
Without solving my problem, jkr's code helped me understanding it better. Here are points. Thank you both.

Cheers,
Eddie
0
 

Expert Comment

by:FNICOLAS
ID: 12235993

                        _heap_alloc_debug will crash all your applications.


This apply for Visual C 6.0, any service pack.


malloc() allocate memory on the base heap (see "Memory Management and the Debug Heap"
in MSDN Library October 2001). In a debug build, malloc() calls _malloc_dbg(),
_nh_malloc_dbg() and finally _heap_alloc_dbg().

_heap_alloc_dbg() maintains a static long counter, _lRequestCurr (see Microsoft visual
Studio\VC98\Crt\Src\dbgheap.c)

This static long counter is initialized at 1.

Each time malloc() is called, this counter is incremented by 1. So if your application
 is running long enough, it could oscillate between -2,147,483,648 to 2,147,483,647.

This counter can be used as a debug tool by the user to inspect the allocation
behavior of its application, calling various debug functions (_CrtIsMemoryBlock(),
 _CrtSetBreakAlloc(), etc., all well documented in MSDN Library October 2001)

So long, everything is fine in that wonderful Microsoft world.

The problem is that there is the following piece of code in _heap_alloc_dbg():

        /* break into debugger at specific memory allocation */
        if (lRequest == _crtBreakAlloc)
            _CrtDbgBreak();


Where lRequest  is initialized with _lRequestCurr, and _crtBreakAlloc is initialized
at -1 when the application starts.

So when the application starts, every malloc() increase lRequest (_lRequestCurr) by 1,
starting at 1. After a while (Some 6 hours in my case), lRequest (_lRequestCurr) reach
2,147,483,647. The next call to malloc add 1, and in binary arithmetics on a signed
long, 2,147,483,647 + 1 = -2,147,483,648... So for another while the next malloc()
increase lRequest (_lRequestCurr) from -2,147,483,648 toward -1, _crtBreakAlloc value.
And when lRequest (_lRequestCurr) finally reach this value, _CrtDbgBreak() fires, and
phones start ro ring madly.

_CrtDbgBreak() display the exception message and stop the application. In my case, the
whole process take some 13h10mn.

So as soon as malloc() is repeatedly used, the debug version of any application MUST
crash if nothing is done. It's just a question of time.


Note: the Visual 6 service pack 6 (vs6sp6.exe) does not solve that problem, so there
is no Microsoft solutions for Visual 6 devloppers. In Visual.net, running under XP,
dghheap.c code is different, and more clever.


                                         ***


Hopefully, Microsoft provide us two documented functions to resolve that problem.

The first, _CrtSetBreakAlloc(), let us initialize _crtBreakAlloc to any
values between -2,147,483,648 to 2,147,483,647.

The second, _CrtIsMemoryBlock() provide an indirect way to know what is the current
value of lRequest (_lRequestCurr). The following example is derived from the
example in "_CrtSetBreakAlloc", MSDN Library October 2001:


        long lRequest;
        my_pointer = malloc(10);
        _CrtIsMemoryBlock(my_pointer, 10, &lRequest, NULL, NULL);
        free(my_pointer);


We just have to initialize regulary _crtBreakAlloc to values far enough from lRequest
to avoid the fatal

 
           if (lRequest == _crtBreakAlloc)
            _CrtDbgBreak();


This can be done using a timer or any other mean to run at a sufficient frequency
(i.e. before lRequest == _crtBreakAlloc, or lRequest == -1) the following code:


        #include LIMITS.H

        long lRequest;
        long NewCrtBreakAlloc;

        my_pointer = malloc(10);
        _CrtIsMemoryBlock(my_pointer, 10, &lRequest, NULL, NULL);
        free(my_pointer);
       
        if (lRequest > -1)
          NewCrtBreakAlloc = LONG_MIN / 2;
        else
          NewCrtBreakAlloc = LONG_MAX / 2;
       
        _CrtSetBreakAlloc(NewCrtBreakAlloc);      
       
       
       
This way,  lRequest == _crtBreakAlloc will never happen again.      
       



0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

Introduction: Dynamic window placements and drawing on a form, simple usage of windows registry as a storage place for information. Continuing from the first article about sudoku.  There we have designed the application and put a lot of user int…
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This video discusses moving either the default database or any database to a new volume.

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now