SRL Archive - OSR Advanced Scripting Tutorials

One way to debug memory leaks


Yakman

2008-03-02 09:36:18

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' +
'AFB4E6B6F91B6CDEFF48CBE68F302881094388F3485396BCCAB29' +
'53AB5A6B98AABBFFE43E81D734C15D48893BB725A44E0CEF7B350' +
'A3F7A05777588FA6968938DEEBEB916077E478EF0901391101232' +
'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' +
'AFB4E6B6F91B6CDEFF48CBE68F302881094388F3485396BCCAB29' +
'53AB5A6B98AABBFFE43E81D734C15D48893BB725A44E0CEF7B350' +
'A3F7A05777588FA6968938DEEBEB916077E478EF0901391101232' +
'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

2008-03-02 09:38:29

First Post!!!
Looking over now…

[EDIT] Looks great!, think there is an achual solve to memory leaks yet? [/EDIT]

Harry

2008-03-02 09:49:50

Nice Yakman!

Santa_Clause

2008-03-09 08:03:03

Holy dang…Yakman knows all this really unique and unknown stuff. Great job! Rep++.

HellBoyz

2008-03-13 21:22:10

Woot Nice tut. Fully support you…