Link to home
Start Free TrialLog in
Avatar of awd
awd

asked on

Bug in CString::AllocBuffer()???

When running an application of mine in debug mode (VC++ 6, I think 5 behaved the same way) I noticed that the output window said  "Detected memory leaks". I proceded to try and track down the source of the problem. What I finally found "appears" to be a bug in CString::AllocBuffer(). If you look in "strcore.cpp" at line 118 you will see memory for pData being allocated with the new operator. This does not appear to ever be freed with delete() or anything else for that matter. Am I missing something or is this a bug? I have searched the KB and not found anything on this. If I am incorrect, please show me where this memory is deallocated.
Avatar of jhance
jhance

The real question for me is why areyou calling AllocBuffer directly?

Even so, why would AllocBuffer free up memory?  If it did, then the CString that had been allocated with AllocBuffer would be gone.  If you are calling this member function, you will need to free the CString object youself.
Avatar of awd

ASKER

I am not calling the function directly. It is being called by the MFC framework in the normal process of allocating a CString. Are you familiar with the source code of CString and how it functions under the hood?
Every time I've had a memory leak involving CString, it was my fault.  Why don't you tell a little about what you are doing and let's see if we can find the problem.

I'm very confident that there is no systemic memory leak in CString.
I think awd is correct
Avatar of awd

ASKER

Correct how? That this is a bug?
I'll ask again:

Why don't you tell a little about what you are doing and let's see if we can find the problem?
Avatar of awd

ASKER

const TCHAR* pszParam

the above is a parameter to a funtion
in the function is:

m_nMode = *pszParam;

the previous line is called the first time through the function
and the next time through the function the foloowing line is called.

dataExchangeDirectory = pszParam;

m_nMode is a char
dataExchangeDirectory is a CString;
Avatar of awd

ASKER

Here is the code to what I am doing:

voidMyClass::MyFunction(const TCHAR* pszParam)
      
{
      static int count = 1;

      if (count == 1) {
            m_nMode = *pszParam;
      }

      if (count == 2) {
            dataExchangeDirectory = pszParam;
      }
            
      count++;
}
Where is dataExchangeDirectory declared?

How are you terminating your app?

The CString destructor should normally be called for this CString object when you app terminates.  If something odd is happening in the termination or some stack corruption is going on this could prevent it from getting called.

Can you provide any more information?
Avatar of awd

ASKER

dataExchangeDirectory is a member variable of the class the function is part of. It is declared in the header file. This is in a dialog based application and the main dialog (and the application) are being terminated by the regular OnOK function (the default for CDialog). I have not been able to find any abnormalities in the way the application terminates. One curious thing I did notice is that the App object's ExitInstance appears to never get called. I put some code in it and set a breakpoint. Should this function get called in dialog apps too? Could this be the cause of the problem?
I agree with jhance. There is nothing wrong in CString. The memory allocated by CString::AllocBuffer is freed by CString::FreeData, which is called by the destructor of CString.

ExitInstance should be called by the framework. There is a bug in your program.

Always suspect your own code first.
Avatar of awd

ASKER

Thanks. That is the direction I am looking in right now.
Hi chensu

There is nothing wrong in suspecting MFC framework code

Hi,

Well, I did a small testing to test that. I opened the strcore.cpp and placed the break points in AllocBuffer() and destructor for CString.

Then in my program
CString str;
str = "456sd";
return;

When I am assigning the string to CString it called AllocBuffer() (it has to) and allocated the memory. I noted that memory location say 0x00762c50. Then when I returned from my code it invoked the CString destructor. There GetData() function returned the same mamory location(0x00762c50) and deleted it. That means, it delets the memory allocated. I need not do anything extra to delete the memory allocated for CString.

Though it seems that it is allocating the memory to the local pointer in the AllocBuffer(), it is actually copying that data into the member variable m_pchData. It also stores the datalength etc. And GetData() will return that. So it deletes the proper memory.

But I have seen so many times the detection of memory leak in strcore.cpp. now question is why?. Is there any limit for the number of CString objects to create?. If that is exceded it is handling it properly?.

But for a CString it is doing it properly. May be the projects are so big and eating the lot of memory and it is causing the problem.

Take a look into that.
VinExpert
ASKER CERTIFIED SOLUTION
Avatar of mikeblas
mikeblas

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>There is nothing wrong in suspecting MFC framework code

Agree. There must be bugs as long as there is code. But always suspect your own code first. Note the word "first".
Avatar of awd

ASKER

Okay guys. I appreciate ALL of your comments. For those who are still interested, here is an update. I was premature in suspecting the CString code. I had to look through it a few times to understand what was really going on. Now I understand that the CString class is fine. What is happening is that the application appears to be terminating abnormally. I am having trouble locating the place where it is happening. Also I still have nor found any good CURRENT documentation on the CMemoryState class.

1. The application is dialog based.
2. I have added command line parameter processing to it using the CCommandLineInfo class. (any potential problems here?)
3. The variables in question are members of my CommandLineInfo class.
4. The CommandLineInfo class is instantiated in the InitInstance function of the App object.
5. The ExitInstance function is never being called.
6. The destructor for the CommandLineInfo object is never getting called.

That is where I am at right now. Thanks again for all of the input.
> I am having trouble locating the place where it is happening.

How is it terminating?  Does it just shut down?  Or do you get an unhandled exception message?  It's possible to debug either situation, but I don't want to waste time giving you advice for both situations.

 > Also I still have nor found any good CURRENT
 > documentation on the CMemoryState class.

Of course not: it's an implementation class that MFC implements and maintains privately--it's none of your business. What do you need to know about it?

1. Okay.

2. If you do it right, no.

3. The variables in which question? The ones that are leaking?

4. Great.

5. That's curious. Does your InitInstance() function return normally? Trace into the code in MFC that calls InitInstance() (the MFC-supplied AfxWinMain() function). What's going on there?

6. That's meaningless to me because I don't know where you've declared your CommandLineInfo object. You say you instantiate it in your InitInstance() function--does that mean its a local to the function, or that you allocate it on the heap in the function?

You should consider putting a breakpoint on AfxAbort() to see if it's being called. Then, the callstack from that point may show you what flow of control is leading to your application's shutdown.

..B ekiM
Avatar of awd

ASKER

>How is it terminating?  Does it just shut down?  Or do you get
>an unhandled exception message?

I don't get any messages other than the "memory leaks detected!" one in the debug output window.  As far as I can tell it just shuts down.  If it helps the last two lines in the output window are:
    The thread 0x4B has exited with code 0 (0x0).The program
    'E:\RAH66MP\Software_Dev\albert\Prototype Database
    Interface\cmb\Debug\cmb.exe' has exited with code 0 (0x0).

>Of course not: it's an implementation class that MFC
>implements and maintains privately--it's none of your
>business. What do you need to know about it?

I don't mean the source code for the class.  What I have been looking for was documentation on the OUTPUT when you do a dump of the objects and memory using CMemoryState::DumpAllObjectsSince() and CMemoryState::DumpStatistics().  What each of the fields represents.

>3. The variables in which question? The ones that are leaking?

Yes.  The variables that are leaking (or at least appear to be) are members of the CCommandLineInfo class that I instantiate.

>5. That's curious. Does your InitInstance() function return
>normally? Trace into the code in MFC that calls InitInstance()
>(the MFC-supplied AfxWinMain() function). What's going on
>there?

I have made some progress now. No. The CWinApp::InitInstance() does NOT return normally. Everything seems to go okay in that function u to the call to CDialog::DoModal().  This function is called and the dialog is displayed.  All of the functionality that should be present with the dialog seems to be there.  I have been clicking the ok button on the dialog as soon as it is displayed for the testing I have been doing recently. The CDialog::OnOK() function is called, but I do not see the DoModal() function call return.  So the problem looks to be in there somewhere.  Should I still look into the code before the InitInstance() function in the AfxWinMain()?

>6. That's meaningless to me because I don't know where
>you've declared your CommandLineInfo object. You say you
>instantiate it in your InitInstance() function--does that mean
>its a local to the function, or that you allocate it on the heap in
>the function?

I declare and instantiate the CCommandLineInfo object in the CWinApp::InitInstance() function.  I don't allocate it on the heap so it SHOULD be local to the InitInstance function.



>You should consider putting a breakpoint on AfxAbort() to see
>if it's being called. Then, the callstack from that point may
>show you what flow of control is leading to your application's
>shutdown.

I tried this following your advice.  The breakpoint was never reached.  So it appears that AfxAbort() is not called.  In addition the TRACE message that is the first statement of the AfxAbort() function is not output to the debug window either.

Thanks for the assistance so far!  Any more thoughts?
> Thanks for the assistance so far!  

You started out by saying there was a memory leak in every CString. Then, I answered the secondary questions about why CString shows up in leaks so often. And now I'm teaching you how to debug your application. Boy, we're sure a looong way from the original question, aren't we? All this for only 200 points!

Well, anyway:

If your DoModal() call from InitInstance() never returns, then it doesn't return: the destructor for any locals in InitInstance() won't be called, and that's the direct cause of your memory leak.

Obviously, what you need to do now is narrow-down the indirect cause; why isn't DoModal() returning? AfxAbort() is one way DoModal() might not return--there are several others: someone's calling TerminateThread() or TerminateProcess() or ExitThread() or ExitProcess() or exit(), or something else.

Do you have an OnOK() handler in your dialog? You should trace through it, if you do. If you don't, you should trace into the call to DoModal().

..B ekiM
Avatar of awd

ASKER

I will gladly increase the points to a fair amount.  What is the help you have offered worth?

I have done some more debugging.  It looks like the DoModal() function is terminating (and killing the application) when CWnd::DestroyWindow() is called.  Towards the end of the DoModal() call.  CWnd::DestroyWindow() calls ::DestroyWindow().  This function call is where the application quits.  It looks like a valid handle being passed to the ::DestroyWindow() function what could cause it to fail?  I can't step into it.

I never meant to imply that I had definitely found a bug.  I got confused because I did not understand the CMemoryState output fully and how to properly use that class.
Why is DestroyWindow() being called? Is it being called on the dialog? You shouldn't call DestroyWindow() on a modal dialog--it's for popup windows and modeless dialogs. You should call EndDialog().

..B ekiM
Avatar of awd

ASKER

I appologize if I did not explain it well enough.  The DestroyWindow() call is being made by MFC on line 561 of the CDialog::DoModal function.

I HAVE FOUND THE CAUSE OF THE 1ST MEMORY LEAK THOUGH.  I missed the fact that the guy who wrote this application had overridden the OnDestroy() function for the dialog class that he was using.  In this function he called:

exit( 0 );

This short circuited the MFC clean up in a big way.

....Anyhow I appreciate all of the input very much!  Let me know what a fair value is for this question and I will raise the point value.  Also, I would like to know what each field is in the output for the CMemoryState::DumpAllObjectsSince() and CMemoryState::DumpStatistics().  The documentation that comes with C++ 6.0 does not cover the OUTPUT of the current implementation.  I can open seperate question for that.
> This short circuited the MFC clean up in a big way.

It sure did!

It sounds like you need to consider having code reviews on your project.

 > fair value is for this question and

Well, I guess 200 for the information about why CString leaks appear so often. And 200 for helping with the diagnosis to the _real_ leak. But, after that, it seems like you might want to assign points to whoever you feel helped you with how AllocData() worked--like chensu or jhance.

Questions drift a lot at Experts Exchange. Experts do lots of extra work as they drift, and it's not very fair to them because those changes cost lots of time.

 > I can open seperate question for that.

Or, just add more points to this one.

Output for a memory leak I just forced looks like this:

d:\foo_mdi\MainFrm.cpp(90) : {64} normal block at 0x00782E60, 404 bytes long.
 Data: <d     L_  L_  L_> 64 00 00 00 14 CB 4C 5F 14 CB 4C 5F 14 CB 4C 5F


The first string means that the memory allocation occurred at line 90 of the MainFrm.cpp file in the project at d:\foo_mdi. This is where the memory in question was actually allocated. On that line, I actually have a call to new that looks like this:

   char* pstr = new char[100];

so it's me who's allocating the memory. But it's possible that leaking memory was allocated by a constructor that you don't own. If, instead, I coded:

   CString* pMyString = new CString("Hockey is best");

I would actually see _two_ lines of leaked memory: one of the CString object and one for the memory CString allocated on my behalf.


Okay, all that aside.  The next field after the colon in {Curly brackets} is an ordinal that tells you when the memory was allocated. In my first example, the memory that leaked was allocated by the 64th call to the memory allocator. That information helps you in two ways. First, it lets you knwo that the memory was allocated relatively early in the lifecycle of the program.

Second, that number allows you to set an automatic breakpoint in the runtime libraries. Since I know that the 64th allocation is leaking, I can use the debugger to set the symbol _crtBreakAlloc to 64. Or, I can call CrtSetBreakAlloc(64) and the CRTs will automatically force a breakpoint on the 64th allocation call. That'll pop me up in the debugger at a point where I can see the stack and observe the memory being allocated.

Note that some allocations happen even beore main() or WinMain() or your InitInstance() is called--because they are allocations done by static constructors, or the initialization code in MFC or the C Runtimes themselves. So, you might have to resort to some crafty techniques to catch those allocations.

After that, you see that the block is a "normal block". That means that  the memory is a block which you allocated yourself for a simple data type. There are also "crt blocks", which the CRT allocates for its own use. You should never see these leaked: if they are, you've shut down somthing in a very bad way (like calling TerminateThread() or ExitThread() instead of letting the CRT and/or MFC cleanup), or there's a bonafide bug in the C runtimes.  There are also "client blocks", which indicate that the memory was allocated by the CObject-local version of operator new.

The number after "at" shows the address of the leaked memory block. It's the pointer you should've freed. The length is the length--obviously. The Data between the <brackets> is an ASCII representation of the first 16 bytes of data in the leacked block. The same 16 bytes are shown in hexadecimal there, too. This is just to help you try and figure out which object has leaked.

..B ekiM
Avatar of awd

ASKER

Ok. 400 points for mikeblas on this question, correct? That is easy because you locked the question with a proposed answer. How would I go about giving some points to the others who helped?
Avatar of awd

ASKER

Quick question, mikeblas. Is there any place I can go to find the info you explained on the CMemoryState class? Everything I have been able to find covers a previous version and  only covers some of the fields.
> place I can go to find the info you explained on the CMemoryState class?

I don't know. I'm busy enough just knowing all this stuff; being a librarian for the places wherever it might be written down is too much more work. The obvious places to check are MSDN and the KB. There might be articles in any number of journals (and, heck, I might have written one of 'em!), or chapters in any number of books.

 > 400 points for mikeblas on this question, correct?

Please don't forget to add points for the answer explaining what the leak dumps are.

 > How would I go about giving some points to the others who helped?

Most people open a new question with some points and just name an expert in the title. "Extra points for mikeblas", for instance. That's pretty lame, but such lameness seems to be the status quo here at EE.

Thanks.

..B ekiM
Avatar of awd

ASKER

So how many TOTAL points for all of the answers you posted under this question?  What is a fair value?
500 seems fine, I guess.  Thanks for being so honest and kind!  Most EE users are jerks who could care less about rewarding experts, no matter how much help they get.

..B ekiM
Avatar of awd

ASKER

I appreciate all of the help.  My problem did not end up being what I thought, but I learned a lot of useful stuff in the process.  Thanks for the patience.  Also, on a side note.  I have your book, "Professional Programming With MFC 5".  (I think that is the title.  It is loaned out at the moment)  I have found a wealth of useful info in that too.
Avatar of awd

ASKER

Oops!  Forgot to close the question.
I'm glad you like the book.

Thanks again.

..B ekiM