PDA

View Full Version : One way to debug memory leaks



Yakman
03-02-2008, 02:36 PM
Introduction
In SCAR, Bitmaps and DTMs are stored so-called dynamic memory, this means that the script must request memory from the operating system, and free the memory when it is finished.

when this memory is not returned to the system, it is in accessable to anything else, effectivly it is lost. This is called a Memory Leak, because like a jug leaking water, the memory slows disappears. Every time that code is run, a bit of memory is lost.

This can be a big problem for scar scripts, because they tend to run for a long time, the memory leaks and leaks, and eventually BOOM, the script runs out of memory and crashes.

To stop it you have to correctly return the memory to the system, by calling FreeBitmap or FreeDTM.
You might see this in the functions of SRL, they allocate a bitmap, and free it when they are finished, the memory is returned.

Debugging
This way of debugging memory leaks is one I saw while learning C/C++, it might be useful for SCAR, because that can also suffer from memory leaks.

The idea is, you have your own array of integers, when you allocate, place the handle into the array, and when you free it, remove it.

const
Initial_Capacity = 8;

var
gMemList: array of Integer;

//Allocates newHandle to a space in the array
function Alloc(newHandle: Integer):Integer;
var
f, len: Integer;
begin
len:= GetArrayLength(gMemList);
for f:=0 to len-1 do
if(gMemList[f] = -1)then
begin
gMemList[f]:= newHandle;
Result:= f;
Exit;
end
//no space, get more
SetArrayLength(gMemList, (len * 2 / 3) + 1);
gMemList[len]:= newHandle;
Result:= len;
end;

//call this once at the start of script
procedure SetupMemList;
var
f: Integer;
begin
SetArrayLength(gMemList, Initial_Capacity);
for f:=0 to Initial_Capacity-1 do
gMemList[f]:= -1;
end;

//changes a memory pointer to a scar-handle
function MemToHandle(mem: Integer):Integer;
begin
if(mem >= GetArrayLength(gMemList))then
begin
Writeln('[Error] invalid pointer ' + IntToStr(mem) + ', returning -1');
Result:= -1;
Exit;
end
Result:= gMemList[mem];
if(Result = -1)then
Writeln('[Error] no memory allocated at ' + IntToStr(mem));
end;

//called to free the memory
function FreeMem(mem: Integer):Integer;
begin
Result:= MemToHandle(mem);
gMemList[mem]:= -1;
end;

//used to allocate a new bitmap
function DebugBitmapFromString(Width, Height: Integer; str: string):Integer;
var
handle: Integer;
begin
handle:= BitmapFromString(Width, Height, str);
Result:= Alloc(handle);
end;

//used to free a bitmap
procedure DebugFreeBitmap(mem: Integer);
var
handle: Integer;
begin
handle:= FreeMem(mem);
FreeBitmap(handle);
end;



The idea is, everywhere in your script that has BitmapFromString, you replace it with DebugBitmapFromString, and everywhere with FreeBitmap, do DebugFreeBitmap

also, when using the handle, you need to use MemToHandle
like this

var
bmp: Integer;

begin
bmp:= DebugBitmapFromString(...
FindBitmap(MemToHandle(bmp), ...
DebugFreeBitmap(bmp);
end;


So how does that help us find the memory leak?
well here's the clever part, we have access to the array now, it is be empty at the beginning of the script, and it should be empty at the end.
if there are handles at the end of the script, a memory leak has happened.

at specific parts of your script, call this procedure.

procedure CheckMemList;
var
f, len: Integer;
begin
len:= GetArrayLength(gMemList);
for f:=0 to len-1 do
if(gMemList[f] >= 0)then
begin
Writeln('Warning - Memory not freed at mem ' + IntToStr(f));
end
end;


so if you have leaking memory, you will get a very helpful warning in the debug box :)

another thing you can do, is add this line
SaveBitmap(gMemList[f], ScriptPath + 'LeakedBitmap' + IntToStr(f) + '.bmp');

then you can actually look at the leaked bitmap, and hopefully it will tell you which procedure needs fixing.

Here is an example buggy function

var
x, y: Integer;
bmp: Integer;

procedure FindAndClickBitmap;
begin
bmp := BitmapFromString(11, 13, 'z78DAA592510A00210844' +
'AFB4E6B6F91B6CDEFF48CBE68F302881094388F3485396BCC AB29' +
'53AB5A6B98AABBFFE43E81D734C15D48893BB725A44E0CEF7 B350' +
'A3F7A05777588FA6968938DEEBEB916077E478EF090139110 1232' +
'2D4BBA84FB2FE9BF58DAA6FF5073394C120');
if(Random(10) = 0)then
Exit;

if(FindBitmapSpiral(bmp, x, y, 0, 0, 500, 500))then
begin
MoveMouseSmooth(x, y);
ClickMouse(x, y, True);
end
FreeBitmap(bmp);
end;

A memory leak can happen here if Random(10) returns 0, as the bitmap will not be freed.

so lets add the debug thingys

var
x, y: Integer;
bmp: Integer;

procedure FindAndClickBitmap;
begin
bmp := DebugBitmapFromString(11, 13, 'z78DAA592510A00210844' +
'AFB4E6B6F91B6CDEFF48CBE68F302881094388F3485396BCC AB29' +
'53AB5A6B98AABBFFE43E81D734C15D48893BB725A44E0CEF7 B350' +
'A3F7A05777588FA6968938DEEBEB916077E478EF090139110 1232' +
'2D4BBA84FB2FE9BF58DAA6FF5073394C120');
if(Random(10) = 0)then
Exit;

if(FindBitmapSpiral(MemToHandle(bmp), x, y, 0, 0, 500, 500))then
begin
MoveMouseSmooth(x, y);
ClickMouse(x, y, True);
end
DebugFreeBitmap(bmp);
end;


So run this script a few times with the CheckMemList; at the end


Successfully compiled
Successfully executed
Successfully compiled
Warning - Memory not freed at mem 0
Successfully executed

Hehe, there it is :)


Do rememeber that this can slow down your script, because you are constantly going through that array, so make sure you only use this while writing and debugging your script.

Also remember you can have all types of dynamic memory debugged like this, including DTMs, Character sets and Connections.
you can use the same array for everything, so adding a new type of memory is easy, here are procedures for DTMs


//used to allocate a dtm
function DebugDTMFromString(str: string): Integer;
var
handle: Integer;
begin
handle:= DTMFromString(str);
Result:= Alloc(handle);
end;

//used to free a dtm
procedure DebugFreeDTM(mem: Integer);
var
handle: Integer;
begin
handle:= FreeMem(mem);
FreeDTM(handle);
end;



Also, Memory Leaks arn't that big a problem, so dont know how useful this will be to people

Timer
03-02-2008, 02:38 PM
First Post!!!
Looking over now...

Looks great!, think there is an achual solve to memory leaks yet?

Harry
03-02-2008, 02:49 PM
Nice Yakman!

Santa_Clause
03-09-2008, 12:03 PM
Holy dang...Yakman knows all this really unique and unknown stuff. Great job! Rep++.

HellBoyz
03-14-2008, 01:22 AM
Woot Nice tut. Fully support you...