AnsiString and multithreading or is it codeguard

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

AnsiString and multithreading or is it codeguard

Postby stephen.terrell » Fri Jul 10, 2009 5:45 am

Hi

My otherwise reasonably pleasent life with BDS 2009 C++ and a major multithreading program is being made miserable with random reports from codeguard about my program being out of memory (it is not!). I suspect the issue is really codeguard and multi-threading but since most of the locations where the reports originate are associated with AnsiStrings I thought it worth asking whether there is a known problem with AnsiStrings in multi-thread programs.

The usual google search did locate some suggestions of this but generally with earlier versions of BDS/C++ Builder.

Any thoughts on this or codeguard problems with multi-threading will be of interest.

Steve
stephen.terrell
Top Poster
Top Poster
 
Posts: 32
Joined: Wed May 07, 2008 3:05 am

Re: AnsiString and multithreading or is it codeguard

Postby rlebeau » Fri Jul 10, 2009 3:17 pm

stephen.terrell wrote:My otherwise reasonably pleasent life with BDS 2009 C++ and a major multithreading program is being made miserable with random reports from codeguard about my program being out of memory (it is not!).


Just because your machine is not out of memory does not necessarily mean the memory manager inside your project can find/allocate memory it needs.

stephen.terrell wrote:I suspect the issue is really codeguard and multi-threading


Possibly.

stephen.terrell wrote:since most of the locations where the reports originate are associated with AnsiStrings I thought it worth asking whether there is a known problem with AnsiStrings in multi-thread programs.


There is not. But it is hard to diagnose without knowing more about how those AnsiString values are being used. It could just be your code mis-using them.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1605
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: AnsiString and multithreading or is it codeguard

Postby arisme » Fri Jul 10, 2009 4:01 pm

stephen.terrell wrote:...random reports from codeguard about my program being out of memory (it is not!).

It might be...

Do the reports tell you whether the memory shortage is in the stack or the heap? Stack memory is a very much smaller work space than heap memory, and it is possible to use it all up in the way the application's memory usage is structured.

This is a potential problem I am aware of, but have not experienced myself in the BCB era. If this is your problem, then either the stack needs to be enlarged or some of your application's definitions and operations may need to be amended.

The stack is a fixed amount of memory reserved for an application when it starts a run. Memory needed for local variables and objects, and local operations is allocated from the stack and released back to it as the program runs. This is all done automatically and invisibly in the background. Memory allocated dynamically by your application is reserved on the heap, which is essentially the whole of Windows virtual memory.

I believe there is a default stack size and the way your applications functions need to use stack memory may be exceeding this limit. I don't know what the default stack size is, but we can explore that and options if you think that this might be the trigger for the CodeGuard reports.

Aris
arisme
BCBJ Master
BCBJ Master
 
Posts: 357
Joined: Thu Jun 07, 2007 9:35 pm
Location: UK

Re: AnsiString and multithreading or is it codeguard

Postby stephen.terrell » Fri Jul 10, 2009 11:40 pm

Thank you all for the replies. I had become laze and stopped thinking about stack and heap issues (BDS makes life too easy for us).

I have looked again at the Eurekalog report (triggered by the codeguard error) and the problem ocured in a comercial
component (TVaComm) where a TList was being extended:- Classes.pas (TList) line 3555. So in that case it was not an AnsiString!! although it has been on other occasions, at least one of which involved an AnsiString being passed by value to a function.

So at least the out of memory report seems to correspond to a memory allocation event.

Presumably TList extensions (and AnsiString creation in a function call ?) come from the heap?

Steve
stephen.terrell
Top Poster
Top Poster
 
Posts: 32
Joined: Wed May 07, 2008 3:05 am

More information

Postby stephen.terrell » Sat Jul 11, 2009 9:25 pm

I have left the application running overnight and had an out of memory fault after about 4 hours. One of the things I do not understand is that the application, and all of its controls, keep running after these errors.

FYI here is the Eurekalog output

EurekaLog 6.0.20

Application:
-------------------------------------------------------
1.1 Start Date : Sat, 11 Jul 2009 19:41:09 +0100
1.2 Name/Description: DrumRig.exe
1.3 Version Number :
1.4 Parameters :
1.5 Compilation Date: Sat, 11 Jul 2009 19:16:20 +0100
1.6 Up Time : 3 hours, 55 minutes, 2 seconds

Exception:
-----------------------------------------------------
2.1 Date : Sat, 11 Jul 2009 23:36:12 +0100
2.2 Address : 004BDF95
2.3 Module Name : DrumRig.exe
2.4 Module Version:
2.5 Type : EOutOfMemory
2.6 Message : Out of memory.
2.7 ID : 3DBD
2.8 Count : 1
2.9 Status : New
2.10 Note :

User:
-------------------------------------------------------
<edited out>

Active Controls:
----------------------------------
4.1 Form Class : TMainForm
4.2 Form Text : Drum Test Rig
4.3 Control Class: TMainForm
4.4 Control Text :

Computer:
-------------------------------------------------------------------------------
5.1 Name : STEPHENSACER
5.2 Total Memory : 2814 Mb
5.3 Free Memory : 2129 Mb
5.4 Total Disk : 110.82 Gb
5.5 Free Disk : 39.91 Gb
5.6 System Up Time: 14 hours, 31 minutes, 26 seconds
5.7 Processor : AMD Turion(tm) 64 X2 Mobile Technology TL-52
5.8 Display Mode : 1280 x 800, 32 bit
5.9 Display DPI : 96
5.10 Video Card : ATI Mobility Radeon X1300 (driver 8.273.0.0 - RAM 512 MB)
5.11 Printer : HP Deskjet 1280 (driver 2.233.6.0)

Operating System:
------------------------------------
6.1 Type : Microsoft Windows XP
6.2 Build # : 2600
6.3 Update : Service Pack 3
6.4 Language: English
6.5 Charset : 0

Network:
---------------------------------------------------
<edited out>

Call Stack Information:
---------------------------------------------------------------------------------------------------------------------
|Address |Module |Unit |Class |Procedure/Method |Line |
---------------------------------------------------------------------------------------------------------------------
|*Exception Thread: ID=5620; Priority=0; Class=; [Main] |
|-------------------------------------------------------------------------------------------------------------------|
|004BDF95|DrumRig.exe |Graphics.pas |TResourceManager |AllocResource |1632[10] |
|0048627A|DrumRig.exe |System.pas | |ErrorAt |4057[3] |
|004860CC|DrumRig.exe |System.pas | |_GetMem |3410[1] |
|004BDF95|DrumRig.exe |Graphics.pas |TResourceManager |AllocResource |1632[10] |
|004BDF18|DrumRig.exe |Graphics.pas |TResourceManager |AllocResource |1622[0] |
|004BE0F0|DrumRig.exe |Graphics.pas |TResourceManager |ChangeResource |1704[8] |
|004BE0C0|DrumRig.exe |Graphics.pas |TResourceManager |ChangeResource |1696[0] |
|004BF7F6|DrumRig.exe |Graphics.pas |TPen |SetData |2902[3] |
|004BF7CC|DrumRig.exe |Graphics.pas |TPen |SetData |2899[0] |
|004BF849|DrumRig.exe |Graphics.pas |TPen |SetColor |2932[12] |
|004BF824|DrumRig.exe |Graphics.pas |TPen |SetColor |2920[0] |
|004BDF10|DrumRig.exe |Graphics.pas |TResourceManager |Unlock |1614[1] |
|004BDF0C|DrumRig.exe |Graphics.pas |TResourceManager |Unlock |1614[1] |
|004BE07E|DrumRig.exe |Graphics.pas |TResourceManager |FreeResource |1675[20] |
|004BDF10|DrumRig.exe |Graphics.pas |TResourceManager |Unlock |1614[1] |
|004BDF0C|DrumRig.exe |Graphics.pas |TResourceManager |Unlock |1614[1] |
|004BE123|DrumRig.exe |Graphics.pas |TResourceManager |ChangeResource |1710[14] |
|00486838|DrumRig.exe |System.pas | |_ROUND |4986[1] |
|0CD1374C|CG32.DLL | | |_CG_VALIDATE | |
|00408874|DrumRig.exe |NewScope.cpp |TNewScopeForm |UpdateTimerTimer |300[160] |
|7C9010E0|ntdll.dll | | |RtlLeaveCriticalSection | |
|004C43A8|DrumRig.exe |Graphics.pas | |FreeMemoryContexts |6818[0] |
|004EED6D|DrumRig.exe |Controls.pas |TWinControl |MainWndProc |9068[6] |
|004693C8|DrumRig.exe |Classes.pas | |StdWndProc |12723[8] |
|7E42B3FC|USER32.DLL | | |CallNextHookEx | |
|004DA577|DrumRig.exe |ExtCtrls.pas |TTimer |Timer |2979[1] |
|0048853C|DrumRig.exe |System.pas | |_CallDynaInst |10186[1] |
|004DA45B|DrumRig.exe |ExtCtrls.pas |TTimer |WndProc |2937[4] |
|004693C8|DrumRig.exe |Classes.pas | |StdWndProc |12723[8] |
|7E418A0B|USER32.DLL | | |DispatchMessageW | |
|7E418A01|USER32.DLL | | |DispatchMessageW | |
|004D673B|DrumRig.exe |Forms.pas |TApplication |ProcessMessage |9613[23] |
|004D6648|DrumRig.exe |Forms.pas |TApplication |ProcessMessage |9590[0] |
|004D677E|DrumRig.exe |Forms.pas |TApplication |HandleMessage |9643[1] |
|004D6774|DrumRig.exe |Forms.pas |TApplication |HandleMessage |9642[0] |
|004D6AA9|DrumRig.exe |Forms.pas |TApplication |Run |9780[26] |
|004D69E0|DrumRig.exe |Forms.pas |TApplication |Run |9754[0] |
|00401CD7|DrumRig.exe |DrumRig.cpp | |WinMain |25[11]
stephen.terrell
Top Poster
Top Poster
 
Posts: 32
Joined: Wed May 07, 2008 3:05 am

Codeguard log from previous post

Postby stephen.terrell » Sat Jul 11, 2009 9:33 pm

When I shut the application down (several hours after the out of memory error) the codeguard log for the run was

Modules used:
00400000 07/11/2009 19:16:22 C:\SilMaster\DrumRig\Debug\DrumRig.exe
==========================================
Functions called:
fclose (2 times)
fflush (1550 times)
fprintf (1546 times)
fopen (2 times)
strcmp (651 times)
vsnprintf (56920 times)
delete (249 times)
vsprintf (14 times)
strncpy (273 times)
SysReallocMem (3151676 times)
SysFreeMem (295493447 times)
SysGetMem (295493502 times)
free (24 times)
_lsetlocale (6 times)
new (258 times)
calloc (9 times)
strlen (69 times)
realloc (1 times)
strdup (5 times)
malloc (9 times)
memcpy (598338 times)
Resource types used:
object (258 allocs, 246 max)
memory block (295493517 allocs, 26473 max)
file stream (2 allocs, 1 max)
file handle (2 allocs, 1 max)


There does seem to be a lot of SysReallocMem/SysFreeMem and SysGetMem calls! but it had run for something like 12 hours.
stephen.terrell
Top Poster
Top Poster
 
Posts: 32
Joined: Wed May 07, 2008 3:05 am

Re: AnsiString and multithreading or is it codeguard

Postby arisme » Sun Jul 12, 2009 3:34 am

Hmmm...a little too much info to digest quickly...

Something to check - the single realloc in the CodeGuard log.

Was it expanding or shrinking? Was it applied to a local memory block? Is the new size a realistic value?

On another tack - the length of run before crash implies that either the problem code is called very rarely, or is called often but only creeps up on the out of memory limit gradually.

So, are there memory operations that would be called only in exceptional circumstances?

On yet another tack -
I have on occasion, and as a last resort in difficult to locate bug cases, added OutputDebugString() to each of the functions in the suspect processing chain. The output string includes suitable local info to identify where the last output was prior to a crash.

This can slow execution somewhat, but has always worked when the bug absolutely had to be pinned down and resolved.

There are alternative mechanisms that can be used in a similar manner to identify the point just prior to a crash. From there it is usually easy to set local breakpoints and traps to pin down the actual faulty code.

Aris
arisme
BCBJ Master
BCBJ Master
 
Posts: 357
Joined: Thu Jun 07, 2007 9:35 pm
Location: UK

Re: AnsiString and multithreading or is it codeguard

Postby arisme » Sun Jul 12, 2009 4:43 am

stephen.terrell wrote:One of the things I do not understand is that the application, and all of its controls, keep running after these errors.

The out of memory report would be a response to an attempt to allocate out of bounds, so no memory would actually be allocated. This would not be a show stopper at that point - merely a failed attempt to do something.

The resulting shortage of planned work space may not actually cause another crash, but would raise questions about the validity or soundness of any data processed thereafter. It all depends on the what is being processed, and on the detail of how the processing is handled.

Aris
arisme
BCBJ Master
BCBJ Master
 
Posts: 357
Joined: Thu Jun 07, 2007 9:35 pm
Location: UK

Re: AnsiString and multithreading or is it codeguard

Postby stephen.terrell » Sun Jul 12, 2009 6:22 am

I do appreciate these thoughts and comments so please keep them coming.

The last two "outofmemory" events have happened after about 4 hours running and according to the error report - as above - involved some memory re-allocation in a (SDL) chart component. I am not suggesting that component is at fault.

The rest of the Eurekalog shows that the other threads were all waiting on single events (as they mostly should be).

The application creates various shared memory (files) regions for communication with other processes and sets up quite a lot of lists holding dynamically created class instances. This all happens in the first few minutes and after that I do not directly create any new objects or directly allocate any memory. Clearly a lot goes on in the background in terms of function calls with strings passed by value (although i have tried to minimise that) and objects such as the chart component do whatever they are programmed to do.

There does not seem to be any codeguard detected memory bleeding and whent the application is shut down there is no lost memory.

Looking at earlier codeguard logs i see the realloc happens quite early on in the application life but I am not sure where (I do not call it directly).

The function where the problem is showing up is run by a timer (150ms) and adds some data to the chart component and scrolls it to the left. An oscilloscope type display. Unfortunately it is a commercial component so i do not have access to the actual code.
A little like your suggestion I had thought of allocating a large chunk of memory on entry to the function and returning it before exiting. That would possible show that memory (of that size) was getting low.

Steve
stephen.terrell
Top Poster
Top Poster
 
Posts: 32
Joined: Wed May 07, 2008 3:05 am

Re: AnsiString and multithreading or is it codeguard

Postby arisme » Sun Jul 12, 2009 12:15 pm

stephen.terrell wrote:The function where the problem is showing up is run by a timer (150ms) and adds some data to the chart component and scrolls it to the left. An oscilloscope type display. Unfortunately it is a commercial component so i do not have access to the actual code.

Is it possible to set up a special testbed application with just the chart component, and emulate the data addition and scrolling that it receives in the main application?

The thinking here is that if there is a weakness in the chart component, both you and the vendor of the component would want to know about it. A testbed failure would provide conclusive proof.

In the first instance, the data addition emulation would not need to be exactly the same as in the main application, but similar in quantity and type. Hopefully, sufficiently similar to test the chart component's memory handling and expose any weaknesses.

The call rate should not be a factor, so the testbed will be able to run much faster than the 150ms tick rate.

Aris
arisme
BCBJ Master
BCBJ Master
 
Posts: 357
Joined: Thu Jun 07, 2007 9:35 pm
Location: UK

Re: AnsiString and multithreading or is it codeguard

Postby stephen.terrell » Sun Jul 12, 2009 5:05 pm

Good idea!

I have tried one other approach, increasing the heap and stack size by a factor of 16. In a highly non statistically valid test (i.e. 1 run) the application ran for about 12 hours before the out of memory event (at the same location as before) as opposed to the previous two runs that laster 4 hours to the event.

Steve
stephen.terrell
Top Poster
Top Poster
 
Posts: 32
Joined: Wed May 07, 2008 3:05 am

Re: AnsiString and multithreading or is it codeguard

Postby arisme » Mon Jul 13, 2009 2:36 am

stephen.terrell wrote:I have tried one other approach, increasing the heap and stack size by a factor of 16. In a highly non statistically valid test (i.e. 1 run) the application ran for about 12 hours before the out of memory event (at the same location as before) as opposed to the previous two runs that laster 4 hours to the event.

A very good piece of the jigsaw. Not absolutely conclusive, but highly indicative.

The implication is that the offending code creeps up on the available memory boundary incrementally. In turn, this strengthens the case against the chart component which is incrementally receiving additional data during a run.

A question we should not overlook - are there any other areas where an incremental data feed may be building up trouble?

Incidentally, check whether your Tool Palette has a TPerformanceGraph component. Some versions of Builder have it. If you are lucky, that might well be a suitable alternative to the third party chart. It can be configured to an oscilloscope type display.

Aris
arisme
BCBJ Master
BCBJ Master
 
Posts: 357
Joined: Thu Jun 07, 2007 9:35 pm
Location: UK

Re: AnsiString and multithreading or is it codeguard

Postby rlebeau » Mon Jul 13, 2009 7:25 am

stephen.terrell wrote:Presumably TList extensions (and AnsiString creation in a function call ?) come from the heap?


Yes, they do. When expanding a TList, String, or other dynamic-sized container, a new memory block of the specified size has to be allocated, the existing data copied into the new memory block, the old memory pointer replaced with the new pointer, and then the old memory freed. The most common error to occur during that process is when the Memory Manager could not locate an available block of contigious memory (and cannot allocate new memory from the OS to expand its pool of available blocks) that is large enough to satisfy the allocation request. That generally means that either your app is using too much memory already, or more likely your app fragmented existing memory too much so not enough contigious blocks are available anymore.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1605
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: AnsiString and multithreading or is it codeguard

Postby stephen.terrell » Mon Jul 13, 2009 9:22 pm

And no garbage collection to save the world ;(

is that also true for strings etc. declared in a function?
i.e.
void foo
{
AnsiString steve; // is this on the stack? (i suspect not)
}

as opposed to AnsiString * pSteve = new AnsiString(); which i presume is not

It is a bit disconcerting that something like this can go on in VCL or third party controls etc and potentially cause problems. I would guess that over 95% of all memory allocations happen in the first few minutes of my application after which (from my code point of view) there is just a background of string useage (mainly local to functions or as passed by value parameters or return values). The application has to run for many days without rebooting so it would be good not to have its memory fragmenting away until there is none left.

Presumably there is no way of asking the memory management routines what sort of state it is in (biggest free block etc.) so that I can monitor it? In a similar vein is there a global default initial string size allocation that I can set larger than the majority of my strings so that the need for extending then is a rare occurance? (i did have a look in the project settings but there does not seem to be anything there)

Steve
stephen.terrell
Top Poster
Top Poster
 
Posts: 32
Joined: Wed May 07, 2008 3:05 am

Re: AnsiString and multithreading or is it codeguard

Postby arisme » Tue Jul 14, 2009 5:07 am

stephen.terrell wrote:Presumably there is no way of asking the memory management routines what sort of state it is in (biggest free block etc.) so that I can monitor it?

I should imagine that someone, somewhere has written such a monitor, and is charging a hefty sum for its use. The net has plenty of sources for memory usage monitors.

You also have at least two on your machine already - Task Manager and PerfMon. There is a rundown on using these here.
They will not provide you with an answer to your specific question, but may provide clues as to whether fragmentation is a problem.

Fragmentation may be a problem, but that is not yet proven (unless you know otherwise already). Assuming that the memory manager is a rational beast, it would be reasonable for it to reuse previously freed blocks if the next request is small enough to fit. Even a large volume of memory allocations and releases need not necessarily occupy a large total memory space. It all depends on what the dynamic memory handling of your application is doing.

Personally, I think I would still run the chart testbed. If it fails around the 96,000 cycle mark (4 hours worth of 150ms ticks), that may resolve your original problem. If it runs well beyond that point, then the implication would be that the chart component is innocent and just the fall guy in circumstances created by other parts of the application.

Either way, useful diagnostic evidence.

Aris
arisme
BCBJ Master
BCBJ Master
 
Posts: 357
Joined: Thu Jun 07, 2007 9:35 pm
Location: UK

Next

Return to Technical

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 15 guests