NoLeak - include to help you find memory leaks (PB5.20)

Share your advanced PureBasic knowledge/code with the community.
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

NoLeak - include to help you find memory leaks (PB5.20)

Post by luis »

This is based on the idea showed by Trond here (thank you!)

The differences are:

1) There is no need to modify the affected function calls in your source, (no search and replace). Just add the include at the top of it.
When the debugger is enabled, the include code is tracking your functions calls, when the debugger is disabled, all is back to normal.

2) The trace data is added to the linked list only if the allocation if successful.
If an allocation fails I choosed to not add the relative trace data, because a correspondent FreeMemory() is not actually needed and there is no leakage.
Moreover I don't even know if we would have the memory to add a new item to the list ! :shock:

3) The traced functions are listed at the top of the source.

I added the images-related commands because, at least in my case, I think another good source of problems are sometimes temporary images used in your procedures. If you forgot a FreeImage() and the *bad* procedure is called a lot, you waste a lot of memory pretty fast.

Probably you can extend it to track other functions, maybe even API functions, didn't try it but should work following the same template (a macro + a renamed proc).

There are so many possible causes for a memory leak this include maybe should be view more as a learning tool for people new to PB (to detect naive errors from the start) than a real useful aid to debugging, but anyway.... here it is.

The reported data is to be taken with a grain of salt. Not always, for example, a image not freed is a bad thing.
One case is when it's created by the program and then not freed just before its end. It will be freed anyway at the exit.
But as said above a temp image used in a procedure is another thing and you must keep an eye on them !

1.2 added locking for multithreading
1.3 added support for automatic deallocation of PB objects when using explicit numeric constants instead of #PB_Any
1.4 corrected a minor problem in the demo and updated the code for PB 5.20

If you want to test for GDI leakages in Windows a good tool is GDIView -> http://www.nirsoft.net/utils/gdi_handles.html

INCLUDE v1.4.1 - NoLeak.pbi

Code: Select all

EnableExplicit

;******************************************************************************
; NoLeak 1.4.1 by Luis 
; This is updated for PB 5.20 and may not work with previous versions.
;
; The traced functions are:
;
; AllocateMemory (iBytes, iFlags = 0)
; ReAllocateMemory (iAddress, iBytes, iFlags = 0)
; FreeMemory (iAddress)
; LoadImage (iImageNum, Filename$, iFlags = 0)
; GrabImage (iImageSrc, iImageNum, iX, iY, iWidth, iHeight)
; CopyImage (iImageSrc, iImageNum)
; CatchImage (iImageNum, iMemoryAddress, iSize = $7FFFFFFF)
; CreateImage (iImageNum, iWidth, iHeight, iDepth = 24, iBackColor = RGB(0,0,0))
; FreeImage (iImageNum)
; LoadFont (iFontNum, Filename$, iYSize, iFlags = #PB_Font_HighQuality)
; FreeFont (iFontNum)
;******************************************************************************

CompilerIf #PB_Compiler_Debugger


CompilerIf (#PB_Compiler_Thread = 1)
 Global hLeakMutex = CreateMutex()
 
 Macro M_LockMutex(mutex)
  LockMutex(mutex)
 EndMacro
 
 Macro M_UnlockMutex(mutex)
  UnlockMutex(mutex)
 EndMacro
 
CompilerElse

 Macro M_LockMutex(mutex)
 EndMacro
 
 Macro M_UnlockMutex(mutex)
 EndMacro
CompilerEndIf


Structure T_NOLEAK_FONT
  Source$
  iLine.i
  iFontNum.i
  Filename$
  iYsize.i
EndStructure : NewList NoLeak_FONT.T_NOLEAK_FONT()

Structure T_NOLEAK_ALLOC
  Source$
  iLine.i
  iAddress.i 
  iBytes.i     
EndStructure : NewList NoLeak_ALLOC.T_NOLEAK_ALLOC()

Structure T_NOLEAK_IMAGE
  Source$
  iLine.i
  iImageNum.i 
  iWidth.i
  iHeight.i
  iDepth.i
EndStructure : NewList NoLeak_IMAGE.T_NOLEAK_IMAGE()


;{ FreeFont()

Procedure _FreeFont (iFontNum, iOverWritten = 0)
 Shared NoLeak_FONT()
 
 M_LockMutex(hLeakMutex)
 
 ForEach NoLeak_FONT()
    If NoLeak_FONT()\iFontNum = iFontNum
        DeleteElement(NoLeak_FONT())
        Break
    EndIf
 Next
 
 M_UnlockMutex(hLeakMutex)
 
 If iOverWritten = 0
    FreeFont(iFontNum)
 EndIf
 
EndProcedure

Macro FreeFont (iFontNum)
 _FreeFont (iFontNum)
EndMacro

;}


;{ LoadFont()

Procedure.i _LoadFont (Source$, iLine, iFontNum, Filename$, iYSize, iFlags)
 Shared NoLeak_FONT()
 
 Protected iRetVal

 iRetVal = LoadFont (iFontNum, Filename$, iYSize, iFlags)

 If iRetVal
    If iFontNum <> #PB_Any 
        _FreeFont(iFontNum, 1)
    EndIf
   
    M_LockMutex(hLeakMutex)
   
    AddElement(NoLeak_FONT())
    NoLeak_FONT()\Source$ = Source$
    NoLeak_FONT()\iLine = iLine
    NoLeak_FONT()\Filename$ = Filename$
    NoLeak_FONT()\iYsize = iYsize

     If iFontNum = #PB_Any
        NoLeak_FONT()\iFontNum = iRetVal
     Else
        NoLeak_FONT()\iFontNum = iFontNum
     EndIf
     
    M_UnlockMutex(hLeakMutex)
 EndIf
     
 ProcedureReturn iRetVal
EndProcedure


Macro LoadFont (iFontNum, Filename, iYSize, iFlags = #PB_Font_HighQuality)
 _LoadFont (#PB_Compiler_File, #PB_Compiler_Line, iFontNum, Filename, iYSize, iFlags)
EndMacro

;}


;{ AllocateMemory()

Procedure.i _AllocateMemory (Source$, iLine, iBytes, iFlags)
 Shared NoLeak_ALLOC()
 
 Protected iRetVal

 iRetVal = AllocateMemory (iBytes, iFlags)

 If iRetVal
    M_LockMutex(hLeakMutex)   
   
    AddElement(NoLeak_ALLOC())
    NoLeak_ALLOC()\Source$ = Source$
    NoLeak_ALLOC()\iLine = iLine
    NoLeak_ALLOC()\iBytes = iBytes
    NoLeak_ALLOC()\iAddress = iRetVal
   
    M_UnlockMutex(hLeakMutex)
 EndIf

 ProcedureReturn iRetVal
EndProcedure

Macro AllocateMemory (iBytes, iFlags = 0)
 _AllocateMemory (#PB_Compiler_File, #PB_Compiler_Line, iBytes, iFlags)
EndMacro

;}

;{ ReAllocateMemory()

Procedure.i _ReAllocateMemory (Source$, iLine, iAddress, iBytes, iFlags)
 Shared NoLeak_ALLOC()
 
 Protected iRetVal
 
 iRetVal = ReAllocateMemory (iAddress, iBytes, iFlags)

 If iRetVal
    M_LockMutex(hLeakMutex)
   
    ForEach NoLeak_ALLOC()
        If NoLeak_ALLOC()\iAddress = iAddress
            DeleteElement(NoLeak_ALLOC())
            Break
        EndIf
    Next
   
    AddElement(NoLeak_ALLOC())
    NoLeak_ALLOC()\Source$ = Source$
    NoLeak_ALLOC()\iLine = iLine
    NoLeak_ALLOC()\iBytes = iBytes
    NoLeak_ALLOC()\iAddress = iRetVal
   
    M_UnlockMutex(hLeakMutex)
 EndIf
     
 ProcedureReturn iRetVal
EndProcedure

Macro ReAllocateMemory (iAddress, iBytes, iFlags = 0)
 _ReAllocateMemory (#PB_Compiler_File, #PB_Compiler_Line, iAddress, iBytes, iFlags)
EndMacro

;}

;{ FreeMemory()

Procedure _FreeMemory (iAddress)
 Shared NoLeak_ALLOC()
 
 M_LockMutex(hLeakMutex)
 
 ForEach NoLeak_ALLOC()
    If NoLeak_ALLOC()\iAddress = iAddress
        DeleteElement(NoLeak_ALLOC())
        Break
    EndIf
 Next

 M_UnlockMutex(hLeakMutex)
 
 FreeMemory(iAddress)
EndProcedure

Macro FreeMemory (iAddress)
 _FreeMemory (iAddress)
EndMacro

;}


;{ FreeImage()

Procedure _FreeImage (iImageNum, iOverWritten = 0)
 Shared NoLeak_IMAGE()
 
 M_LockMutex(hLeakMutex)
 
 ForEach NoLeak_IMAGE()
    If NoLeak_IMAGE()\iImageNum = iImageNum
        DeleteElement(NoLeak_IMAGE())
        Break
    EndIf
 Next

 M_UnlockMutex(hLeakMutex)
 
 If iOverWritten = 0
    FreeImage(iImageNum)
 EndIf
 
EndProcedure

Macro FreeImage (iImageNum)
 _FreeImage (iImageNum)
EndMacro

;}


;{ LoadImage()

Procedure.i _LoadImage (Source$, iLine, iImageNum, Filename$, iFlags)
 Shared NoLeak_IMAGE()
 
 Protected iRetVal, iRetImage

 iRetVal = LoadImage (iImageNum, Filename$, iFlags)

 If iRetVal
    If iImageNum <> #PB_Any
        _FreeImage (iImageNum, 1)
    EndIf
   
    M_LockMutex(hLeakMutex)
   
    AddElement(NoLeak_IMAGE())
    NoLeak_IMAGE()\Source$ = Source$
    NoLeak_IMAGE()\iLine = iLine

    If iImageNum = #PB_Any
        iRetImage = iRetVal
    Else
        iRetImage = iImageNum
    EndIf
     
    NoLeak_IMAGE()\iImageNum = iRetImage
    NoLeak_IMAGE()\iWidth = ImageWidth(iRetImage)
    NoLeak_IMAGE()\iHeight = ImageHeight(iRetImage)
    NoLeak_IMAGE()\iDepth = ImageDepth(iRetImage)
   
    M_UnlockMutex(hLeakMutex)
 EndIf
     
 ProcedureReturn iRetVal
EndProcedure

Macro LoadImage (iImageNum, Filename, iFlags = 0)
 _LoadImage (#PB_Compiler_File, #PB_Compiler_Line, iImageNum, Filename, iFlags)
EndMacro

;}

;{ GrabImage()

Procedure.i _GrabImage (Source$, iLine, iImageSrc, iImageNum, iX, iY, iWidth, iHeight)
 Shared NoLeak_IMAGE()
 
 Protected iRetVal, iRetImage

 iRetVal = GrabImage (iImageSrc, iImageNum, iX, iY, iWidth, iHeight)

 If iRetVal
    If iImageNum <> #PB_Any
        _FreeImage (iImageNum, 1)
    EndIf
   
    M_LockMutex(hLeakMutex)
   
    AddElement(NoLeak_IMAGE())
    NoLeak_IMAGE()\Source$ = Source$
    NoLeak_IMAGE()\iLine = iLine

    If iImageNum = #PB_Any
        iRetImage = iRetVal
    Else
        iRetImage = iImageNum
    EndIf
     
    NoLeak_IMAGE()\iImageNum = iRetImage
    NoLeak_IMAGE()\iWidth = ImageWidth(iRetImage)
    NoLeak_IMAGE()\iHeight = ImageHeight(iRetImage)
    NoLeak_IMAGE()\iDepth = ImageDepth(iRetImage)
   
    M_UnlockMutex(hLeakMutex)
 EndIf
     
 ProcedureReturn iRetVal
EndProcedure

Macro GrabImage (iImageSrc, iImageNum, iX, iY, iWidth, iHeight)
 _GrabImage (#PB_Compiler_File, #PB_Compiler_Line, iImageSrc, iImageNum, iX, iY, iWidth, iHeight)
EndMacro

;}

;{ CopyImage()

Procedure.i _CopyImage (Source$, iLine, iImageSrc, iImageNum)
 Shared NoLeak_IMAGE()
 
 Protected iRetVal, iRetImage

 iRetVal = CopyImage (iImageSrc, iImageNum)

 If iRetVal
    If iImageNum <> #PB_Any
        _FreeImage (iImageNum, 1)
    EndIf
   
    M_LockMutex(hLeakMutex)     
   
    AddElement(NoLeak_IMAGE())
    NoLeak_IMAGE()\Source$ = Source$
    NoLeak_IMAGE()\iLine = iLine

    If iImageNum = #PB_Any
        iRetImage = iRetVal
    Else
        iRetImage = iImageNum
    EndIf
     
    NoLeak_IMAGE()\iImageNum = iRetImage
    NoLeak_IMAGE()\iWidth = ImageWidth(iRetImage)
    NoLeak_IMAGE()\iHeight = ImageHeight(iRetImage)
    NoLeak_IMAGE()\iDepth = ImageDepth(iRetImage)
   
    M_UnlockMutex(hLeakMutex)
 EndIf
     
 ProcedureReturn iRetVal
EndProcedure

Macro CopyImage (iImageSrc, iImageNum)
 _CopyImage (#PB_Compiler_File, #PB_Compiler_Line, iImageSrc, iImageNum)
EndMacro

;}

;{ CatchImage()

Procedure.i _CatchImage (Source$, iLine, iImageNum, iMemoryAddress, iSize)
 Shared NoLeak_IMAGE()
 
 Protected iRetVal, iRetImage

 iRetVal = CatchImage (iImageNum, iMemoryAddress, iSize)

 If iRetVal
    If iImageNum <> #PB_Any
        _FreeImage (iImageNum, 1)
    EndIf
   
    M_LockMutex(hLeakMutex)
   
    AddElement(NoLeak_IMAGE())
    NoLeak_IMAGE()\Source$ = Source$
    NoLeak_IMAGE()\iLine = iLine

    If iImageNum = #PB_Any
        iRetImage = iRetVal
    Else
        iRetImage = iImageNum       
    EndIf
     
    NoLeak_IMAGE()\iImageNum = iRetImage
    NoLeak_IMAGE()\iWidth = ImageWidth(iRetImage)
    NoLeak_IMAGE()\iHeight = ImageHeight(iRetImage)
    NoLeak_IMAGE()\iDepth = ImageDepth(iRetImage)
   
    M_UnlockMutex(hLeakMutex)
 EndIf
     
 ProcedureReturn iRetVal
EndProcedure

Macro CatchImage (iImageNum, iMemoryAddress, iSize = $7FFFFFFF)
 _CatchImage (#PB_Compiler_File, #PB_Compiler_Line, iImageNum, iMemoryAddress, iSize)
EndMacro

;}

;{ CreateImage()

Procedure.i _CreateImage (Source$, iLine, iImageNum, iWidth, iHeight, iDepth, iBackColor)
 Shared NoLeak_IMAGE()
 
 Protected iRetVal

 iRetVal = CreateImage (iImageNum, iWidth, iHeight, iDepth, iBackColor)

 If iRetVal
    If iImageNum <> #PB_Any
        _FreeImage (iImageNum, 1)
    EndIf
   
    M_LockMutex(hLeakMutex)
   
    AddElement(NoLeak_IMAGE())
    NoLeak_IMAGE()\Source$ = Source$
    NoLeak_IMAGE()\iLine = iLine
    NoLeak_IMAGE()\iWidth = iWidth
    NoLeak_IMAGE()\iHeight = iHeight
    NoLeak_IMAGE()\iDepth = iDepth

    If iImageNum = #PB_Any
        NoLeak_IMAGE()\iImageNum = iRetVal
    Else
        NoLeak_IMAGE()\iImageNum = iImageNum
    EndIf
   
    M_UnlockMutex(hLeakMutex)
 EndIf
     
 ProcedureReturn iRetVal
EndProcedure

Macro CreateImage (iImageNum, iWidth, iHeight, iDepth = 24, iBackColor = RGB(0,0,0))
 _CreateImage (#PB_Compiler_File, #PB_Compiler_Line, iImageNum, iWidth, iHeight, iDepth, iBackColor)
EndMacro

;}

CompilerEndIf

TEST PROGRAM

Code: Select all

IncludeFile "NoLeak.pb"

Procedure BadAllocation()
 Protected *ptr
 
 *ptr = AllocateMemory(1234, #PB_Memory_NoClear)
 
 ; overwrite same pointer
 *ptr = AllocateMemory(5678)
 
 ; this is bad, we lose the previous one
 FreeMemory(*ptr)
 
 ; this should sooner or later fail -> (*ptr = 0) so no leakage on this line
 ; if/when does not fails it causes as a second leakage
 *ptr = AllocateMemory($7FFFFFFF)
EndProcedure

Procedure GoodAllocation()
 Protected *ptr
 
 *ptr = AllocateMemory(4096)
 
 ; reuse same pointer
 *ptr = ReAllocateMemory(*ptr, 8192)
 
 ; all ok
 FreeMemory(*ptr)
EndProcedure


Procedure BadImageCreation()
 Protected img
 
 ; this is ok
 img = CreateImage(#PB_Any, 128, 128)
 FreeImage(img)

 ; this is freed by the one who follow
 CreateImage(1, 640, 480, 32)

 ; this is not freed right now but it will be in the next call of this procedure by the command above ...
 ; ... until the last loop, then it will leak once
 CreateImage(1, 320, 240)
 
 ; this is also not freed
 img = CreateImage(#PB_Any, 640, 480, 32)
 
 ; this is not either
 img = CopyImage(img, #PB_Any)
EndProcedure


Procedure GoodImageCreation()
 Protected img, img2
 
 ; this is ok
 img = CreateImage(#PB_Any, 128, 128)
 
 img2 = GrabImage(img, #PB_Any, 0, 0, 32, 32)
 
 FreeImage(img)
 
 FreeImage(img2) 
EndProcedure
 
Procedure Main()
 Protected img, k
 
 ; this is formally bad without a free, but it's acceptable
 img = CreateImage(0, 320, 200)
 
 For k = 1 To 3
    ; leakages inside functions called many times should be fixed
    ; especially if the "many times" is not known in advance !
       
    BadAllocation()
    GoodAllocation()
    BadImageCreation()
    GoodImageCreation()
 Next
 
EndProcedure

Main()

ShowVariableViewer()
CallDebugger 
EDIT : DELETED useless (and much complex) code to avoid confusion
EDIT : changed the example to remove the "F12 thing" causing some confusion, now we can use ShowVariableViewer()
EDIT : updated as requested to PB 5.2

If you prefer to dump the infos directly to the debug window you can obviously loop through the list with some code like this: http://www.purebasic.fr/english/viewtop ... 63#p424463
Last edited by luis on Thu Sep 19, 2013 7:36 pm, edited 21 times in total.
"Have you tried turning it off and on again ?"
A little PureBasic review
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: NoLeak - another include to help you find memory leaks

Post by freak »

Replacing a PB function can be done much simpler (and crossplatform)

Code: Select all

Procedure _CreateImage(Image, Width, Height, Depth)
  Debug "Hello World!"
  
  ; This is the real PB function because there is no macro yet
  ;
  ProcedureReturn CreateImage(Image, Width, Height, Depth) 
EndProcedure

Macro CreateImage(Image, Width, Height, Depth = 24)
  _CreateImage(Image, Width, Height, Depth)
EndMacro

; This is the replacement function because of the macro
;
CreateImage(0, 100, 100)
quidquid Latine dictum sit altum videtur
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: NoLeak - another include to help you find memory leaks

Post by luis »

Doh! Never thought it was possible only because macro is defined later...

That would have saved me a lot of trouble, but I least I learned something on pb insides anyway!

I thought the macro substitution had to be global, I should have known considering PB is a single pass compiler.
Moreover I have the habit to put all my macro on the top of the source, so I have never noticed this behavior!

SIGH!

Thank you, I'll try to change my include accordingly. It's certainly simpler :)

UPDATED the first post !
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by flaith »

Hi Luis, in your test code :

Code: Select all

CallDebugger ; press F12 here to inspect the NoLeak_*() lists 
I don't have F12 ?
“Fear is a reaction. Courage is a decision.” - WC
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by luis »

flaith wrote:

Code: Select all

CallDebugger ; press F12 here to inspect the NoLeak_*() lists 
I don't have F12 ?
Select Debugger -> Variable Viewer from the PB ide menu instead :)

(File -> Preferences -> General -> Shortcuts if you want to assign another hotkey)
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by flaith »

Got it, thanks :D
“Fear is a reaction. Courage is a decision.” - WC
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by Little John »

luis wrote:1) There is no need to modify the affected function calls in your source, (no search and replace).
luis wrote:2) The trace data is added to the linked list only if the allocation if successful.

Good ideas, thank you! :-)

Regards, Little John
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by jassing »

I ran out of ram the other day while running some tests, so I assumed a memory leak; and proceeded to trace all allocs/frees -- didnt see anything, then gave noleak a go...

What does it mean when the list has several entries with 0 for line, byte, and address, and a blank for sFile?

thanks
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by Trond »

No idea, but what if you try to stop the program halfway to see if there is any problem by then?

Also, there is a lot of other object types that can be leaked, this can be examined with the library viewer (debugger menu).
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by luis »

It shouldn't happen. Unless you are really reaching a point where there is simply no more memory available, in that case addelement() is supposed to fail but there is no test for that since this is *not* usable in a NEAR ZERO MEMORY situation, as it needs some available RAM to store its results.

As you can see from the source each addelement() is followed by the assignment of those values and #PB_Compiler_File and #PB_Compiler_Line certainly cannot be empty... right ?

So (excluding what said above) I don't know why it could happen, but since you have the source you can investigate using breakpoints and discover why.

If you really ate all your memory using some bad allocatememory() you should inspect the generated linked list before reaching the point of no return (force a break in the program before that) and at that point you should be able to see all the allocations un-matched by the appropriate freememory() calls.
"Have you tried turning it off and on again ?"
A little PureBasic review
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by jassing »

Thanks Luis...

Just figured I'd ask someone who knows more than I.... No this was on a test where I have not used all the memory... but I can see memory usage go up, when it really shouldn't be. I've spent days tracking & stepping thru this to be sure it's not me... as near as I can see, every allocatememory() has a matching freememory(). I'm not using any 3rd party libs, so I'm starting to think it might be a leak in pb's core libs...
thanks for your time.
cheers
-josh
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by luis »

There are so many possible causes it's hard to give you a suggestion without seeing the code.
If you use only PB functions another problem can be in the way you free (or not) memory when using structured lists, or maps. Remember if you use pointers for dynamically allocated structures used in lists or maps you need to free them before you delete the specific element. See ClearStructure.
"Have you tried turning it off and on again ?"
A little PureBasic review
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by rsts »

Don't know how I missed this originally, but thanks luis. It found a memory leak introduced when I converted from native SQLite commands to PB database commands.

Great tool.
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by jassing »

luis wrote:if you use pointers for dynamically allocated structures used in lists or maps you need to free them before you delete the specific element. See ClearStructure.
Thanks -- I remember that; but will check for that specifically...

** EDIT **
Found reason for the 0 entries -- threads. Here's modified code to use mutexes...

Code: Select all

EnableExplicit

;******************************************************************************
; No Leak 1.1 by Luis for PB 4.31 - 4.40
;
; The traced functions are:
;
; AllocateMemory (iBytes)
; ReAllocateMemory (iAddress, iBytes)
; FreeMemory (iAddress)
; CreateImage (iImageNum, iWidth, iHeight, iDepth)
; FreeImage (iImageNum)
;******************************************************************************

CompilerIf #PB_Compiler_Debugger
	Global hLeakMutex = CreateMutex()
	
  Structure T_ALLOC_DATA
    sFile.s
    iLine.i
    iBytes.i
    iAddress.i
    EndStructure : Global NewList NoLeak_ALLOC.T_ALLOC_DATA()
    
    Structure T_IMAGE_DATA
      sFile.s
      iLine.i
      iWidth.i
      iHeight.i
      iDepth.i
      iImageNum.i
      EndStructure : Global NewList NoLeak_IMAGE.T_IMAGE_DATA()
      
      
      ;{ AllocateMemory()
      
      Procedure.i _AllocateMemory (sFile.s, iLine, iBytes)
        Shared NoLeak_ALLOC()
        Protected iRetVal
        iRetVal = AllocateMemory (iBytes)
        
        If iRetVal
        	LockMutex(hLeakMutex)
          AddElement(NoLeak_ALLOC())
          NoLeak_ALLOC()\sFile = sFile
          NoLeak_ALLOC()\iLine = iLine
          NoLeak_ALLOC()\iBytes = iBytes
          NoLeak_ALLOC()\iAddress = iRetVal
          UnlockMutex(hLeakMutex)
        EndIf
        
        ProcedureReturn iRetVal
      EndProcedure
      
      Macro AllocateMemory (iBytes)
        _AllocateMemory (#PB_Compiler_File, #PB_Compiler_Line, iBytes)
      EndMacro
      
      ;}
      
      ;{ ReAllocateMemory()
      
      Procedure.i _ReAllocateMemory (sFile.s, iLine, iAddress, iBytes)
        Shared NoLeak_ALLOC()
        Protected iRetVal
        
        iRetVal = ReAllocateMemory (iAddress, iBytes)
        
        If iRetVal
        	LockMutex(hLeakMutex)
          ForEach NoLeak_ALLOC()
          	If NoLeak_ALLOC()\iAddress = iAddress
              DeleteElement(NoLeak_ALLOC())
              Break
            EndIf
          Next
          UnlockMutex(hLeakMutex)
          LockMutex(hLeakMutex)
          AddElement(NoLeak_ALLOC())
          NoLeak_ALLOC()\sFile = sFile
          NoLeak_ALLOC()\iLine = iLine
          NoLeak_ALLOC()\iBytes = iBytes
          NoLeak_ALLOC()\iAddress = iRetVal
          UnlockMutex(hLeakMutex)
        EndIf
        
        ProcedureReturn iRetVal
      EndProcedure
      
      Macro ReAllocateMemory (iAddress, iBytes)
        _ReAllocateMemory (#PB_Compiler_File, #PB_Compiler_Line, iAddress, iBytes)
      EndMacro
      
      ;}
      
      ;{ FreeMemory()
      
      Procedure _FreeMemory (iAddress)
        Shared NoLeak_ALLOC()
        LockMutex(hLeakMutex)
        ForEach NoLeak_ALLOC()
          If NoLeak_ALLOC()\iAddress = iAddress
            DeleteElement(NoLeak_ALLOC())
            Break
          EndIf
        Next
        UnlockMutex(hLeakMutex)
        FreeMemory(iAddress)
      EndProcedure
      
      Macro FreeMemory (iAddress)
        _FreeMemory (iAddress)
      EndMacro
      
      ;}
      
      ;{ CreateImage()
      
      Procedure.i _CreateImage (sFile.s, iLine, iImageNum, iWidth, iHeight, iDepth)
        Shared NoLeak_IMAGE()
        Protected iRetVal
        
        iRetVal = CreateImage (iImageNum, iWidth, iHeight, iDepth)
        
        If iRetVal
        	LockMutex(hLeakMutex)
          AddElement(NoLeak_IMAGE())
          NoLeak_IMAGE()\sFile = sFile
          NoLeak_IMAGE()\iLine = iLine
          NoLeak_IMAGE()\iWidth = iWidth
          NoLeak_IMAGE()\iHeight = iHeight
          NoLeak_IMAGE()\iDepth = iDepth
          UnlockMutex(hLeakMutex)
          
          If iImageNum = #PB_Any
            NoLeak_IMAGE()\iImageNum = iRetVal
          Else
            NoLeak_IMAGE()\iImageNum = iImageNum
          EndIf
        EndIf
        
        ProcedureReturn iRetVal
      EndProcedure
      
      Macro CreateImage (iImageNum, iWidth, iHeight, iDepth = 24)
        _CreateImage (#PB_Compiler_File, #PB_Compiler_Line, iImageNum, iWidth, iHeight, iDepth)
      EndMacro
      
      ;}
      
      ;{ FreeImage()
      
      Procedure _FreeImage (sFile.s, iLine, iImageNum)
        Shared NoLeak_IMAGE()
        LockMutex(hLeakMutex)
        ForEach NoLeak_IMAGE()
          If NoLeak_IMAGE()\iImageNum = iImageNum
            DeleteElement(NoLeak_IMAGE())
            Break
          EndIf
        Next
        UnlockMutex(hLeakMutex)
        FreeImage(iImageNum)
      EndProcedure
      
      Macro FreeImage (iImageNum)
        _FreeImage (#PB_Compiler_File, #PB_Compiler_Line, iImageNum)
      EndMacro
      
      ;}
      
    CompilerEndIf
    
    
    CompilerIf #False ; Simplier Method For funcs
      Procedure _CreateImage(Image, Width, Height, Depth)
        Debug "Hello World!"
        
        ; This is the real PB function because there is no macro yet
        ;
        ProcedureReturn CreateImage(Image, Width, Height, Depth)
      EndProcedure
      
      Macro CreateImage(Image, Width, Height, Depth = 24)
        _CreateImage(Image, Width, Height, Depth)
      EndMacro
      
      ; This is the replacement function because of the macro
      ;
      CreateImage(0, 100, 100)
    CompilerEndIf
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: NoLeak - include to help you find memory leaks (UPDATED)

Post by luis »

jassing wrote: Found reason for the 0 entries -- threads. Here's modified code to use mutexes...
Ahhhh right ! You should have mentioned that tough (but maybe I should have think of it) :wink:

Yes, didn't consider supporting threads at the time but why not ? You are right, added the appropriate code in the first post.

Thanks !

@rsts
Glad it was useful to you :)
"Have you tried turning it off and on again ?"
A little PureBasic review
Post Reply