Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Breakpoint reached in the _heap_alloc_dbg function

Posted on 2004-08-10
16
Medium Priority
?
2,924 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
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
 
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 1500 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

[Webinar] Lessons on Recovering from Petya

Skyport is working hard to help customers recover from recent attacks, like the Petya worm. This work has brought to light some important lessons. New malware attacks like this can take down your entire environment. Learn from others mistakes on how to prevent Petya like worms.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
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.
How to fix incompatible JVM issue while installing Eclipse While installing Eclipse in windows, got one error like above and unable to proceed with the installation. This video describes how to successfully install Eclipse. How to solve incompa…

609 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