Finding leaking memory - what function to use?

Mac OSX specific forum
jesperbrannmark
Enthusiast
Enthusiast
Posts: 536
Joined: Mon Feb 16, 2009 10:42 am
Location: sweden
Contact:

Finding leaking memory - what function to use?

Post by jesperbrannmark »

Hi.
I am trying to find a memleak issue.
I am limited to using activity monitor and look for any increase and then guess what function (from debugger output) is running at that time.
Yesterday my program was running using 1.2 GB RAM and now I got it down to around 80 MB - but it still increases everytime I do something.

Is there a function to return the amount of memory used by the program as a integer? That is really what I would need, because then I can put something in the beginning and end of the procedures I want to pinpoint, like:

Code: Select all

Procedure MyFunction()
  StartMem.i = FUNCTIONTOGETMEMORY()
  ;code goes here
  Debug Str(FUNCTIONTOGETMEMORY()-StartMem)+" bytes used by function"
Endprocedure
Similar to what I can do with ellapsedmilliseconds() - but for memory?
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Finding leaking memory - what function to use?

Post by skywalk »

Search for noleak from luis.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
jesperbrannmark
Enthusiast
Enthusiast
Posts: 536
Joined: Mon Feb 16, 2009 10:42 am
Location: sweden
Contact:

Re: Finding leaking memory - what function to use?

Post by jesperbrannmark »

That could be handy, but honestly - I dont use any of the functions its tracing so it will not help me this time.
I really need to know size allocated memory (total) at beginning and end of a procedure.
jesperbrannmark
Enthusiast
Enthusiast
Posts: 536
Joined: Mon Feb 16, 2009 10:42 am
Location: sweden
Contact:

Re: Finding leaking memory - what function to use?

Post by jesperbrannmark »

Yes. Looking at noleak, variable viewer and comparing PC to MAC I can definitely say that the memory leak is only on MAC. This bring me to think that some function in PB is not freeing up memory like it does on the PC.
There must be someone cleaver that know how to get program used memory...
jesperbrannmark
Enthusiast
Enthusiast
Posts: 536
Joined: Mon Feb 16, 2009 10:42 am
Location: sweden
Contact:

Re: Finding leaking memory - what function to use?

Post by jesperbrannmark »

OK. I tried AppleScript and it works somewhat.
I need first a way to replace this
a.s="set pid to pidOfRunningApp("+Chr(34)+"PureBasic1"+Chr(34)+")"+#CRLF$
with actual executable name instead of "PureBasic1". Is there a string with the compiled programs filename?

Test program:

Code: Select all

;memleaktest.pb
;checks for allocated memory of program
;saves output in /tmp/ folder
IncludeFile "continate.pb"
Procedure LeakSomething()
  Continate(#memory,"LeakSomething Start")
  AllocateMemory(10*1024)
  Continate(#memory,"LeakSomething Stop")
EndProcedure
For i=1 To 10
  LeakSomething()
Next
Includefile "continate.pb"

Code: Select all

;continate.pb
#Memory=1
#CPU=2
Procedure.l Continate(i.b=#Memory,name.s="")
  a.s="set pid to pidOfRunningApp("+Chr(34)+"PureBasic1"+Chr(34)+")"+#CRLF$
  a.s+"if pid is -1 then"+#CRLF$
  a.s+"	-- app is not running, so nothing to do"+#CRLF$
  a.s+"else"+#CRLF$
  a.s+"	set info to getProcessInfo(pid)"+#CRLF$
  a.s+"	if info is "+Chr(34)+""+Chr(34)+" then return"+#CRLF$
  a.s+"	return (round (first word of info as real)) & (round (second word of info as real))"+#CRLF$
  a.s+"end if"+#CRLF$
  a.s+"on fixLineEndings(str)"+#CRLF$
  a.s+"	set oldTIDs to AppleScript's text item delimiters"+#CRLF$
  a.s+"	set theLines to paragraphs of str"+#CRLF$
  a.s+"	set AppleScript's text item delimiters to ASCII character 10"+#CRLF$
  a.s+"	set fixedStr to theLines as string"+#CRLF$
  a.s+"	set AppleScript's text item delimiters to oldTIDs"+#CRLF$
  a.s+"	return fixedStr"+#CRLF$
  a.s+"end fixLineEndings"+#CRLF$
  a.s+"on getProcessInfo(pid)"+#CRLF$
  a.s+"	set perlCode to fixLineEndings("+Chr(34)+#CRLF$
  a.s+"open(PS, \"+Chr(34)+"/bin/ps -p "+Chr(34)+" & pid & "+Chr(34)+" -o pid,rss,%cpu |\"+Chr(34)+");"+#LF$
  a.s+"while (<PS>)"+#LF$
  a.s+"{"+#LF$
  a.s+"    if (/^\\s*"+Chr(34)+" & pid & "+Chr(34)+"\\s+(\\d+)\\s+([\\d.]+)\\s*$/)"+#LF$
  a.s+"    {"+#LF$
  a.s+"        my $rss = $1;"+#LF$
  a.s+"        my $cpu = $2;"+#LF$
  a.s+"        my $rssMB = sprintf(\"+Chr(34)+"%.1f\"+Chr(34)+", $rss / 1);"+#LF$
  a.s+"        print \"+Chr(34)+"$rssMB $cpu\"+Chr(34)+";"+#LF$
  a.s+"        last;"+#LF$
  a.s+"    }"+#LF$
  a.s+"}"+#LF$
  a.s+"close(PS);"+#LF$
  a.s+""+Chr(34)+")"+#LF$
  a.s+"	set info to do shell script "+Chr(34)+"perl -e "+Chr(34)+" & quoted form of perlCode"+#CRLF$
  a.s+"	return info"+#CRLF$
  a.s+"end getProcessInfo"+#CRLF$
  a.s+"on removeLastPart(str, delim)"+#CRLF$
  a.s+"	set oldTIDs to AppleScript's text item delimiters"+#CRLF$
  a.s+"	set AppleScript's text item delimiters to delim"+#CRLF$
  a.s+"	if (count str's text items) > 1 then"+#CRLF$
  a.s+"		set str to str's text 1 thru text item -2"+#CRLF$
  a.s+"	end if"+#CRLF$
  a.s+"	set AppleScript's text item delimiters to oldTIDs"+#CRLF$
  a.s+"	return str"+#CRLF$
  a.s+"end removeLastPart"+#CRLF$
  a.s+"on pidOfRunningApp(appName)"+#CRLF$
  a.s+"	tell application "+Chr(34)+"System Events"+Chr(34)+#CRLF$
  a.s+"		try"+#CRLF$
  a.s+"			set pid to the unix id of process appName"+#CRLF$
  a.s+"		on error"+#CRLF$
  a.s+"			set pid to -1"+#CRLF$
  a.s+"		end try"+#CRLF$
  a.s+"	end tell"+#CRLF$
  a.s+"	return pid"+#CRLF$
  a.s+"end pidOfRunningApp"+#CRLF$
  ret.l=Val(StringField(COCOA_AppleScript(a.s),i.b," "))
  If name.s
    fil.l=OpenFile(#PB_Any, GetTemporaryDirectory()+"memory.csv")    ; opens an existing file or creates one, if it does not exist yet
    If fil.l
      FileSeek(fil, Lof(fil))         ; jump to the end of the file (result of Lof() is used)
      WriteStringN(fil, FormatDate("%yyyy-%mm-%dd %hh:%ii",Date())+Chr(9)+name+Chr(9)+Str(ret.l))
      CloseFile(fil)
    EndIf
  EndIf
  ProcedureReturn ret.l
EndProcedure
Edit: Credit to original author: http://hayne.net/MacDev/AppleScript/app ... pplescript
Last edited by jesperbrannmark on Thu Mar 29, 2012 10:51 am, edited 1 time in total.
jesperbrannmark
Enthusiast
Enthusiast
Posts: 536
Joined: Mon Feb 16, 2009 10:42 am
Location: sweden
Contact:

Re: Finding leaking memory - what function to use?

Post by jesperbrannmark »

(continate was just the opposite of incontinate - so the opposite of leaking.. i dont know if there is such a word or thing.. probably not)
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Finding leaking memory - what function to use?

Post by Shardik »

jesperbrannmark wrote:Is there a function to return the amount of memory used by the program as a integer?
Try my procedure GetUsedMemory(). It's called from a window timer
every second and displays the program's memory usage in a small
window. The integer value in bytes returned from GetUsedMemory()
is converted to MB and displayed as a string with StrF and one digit
behind the decimal point. So you have the direct comparison to the
value displayed in the activity monitor.

You have to compile my code example to an app and start this app
together with the activity monitor. The running app should display
the same memory usage as the activity monitor... :wink:

I think the call of a mach kernel function is a much more appropriate
method to obtain the memory usage of a program than your lengthy
Applescript... :twisted:

Code: Select all

EnableExplicit

#TASK_BASIC_INFO_32 = 4

ImportC ""
  mach_task_self()
  task_info(TaskPort.I, InformationType.I, *TaskInfo, *BufferSize)
EndImport

Structure time_value
  Seconds.I
  Microseconds.I
EndStructure

Structure task_basic_info
  suspend_count.I
  virtual_size.I
  resident_size.I
  user_time.time_value
  system_time.time_value
  policy.I
EndStructure

Procedure.I GetUsedMemory()
  Protected TaskInfo.task_basic_info
  Protected TaskInfoSize.I
  Protected TaskPort.I

  TaskPort = mach_task_self()
  TaskInfoSize = SizeOf(task_basic_info)
  
  If task_info(TaskPort, #TASK_BASIC_INFO_32, @TaskInfo, @TaskInfoSize) = 0
    ProcedureReturn TaskInfo\resident_size
  EndIf

  ProcedureReturn
EndProcedure

Define i.I

OpenWindow(0, 270, 100, 170, 40, "Memory usage")
TextGadget(0, 10, 10, WindowWidth(0) - 20, 20, "", #PB_Text_Center)
SetGadgetText(0, StrF(GetUsedMemory() / 1048576, 1) + " MB")
AddWindowTimer(0, 0, 1000)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Timer
      SetGadgetText(0, StrF(GetUsedMemory() / 1048576, 1) + " MB")
  EndSelect
ForEver

RemoveWindowTimer(0, 0)
Post Reply