PDA

View Full Version : Memory Leak Test Program?



kryton9
09-07-2010, 08:52
I was wondering if anyone knows of a utility where I can check my program for memory leaks?
I found some searching but wanted to know if there were any you guys use that you like and trust.
Thanks.

Michael Hartlef
09-07-2010, 12:58
I used something like this for my Delphi projects as a component. It was mega helpfull finding bugs.
But no, for other programming languages I don't know any tool for that.

LanceGary
09-07-2010, 14:39
I used something like this for my Delphi projects as a component. It was mega helpfull finding bugs.
But no, for other programming languages I don't know any tool for that.


This sounds wonderful. Sorry for being so ignorant but how would you go about writing such an utility? Seems like magic to me.

Lance

Lionheart008
09-07-2010, 15:03
hi kent, short question, you don't meaning a tool like "memcleaner" isn't it ?
("ccleaner", website: http://www.piriform.com/products)

here's a thinbasic attempt for memory info, but it isn't perfect. take attention, it's not ready (array error), use for own risk!



' Empty GUI script created on 07-09-2010 10:21:07 by frank (ThinAIR)

Uses "console", "ui"

Type PROCESS_MEMORY_COUNTERS
cb As Long
PageFaultCount As Long
PeakWorkingSetSize As Long
WorkingSetSize As Long
QuotaPeakPagedPoolUsage As Long
QuotaPagedPoolUsage As Long
QuotaPeakNonPagedPoolUsage As Long
QuotaNonPagedPoolUsage As Long
PagefileUsage As Long

PeakPagefileUsage As Long
End Type

'--> process status api (psapi)

Declare Function GetProcessMemoryInfo Lib "PSAPI.DLL" Alias "GetProcessMemoryInfo" ( _
ByVal hProcess As Long, _
ByVal ppsmemCounters As PROCESS_MEMORY_COUNTERS, _
ByVal cb As Long _
) As Long

Function MyFunction3(ByVal idim As Long, ByVal lArray() As Long ) As Long

If idim < LBound(lArray) Or idim > UBound(larray) Then Exit Function
lArray( idim ) = idim
End Function

Function MyFunction2(ByVal lArray() As Long ) As Long

ReDim Preserve lArray( UBound(lArray) + 1)
lArray( UBound(lArray) ) = UBound(lArray)
End Function

Function MyFunction1() As Long
'? not yet ready...
End Function

'---------------------------------------------------
Function TBMain () As Long

Local sMemoryInfo As String
Local i As Long
Local result As Long
Local lArray() As String

sMemoryInfo = "after loop0: " + GetProcessMemoryInfo, %MB_OK, "Kb" + $CRLF

For i = 1 To 5
Sleep 1
Next
sMemoryInfo = sMemoryInfo + "after loop1: " + GetProcessMemoryInfo, %MB_OK, "Kb" + $CRLF


For i = 1 To 500
result = MyFunction1()
Next
sMemoryInfo = sMemoryInfo + "after loop2: " + GetProcessMemoryInfo, %MB_OK, "Kb" + $CRLF


For i = 1 To 5
Sleep 1
Next
sMemoryInfo = sMemoryInfo + "after loop3: " + GetProcessMemoryInfo, %MB_OK, "Kb" + $CRLF

'----> line 72 error!
For i = 1 To 500
result = MyFunction2( lArray() )
Next
Delete lArray()
sMemoryInfo = sMemoryInfo & "after loop4: " + GetProcessMemoryInfo, %MB_OK, "Kb" + $CRLF

ReDim lArray(500)
For i = 1 To 500
result = MyFunction3( i, lArray() )
Next
Delete lArray()
sMemoryInfo = sMemoryInfo & "after loop5: " + GetProcessMemoryInfo, %MB_OK, "Kb" + $CRLF

MsgBox 0, sMemoryInfo

End Function


best regards, frank

Michael Hartlef
09-07-2010, 15:24
I used something like this for my Delphi projects as a component. It was mega helpfull finding bugs.
But no, for other programming languages I don't know any tool for that.


This sounds wonderful. Sorry for being so ignorant but how would you go about writing such an utility? Seems like magic to me.

Lance


I don't know about how to create such a thing myself. The awesome thing about Delphi is that you can get components (objects) that capsule all these functionalities inside and then you just need to add them to your project and maybe call some functions from it.

Michael Clease
09-07-2010, 15:50
Kent I did use one many moons ago but cant remember what its called but I will scan my HDD backup for clues and come back to you.

Mike

Petr Schreiber
09-07-2010, 20:05
Hi Kent,

there is one practical approach I learned on the speech of one Slovaq game developer from Cauldron (I think).

Use wrappers for memory allocation and freeing, so you can keep track of what was forgotten to release. I used this approach in C when coding for ATMega128, and it paid of greatly.

Just log the allocations in your program to file, and then use the attached ThinBASIC script to keep track of the allocations.

Logging allocations in C (in ThinBASIC you would use Heap_Alloc, Heap_Free):


#define debugMode

void* dmalloc ( size_t size, const char * text )
{
void* ptr = malloc(size);
#ifdef debugMode
FILE *fp;
fp=fopen("c:\\debug_alloc.txt", "a+");
fprintf(fp, "MALLOC,%i,%i,%s\n", ptr, size, text);
fclose(fp);
#endif

return ptr;
}

void* dcalloc ( size_t num, size_t size, const char * text )
{
void* ptr = calloc(num, size);
#ifdef debugMode
FILE *fp;
fp=fopen("c:\\debug_alloc.txt", "a+");
fprintf(fp, "CALLOC,%i,%i,%i,%s\n", ptr, num, size, text);
fclose(fp);
#endif

return ptr;
}

void dfree ( void *ptr, const char * text )
{
#ifdef debugMode
FILE *fp;
fp=fopen("c:\\debug_alloc.txt", "a+");
fprintf(fp, "FREE,%i,%s\n", ptr, text);
fclose(fp);
#endif

free(ptr);
return;
}


Analyser in ThinBASIC:


Uses "console", "File"

Type Allocated
where As DWord
howMuch As DWord
description As String
freed As Long
End Type

Type Deallocated
where As DWord
count As Long
End Type

Global Allocations() As Allocated
Global AllocCount As Long

Global Deallocations() As Deallocated
Global DeallocCount As Long

Function TBMain()

Dim s As String = FILE_Load("c:\debug_alloc.txt")
Dim sout As String

Dim memCounter As Long
Dim memVal As Long
Dim sProfile As String = "0"+$CRLF'$TAB+"0"+$CRLF

Dim lines() As String
Dim nLines As Long = Parse(s, lines, $CRLF)

Dim oneLine As String
Dim token() As String
Dim i As Long

Dim mAddress, mAmount As Long
Dim mDescription As String
' ---

For i = 1 To nLines
oneLine = lines(i)

Parse(oneLine, token, ",")

Select Case token(1)
Case "MALLOC"
mAddress = Val(token(2))
mAmount = Val(token(3))
mDescription = token(4)
AppendAlloc(mAddress, mAmount, mDescription)

memCounter += 1
memVal += mAmount

sProfile += Format$(memVal)+$CRLF'Format$(memCounter)+$TAB+Format$(memVal)+$CRLF

Case "CALLOC"
mAddress = Val(token(2))
mAmount = Val(token(3))*Val(token(4))
mDescription = token(5)
AppendAlloc(mAddress, mAmount, mDescription)

memCounter += 1
memVal += mAmount

sProfile += Format$(memVal)+$CRLF'Format$(memCounter)+$TAB+Format$(memVal)+$CRLF

Case "FREE"
mAddress = Val(token(2))
AppendFree(mAddress)

memCounter += 1
memVal -= FindDeallocation(token(2))

sProfile += Format$(memVal)+$CRLF'+$TAB+Format$(memVal)+$CRLF'+$TAB+"De"+FindDeallocationDescription(token(2))+$TAB+FindDeallocationWhere(token(2))+$CRLF
End Select

Next

' -- Hledani leaku
Dim j, freed As Long
For i = 1 To AllocCount

For j = 1 To DeAllocCount
If Allocations(i).where = Deallocations(j).where Then
FreeAllocation(Allocations(i).where)
Allocations(i).freed = 1
Print "[!]", Allocations(i).where
Exit For
End If
Next


Next

Dim totalLeak As Long
For i = 1 To AllocCount

If IsDeallocated(Allocations(i).where) = %FALSE Then
sOut += "Block at " & Allocations(i).where & " of size " & Allocations(i).howMuch & " described as " & Allocations(i).description & $CRLF
totalLeak += Allocations(i).howMuch
End If

Next

For i = 1 To DeAllocCount
If DeAllocations(i).count = 0 Then
sOut += "Free block at " & DeAllocations(i).where & $CRLF
'totalLeak += Allocations(i).howMuch
End If
Next
sOut += "TOTAL LEAK: " & totalLeak

PrintL "Deallocs"
For i = 1 To DeAllocCount
Print DeAllocations(i).Count, ":"
Next


ClipBoard_SetText(sProfile) ' -- To be used for visualisation in MS Excel
FILE_Save("c:\debug_alloc_profile.txt", sProfile)
End Function

Sub AppendAlloc( where As DWord, howMuch As DWord, description As String )

Incr AllocCount
If AllocCount > UBound(Allocations) Then
ReDim Preserve Allocations(AllocCount)
End If

Allocations(AllocCount).where = where
Allocations(AllocCount).howMuch = howMuch
Allocations(AllocCount).description = description
End Sub

Sub AppendFree( where As DWord )

Incr DeAllocCount
If DeAllocCount > UBound(DeAllocations) Then
ReDim Preserve DeAllocations(DeAllocCount)
End If

DeAllocations(DeAllocCount).where = where
DeAllocations(DeAllocCount).count = 0
End Sub

Function FindDeallocation( where As DWord ) As DWord

Local i As Long

For i = AllocCount To 1 Step -1

If Allocations(i).where = where Then
Return Allocations(i).howMuch
End If
Next

End Function


Function FindDeallocationDescription( where As DWord ) As String

Local i As Long

For i = 1 To AllocCount

If Allocations(i).where = where Then
Return Allocations(i).description
End If
Next

End Function

Function FindDeallocationWhere( where As DWord ) As DWord

Local i As Long

For i = 1 To AllocCount

If Allocations(i).where = where Then
Return Allocations(i).where
End If
Next

End Function

Function IsDeallocated( where As DWord ) As Long

Local i As Long

For i = 1 To DeallocCount

If Deallocations(i).where = where And Deallocations(i).Count > 0 Then
Return %TRUE
End If
Next

Return %FALSE

End Function

Function FreeAllocation(where As DWord)
Local i As Long

For i = 1 To DeallocCount

If Deallocations(i).where = where And Deallocations(i).Count = 0 Then
Deallocations(i).Count = 1
Return
End If
Next

PrintL "Failed to free", where
End Function



Petr

kryton9
10-07-2010, 00:07
Thanks for the replies guys.

@Mike, wow never knew of that component, next time I install my delphi I will have to look for that. Do you think it would work with Lazarus too?
@Frank thanks for the memory cleaner example. I use and play with the free IObit SmartRAM utility for operations for cleaning and monitoring. I recommend it, so far very nice.

@MichaelHappy hunting, it will be interesting to see what that program was.

@Petr thanks for the example. Yes in c++, I am wrapping most of my new and deletes in the classes, my problem is with raw opengl and other libraries where the releasing is done via opengl. For example, I was working on writing raw opengl in an irrlicht custom scene node. I made 2 display lists and after a while I realized I forgot to delete the display lists. If I had a memory leak program to test my program, I would have at least known sooner and eventually have traced the problem down. I was just lucky that I saw in another example of display lists and the author of it deleted the lists.

I also read a great article, but this applies to c++ because of being able to assign NULL to pointers. When ever you declare a pointer you assign NULL to it. Then you assign via the new command afterwards. At the end of the program you run what the author calls a zap function. You delete all of your pointers. Since c++ always checks a pointer if it is NULL before trying to delete, it won't delete it. But if it is not NULL it will delete it. So a very safe and secure way of knowing that all dynamically created memory for sure is deleted. This is a very simple solution and great I thought. But it doesn't help with display lists, since you have to know that behind the scenes it is being dynamically created.

Petr Schreiber
10-07-2010, 08:03
Hi Kent,

for OpenGL specific problems you can use the same approach - separate alloc/free for display lists, textures, ...
But even more comfortable is use of GL Intercept.

What it is?

Just simple DLL named OpenGL.DLL, but with enabled logging of errors and unreleased resources. I used it frequently with TBGL to make sure it is ok.
Grab it here: http://glintercept.nutty.org/

The only problems are these 2:

It slows the execution a bit
If you allocate handle for display list, do not define it, and then delete, it gives false positive


You can fine tune what you need to log, it can log even code of display lists and store textures for you. There are commercial solutions like gDebugger, but GLIntercept is enough for what you need.


Petr

Michael Hartlef
10-07-2010, 10:15
@Kent:

I think it was called MemCheck. I don't know if it runs under lazarus too.

Michael

Michael Clease
10-07-2010, 11:15
Sorry Kent I cant find the program but I suspect it was during my borland C phase :lol:

Charles Pegge
10-07-2010, 23:02
Kent, If the memory leaks are cumulative on a fairly large scale you can go quite a long way with windows task manager. I find it indispensable when working at the Assembler level.

Charles

kryton9
10-07-2010, 23:22
@Mike, thanks for the tip on the control for Delphi-- MemCheck. Next time I install Lazarus I will search for it and try it out. I took Lazarus off so I would stay focused on c++ :)

@Petr Thanks that looks like the magic tool I was looking for!!!

@Michael thanks for searching. You can't believe how many duplicates I have stored away of stuff not needed anymore, and how many things I wish I still had that I thought I backed up that are lost forever. Especially test programs that I write in different languages and then use for refreshing my memory in the future!!!

@Charles You might want to try out IOBits SmartMemory. It runs in the system tray and shows how much free memory is left all the time. You can then double click on it to monitor in a graph and compress or release memory. It is a very nice free tool. There free system care is very good too. With all the testing I do and installing and uninstalling languages, frameworks, it helps keep my computer running nicely.