851 lines
23 KiB
ObjectPascal
851 lines
23 KiB
ObjectPascal
unit PointerscanWorker;
|
|
|
|
//class responsible for receiving paths and working them out
|
|
|
|
{$mode delphi}
|
|
|
|
interface
|
|
|
|
uses
|
|
windows, Classes, SysUtils, syncobjs, PointerscanStructures, ProcessHandlerUnit, pointervaluelist,
|
|
pointeraddresslist, NewKernelHandler, zstream, zstreamext;
|
|
|
|
type
|
|
TPointerscanWorker = class (tthread)
|
|
private
|
|
|
|
|
|
pointersize: integer;
|
|
fOnException: TNotifyEvent;
|
|
|
|
temppathqueue: TPathQueueElement;
|
|
|
|
|
|
procedure rscan(valuetofind:ptrUint; level: valSint);
|
|
procedure StorePath(level: valSint; moduleid: integer; offset: ptruint);
|
|
function DoRescan(level: valSint; moduleid: integer; offset: ptruint): boolean;
|
|
protected
|
|
results: tstream;
|
|
procedure initialize; virtual; abstract;
|
|
procedure flushresults; virtual; abstract;
|
|
procedure flushifneeded; virtual;
|
|
public
|
|
pointerlisthandler: TReversePointerListHandler;
|
|
pathqueuesemaphore: THandle;
|
|
pathqueuelength: ^integer;
|
|
pathqueueCS: TCriticalSection;
|
|
pathqueue: PMainPathQueue;
|
|
|
|
OutOfDiskSpace: ^boolean;
|
|
mustEndWithSpecificOffset: boolean;
|
|
mustendwithoffsetlist: array of dword;
|
|
|
|
useHeapData: boolean;
|
|
useOnlyHeapData: boolean;
|
|
|
|
onlyOneStaticInPath: boolean;
|
|
|
|
|
|
|
|
valuetofind: ptrUint;
|
|
maxlevel: integer;
|
|
structsize: integer;
|
|
// startaddress: dword;
|
|
startlevel: integer;
|
|
alligned: boolean;
|
|
staticonly: boolean;
|
|
noLoop: boolean;
|
|
|
|
LimitToMaxOffsetsPerNode: boolean;
|
|
MaxOffsetsPerNode: integer;
|
|
|
|
|
|
isFlushing: boolean;
|
|
timespentwriting: qword;
|
|
currentwritestart: qword;
|
|
|
|
|
|
|
|
isdone: boolean;
|
|
hasTerminated: boolean;
|
|
// savestate: boolean;
|
|
stop: boolean;
|
|
|
|
//staticscanner: TStaticscanner;
|
|
tempresults: array of dword; //offsetlist
|
|
valuelist: array of ptruint; //used by noLoop .
|
|
{
|
|
I could have used a map, but inserting in a map takes longer than a array append
|
|
Also, the array is maxlevel big, and usually not that long
|
|
Really not sure what's the best solution in this case though
|
|
}
|
|
|
|
|
|
//info:
|
|
currentaddress: pointer;
|
|
currentlevel: integer;
|
|
LookingForMin: ptrUint;
|
|
LookingForMax: ptrUint;
|
|
//lastaddress: ptrUint;
|
|
|
|
|
|
|
|
haserror: boolean;
|
|
errorstring: string;
|
|
|
|
pathsEvaluated: qword;
|
|
pointersfound: qword;
|
|
|
|
compressedptr: boolean;
|
|
MaxBitCountModuleIndex: dword;
|
|
MaxBitCountModuleOffset: dword;
|
|
MaxBitCountLevel: dword;
|
|
MaxBitCountOffset: dword;
|
|
|
|
MaskModuleIndex: dword;
|
|
MaskLevel: dword;
|
|
MaskOffset: dword;
|
|
|
|
compressedEntry: pbytearray;
|
|
compressedEntrySize: integer;
|
|
|
|
mustendwithoffsetlistlength: integer;
|
|
|
|
|
|
instantrescan: boolean;
|
|
instantrescanlistcount: integer;
|
|
instantrescanlist: array of TPointerListHandler;
|
|
instantrescanaddress: array of ptruint;
|
|
|
|
savestate: boolean;
|
|
overflowqueuewriter:TQueueWriterMethod;
|
|
procedure SaveStateAndTerminate;
|
|
|
|
|
|
|
|
procedure execute; override;
|
|
constructor create(suspended: boolean);
|
|
destructor destroy; override;
|
|
|
|
property OnException: TNotifyEvent read fOnException write fOnException;
|
|
end;
|
|
|
|
TFlushResultsEvent=function(size: integer; m: TMemoryStream): boolean of object;
|
|
TPointerscanWorkerNetwork=class(TPointerscanWorker)
|
|
private
|
|
fFlushSize: integer;
|
|
fOnFlushResults: TFlushResultsEvent;
|
|
resultscs: TCompressionstreamWithPositionSupport;
|
|
resultsms: TMemorystream;
|
|
procedure setFlushSize(size: integer);
|
|
protected
|
|
procedure initialize; override;
|
|
procedure flushresults; override;
|
|
procedure flushIfNeeded; override;
|
|
public
|
|
destructor destroy; override;
|
|
|
|
property OnFlushResults: TFlushResultsEvent read fOnFlushResults write fOnFlushResults;
|
|
property FlushSize: integer read fFlushSize write setFlushSize;
|
|
end;
|
|
|
|
TPointerscanWorkerLocal=class(TPointerscanWorker)
|
|
private
|
|
ffilename: string;
|
|
resultsfile: tfilestream;
|
|
resultsms: TMemorystream;
|
|
protected
|
|
procedure initialize; override;
|
|
procedure flushresults; override;
|
|
public
|
|
constructor create(suspended: boolean; filename: string);
|
|
destructor destroy; override;
|
|
|
|
property filename: string read ffilename;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses frmMemoryAllocHandlerUnit, pointerscancontroller;
|
|
|
|
//---------------Reversescanworker
|
|
|
|
procedure TPointerscanWorkerNetwork.initialize;
|
|
begin
|
|
// nothing for now
|
|
//fflushsize:=15*1024*1024;
|
|
|
|
resultsms:=tmemorystream.create;
|
|
resultscs:=TCompressionstreamWithPositionSupport.create(cldefault, resultsms);
|
|
|
|
results:=resultscs;
|
|
end;
|
|
|
|
procedure TPointerscanWorkerNetwork.setFlushSize(size: integer);
|
|
begin
|
|
fflushsize:=max(1024, min(size, 15*1024*1024)); //value between 1kb and 15mb
|
|
|
|
//debug:
|
|
//fflushsize:=0; //make it flush every time
|
|
end;
|
|
|
|
procedure TPointerscanWorkerNetwork.flushIfNeeded;
|
|
begin
|
|
if (resultscs.Position>fflushsize) or (resultsms.position>fflushsize) then
|
|
flushresults;
|
|
end;
|
|
|
|
procedure TPointerscanWorkerNetwork.flushresults;
|
|
var size: integer;
|
|
begin
|
|
if not haserror then
|
|
begin
|
|
currentwritestart:=gettickcount64;
|
|
isFlushing:=true;
|
|
|
|
|
|
size:=resultscs.Position;
|
|
|
|
resultscs.free;
|
|
resultsms.position:=0;
|
|
|
|
if assigned(fOnFlushResults) then
|
|
begin
|
|
while fOnFlushResults(size, resultsms)=false do
|
|
begin
|
|
if terminated then break;
|
|
sleep(10+random(500));
|
|
end;
|
|
end;
|
|
|
|
resultsms.position:=0;
|
|
resultscs:=TCompressionstreamWithPositionSupport.create(cldefault, resultsms);
|
|
|
|
results:=resultscs;
|
|
|
|
isFlushing:=false;
|
|
inc(timespentwriting, gettickcount64-currentwritestart);
|
|
end;
|
|
end;
|
|
|
|
destructor TPointerscanWorkerNetwork.destroy;
|
|
begin
|
|
if resultscs<>nil then
|
|
resultscs.free;
|
|
|
|
if resultsms<>nil then
|
|
resultsms.free;
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
//------------
|
|
|
|
procedure TPointerscanWorkerLocal.initialize;
|
|
begin
|
|
resultsms:=tmemorystream.Create;
|
|
resultsms.SetSize(16*1024*1024);
|
|
|
|
results:=resultsms;
|
|
|
|
|
|
|
|
if fileexists(filename) then
|
|
begin
|
|
//append to the end
|
|
resultsfile:=tfilestream.Create(filename,fmOpenWrite or fmShareDenyNone);
|
|
resultsfile.Seek(0, soEnd);
|
|
end
|
|
else
|
|
begin
|
|
//new file
|
|
resultsfile:= tfilestream.Create(filename,fmcreate);
|
|
resultsfile.free;
|
|
resultsfile:= tfilestream.Create(filename,fmOpenWrite or fmShareDenyNone);
|
|
end;
|
|
end;
|
|
|
|
procedure TPointerscanWorkerLocal.flushresults;
|
|
begin
|
|
if not haserror then
|
|
begin
|
|
currentwritestart:=gettickcount64;
|
|
isFlushing:=true;
|
|
|
|
resultsfile.WriteBuffer(resultsms.Memory^,resultsms.Position);
|
|
results.Seek(0,sofrombeginning);
|
|
|
|
isFlushing:=false;
|
|
inc(timespentwriting, gettickcount64-currentwritestart);
|
|
end;
|
|
end;
|
|
|
|
constructor TPointerscanWorkerLocal.create(suspended: boolean; filename: string);
|
|
begin
|
|
self.ffilename:=filename;
|
|
|
|
inherited create(suspended);
|
|
end;
|
|
|
|
destructor TPointerscanWorkerLocal.destroy;
|
|
begin
|
|
if resultsfile<>nil then
|
|
freeandnil(resultsfile);
|
|
|
|
if resultsms<>nil then
|
|
resultsms.free;
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
procedure TPointerscanWorker.SaveStateAndTerminate;
|
|
begin
|
|
savestate:=true;
|
|
Terminate;
|
|
end;
|
|
|
|
constructor TPointerscanWorker.create(suspended:boolean);
|
|
begin
|
|
isdone:=true;
|
|
|
|
pointersize:=processhandler.pointersize;
|
|
|
|
inherited create(suspended);
|
|
end;
|
|
|
|
destructor TPointerscanWorker.destroy;
|
|
begin
|
|
if compressedEntry<>nil then
|
|
FreeMem(compressedEntry);
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
|
|
procedure TPointerscanWorker.execute;
|
|
var
|
|
wr: dword;
|
|
i: integer;
|
|
begin
|
|
try
|
|
try
|
|
Initialize;
|
|
|
|
compressedEntrySize:=MaxBitCountModuleOffset+MaxBitCountModuleIndex+MaxBitCountLevel+MaxBitCountOffset*(maxlevel-mustendwithoffsetlistlength);
|
|
compressedEntrySize:=(compressedEntrySize+7) div 8;
|
|
|
|
getmem(compressedEntry, compressedEntrySize+4); //+4 so there's some space for overhead (writing using a dword pointer to the last byte)
|
|
|
|
MaskModuleIndex:=0;
|
|
for i:=1 to MaxBitCountModuleIndex do
|
|
MaskModuleIndex:=(MaskModuleIndex shl 1) or 1;
|
|
|
|
MaskLevel:=0;
|
|
for i:=1 to MaxBitCountLevel do
|
|
MaskLevel:=(MaskLevel shl 1) or 1;
|
|
|
|
MaskOffset:=0;
|
|
for i:=1 to MaxBitCountOffset do
|
|
MaskOffset:=(MaskOffset shl 1) or 1;
|
|
|
|
|
|
|
|
while (not terminated) do
|
|
begin
|
|
wr:=WaitForSingleObject(pathqueueSemaphore, 500); //obtain semaphore
|
|
|
|
if wr=WAIT_OBJECT_0 then
|
|
begin
|
|
if stop or terminated then
|
|
begin
|
|
ReleaseSemaphore(pathqueueSemaphore, 1, nil);
|
|
exit;
|
|
end;
|
|
|
|
|
|
//fetch the data from the queue and staticscanner
|
|
if outofdiskspace^ then
|
|
begin
|
|
ReleaseSemaphore(pathqueueSemaphore, 1, nil); //don't use it. give the semaphore back
|
|
sleep(2000);
|
|
continue;
|
|
end;
|
|
|
|
|
|
|
|
pathqueueCS.Enter;
|
|
if pathqueuelength^>0 then //should always be true due to the semaphore
|
|
begin
|
|
dec(pathqueuelength^);
|
|
i:=pathqueuelength^;
|
|
|
|
|
|
valuetofind:=pathqueue[i].valuetofind;
|
|
startlevel:=pathqueue[i].startlevel;
|
|
|
|
CopyMemory(@tempresults[0], @pathqueue[i].tempresults[0], maxlevel*sizeof(dword));
|
|
if noLoop then
|
|
CopyMemory(@valuelist[0], @pathqueue[i].valuelist[0], maxlevel*sizeof(ptruint));
|
|
end;
|
|
|
|
isdone:=false;
|
|
pathqueueCS.Leave;
|
|
|
|
|
|
try
|
|
rscan(valuetofind,startlevel);
|
|
finally
|
|
isdone:=true; //set isdone to true
|
|
end;
|
|
end;
|
|
|
|
if stop or terminated then exit;
|
|
end;
|
|
|
|
except
|
|
on e: exception do
|
|
begin
|
|
OutputDebugString('ScanWorker has error');
|
|
haserror:=true;
|
|
errorstring:='ReverseScanWorker:'+e.message;
|
|
|
|
//tell all siblings they should kill themself. There is no reason to live for them...
|
|
if assigned(fOnException) then
|
|
fOnException(self);
|
|
|
|
terminate;
|
|
end;
|
|
end;
|
|
finally
|
|
isdone:=true;
|
|
hasTerminated:=true;
|
|
|
|
flushresults;
|
|
|
|
OutputDebugString('Scanworker is done');
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TPointerscanWorker.DoRescan(level: valSint; moduleid: integer; offset: ptruint): boolean;
|
|
var
|
|
i,j: integer;
|
|
a: ptruint;
|
|
|
|
begin
|
|
result:=false;
|
|
for i:=0 to instantrescanlistcount-1 do
|
|
begin
|
|
a:=instantrescanlist[i].getAddressFromModuleIndexPlusOffset(moduleid, offset);
|
|
|
|
for j:=level downto 0 do
|
|
begin
|
|
a:=instantrescanlist[i].getPointer(a);
|
|
if a=0 then exit;
|
|
a:=a+tempresults[j];
|
|
end;
|
|
|
|
if a<>instantrescanaddress[i] then exit;
|
|
end;
|
|
|
|
result:=true;
|
|
end;
|
|
|
|
procedure TPointerscanWorker.StorePath(level: valSint; moduleid: integer; offset: ptruint);
|
|
{Store the current path to memory and flush if needed}
|
|
var
|
|
i: integer;
|
|
|
|
bd8, bm8: dword;
|
|
|
|
bit: integer;
|
|
begin
|
|
if instantrescan and (not DoRescan(level, moduleid, offset)) then exit;
|
|
|
|
//fill in the offset list
|
|
inc(pointersfound);
|
|
|
|
|
|
|
|
{
|
|
if databaseptr? then
|
|
begin
|
|
//table with last offsets
|
|
//table with secondary offsets
|
|
//...
|
|
//table with first offsets
|
|
|
|
//table with results, containing columns for every offset and the base
|
|
//moduleindex base offset1 offset2 offset3 offset4
|
|
//------------------------------------------------------
|
|
//0 1 reftooff1 reftooff2 3 4 5
|
|
end
|
|
else
|
|
}
|
|
if compressedptr then
|
|
begin
|
|
//leave the offset alone
|
|
//compress the module index
|
|
//compress the level
|
|
//compress the tempresults (additionally, if alligned, shift by 2)
|
|
|
|
|
|
//e.g: structsize 2048, maxlevel 5 , alligned, 100 modules in target
|
|
//offset: 32 bits
|
|
//module index(100) : 7 bits
|
|
//level(5): 3 bits
|
|
//tempresults(2048 alligned=512 , 9 bits/offset): 5*9=45
|
|
// total/entry: 32+7+3+45=87 bits. Align it to a byte boundary(88 bits)=11 bytes
|
|
|
|
|
|
//as opposed to:
|
|
//offset: 32 bits:
|
|
//module index: 32 bits
|
|
//level(5): 32
|
|
//tempresults: 5*32=160
|
|
//total/entry: 32+32+32+160=256 bits = 32 bytes
|
|
|
|
//so, the compressed version should be almost 3 times as small on a default scan (the shifting and alignment might cause a slightly slower scan)
|
|
|
|
if level<(mustendwithoffsetlistlength-1) then exit; //on a multi offset end scan, entries with a partial match resulting in a static are saved as well. Don't as they are not what the user wished, and would cause problems
|
|
|
|
|
|
bit:=0;
|
|
|
|
pqword(compressedEntry)^:=offset;
|
|
bit:=bit+MaxBitCountModuleOffset;
|
|
|
|
bd8:=bit shr 3; //bit div 8;
|
|
pdword(@compressedEntry[bd8])^:=moduleid;
|
|
bit:=bit+MaxBitCountModuleIndex;
|
|
|
|
|
|
bd8:=bit shr 3; //bit div 8;
|
|
bm8:=bit and $7; //bit mod 8;
|
|
|
|
pdword(@compressedEntry[bd8])^:=pdword(@compressedEntry[bd8])^ and (not (MaskLevel shl bm8)) or ((1+(level-mustendwithoffsetlistlength)) shl bm8);
|
|
bit:=bit+MaxBitCountLevel; //next section
|
|
|
|
|
|
|
|
//compress the offsets
|
|
for i:=mustendwithoffsetlistlength to level do
|
|
begin
|
|
bd8:=bit shr 3; //bit div 8;
|
|
bm8:=bit and $7; //bit mod 8;
|
|
|
|
if alligned then
|
|
pdword(@compressedEntry[bd8])^:=pdword(@compressedEntry[bd8])^ and (not (MaskOffset shl bm8)) or ((tempresults[i] shr 2) shl bm8)
|
|
else
|
|
pdword(@compressedEntry[bd8])^:=pdword(@compressedEntry[bd8])^ and (not (MaskOffset shl bm8)) or ((tempresults[i]) shl bm8);
|
|
|
|
bit:=bit+MaxBitCountOffset;
|
|
end;
|
|
|
|
results.WriteBuffer(compressedEntry^, compressedEntrySize);
|
|
|
|
end
|
|
else
|
|
begin
|
|
results.WriteDword(moduleid);
|
|
results.WriteQword(offset);
|
|
|
|
i:=level+1; //store how many offsets are actually used (since all are saved)
|
|
results.WriteDword(i);
|
|
results.WriteBuffer(tempresults[0], maxlevel*sizeof(tempresults[0]) );
|
|
end;
|
|
flushIfNeeded;
|
|
end;
|
|
|
|
procedure TPointerscanWorker.flushifneeded;
|
|
begin
|
|
//default behaviour. Override for smaller buffers
|
|
if results.position>15*1024*1024 then
|
|
flushresults;
|
|
end;
|
|
|
|
|
|
procedure TPointerscanWorker.rscan(valuetofind:ptrUint; level: valSint);
|
|
{
|
|
scan through the memory for a address that points in the region of address, if found, recursive call till level maxlevel
|
|
}
|
|
var p: ^byte;
|
|
pd: ^dword absolute p;
|
|
pq: ^qword absolute p;
|
|
|
|
|
|
i,j: valSint;
|
|
addedToQueue: boolean;
|
|
|
|
|
|
ExactOffset: boolean;
|
|
mae: TMemoryAllocEvent;
|
|
|
|
startvalue: ptrUint;
|
|
stopvalue: ptrUint;
|
|
plist: PPointerlist;
|
|
|
|
nostatic: TStaticData;
|
|
DontGoDeeper: boolean;
|
|
DifferentOffsetsInThisNode: integer;
|
|
locked: boolean;
|
|
|
|
begin
|
|
if (level>=maxlevel) or (terminated and (savestate=false)) then
|
|
exit;
|
|
|
|
|
|
currentlevel:=level;
|
|
DifferentOffsetsInThisNode:=0;
|
|
|
|
|
|
exactOffset:=mustEndWithSpecificOffset and (length(mustendwithoffsetlist)-1>=level);
|
|
|
|
if exactOffset then
|
|
begin
|
|
startvalue:=valuetofind-mustendwithoffsetlist[level];
|
|
stopvalue:=startvalue;
|
|
end
|
|
else
|
|
begin
|
|
startvalue:=valuetofind-structsize;
|
|
stopvalue:=valuetofind;
|
|
|
|
if useheapdata then
|
|
begin
|
|
mae:=frmMemoryAllocHandler.FindAddress(@frmMemoryAllocHandler.HeapBaselevel, valuetofind);
|
|
if mae<>nil then
|
|
begin
|
|
exactoffset:=true;
|
|
startvalue:=mae.BaseAddress;
|
|
stopvalue:=startvalue;
|
|
end
|
|
else //not static and not in heap
|
|
if useOnlyHeapData then
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
if noLoop then
|
|
begin
|
|
//check if this valuetofind is already in the list
|
|
for i:=0 to level-1 do
|
|
if valuelist[i]=valuetofind then
|
|
begin
|
|
exit;
|
|
end;
|
|
|
|
//add this valuetofind to the list
|
|
valuelist[level]:=valuetofind;
|
|
end;
|
|
|
|
//lastaddress:=maxaddress;
|
|
|
|
LookingForMin:=startvalue;
|
|
LookingForMax:=stopvalue;
|
|
|
|
dontGoDeeper:=false;
|
|
plist:=nil;
|
|
while stopvalue>=startvalue do
|
|
begin
|
|
if plist=nil then
|
|
plist:=pointerlisthandler.findPointerValue(startvalue, stopvalue);
|
|
|
|
if plist<>nil then
|
|
begin
|
|
tempresults[level]:=valuetofind-stopvalue; //store the offset
|
|
|
|
|
|
|
|
|
|
//go through the list of addresses that have this address(stopvalue) as their value
|
|
for j:=0 to plist.pos-1 do
|
|
begin
|
|
{$ifdef benchmarkps}
|
|
inc(pathsevaluated);
|
|
{$endif}
|
|
|
|
|
|
if (plist.list[j].staticdata=nil) then //this removes a lot of other possible paths. Perhaps a feature to remove this check ?
|
|
begin
|
|
if (not dontGoDeeper) then
|
|
begin
|
|
//check if we should go deeper into these results (not if max level has been reached)
|
|
|
|
|
|
if (level+1) < maxlevel then
|
|
begin
|
|
addedToQueue:=false;
|
|
|
|
if (not terminated) and (not outofdiskspace^) then //if there is not enough diskspace left wait till it's terminated, or diskspace is freed
|
|
begin
|
|
|
|
if (
|
|
(level+3<maxlevel) and
|
|
(
|
|
((pathqueuelength^<MAXQUEUESIZE - (MAXQUEUESIZE div 3))) or
|
|
((level<=2) and (pathqueuelength^<MAXQUEUESIZE - (MAXQUEUESIZE div 8))) or
|
|
((level<=1) and (pathqueuelength^<MAXQUEUESIZE - (MAXQUEUESIZE div 16))) or
|
|
((level=0) and (pathqueuelength^<MAXQUEUESIZE - 1))
|
|
)
|
|
)
|
|
or
|
|
(pathqueuelength^=0) //completely empty
|
|
then //there's room and not a crappy work item. Add it
|
|
begin
|
|
if (not Terminated) or savestate then
|
|
begin
|
|
//try to lock multiple times if high level pointers
|
|
locked:=pathqueueCS.tryEnter;
|
|
if not locked and (level<=2) then locked:=pathqueueCS.tryEnter;
|
|
if not locked and (level<=1) then
|
|
begin
|
|
//Two previous locks failed. Yield and try a lock again
|
|
sleep(0);
|
|
locked:=pathqueueCS.tryEnter;
|
|
if not locked then
|
|
begin
|
|
//one more time
|
|
sleep(0);
|
|
locked:=pathqueueCS.tryEnter;
|
|
end;
|
|
end;
|
|
|
|
if not locked and (level=0) then
|
|
begin
|
|
//I must have this lock
|
|
pathqueueCS.Enter;
|
|
locked:=true;
|
|
end;
|
|
|
|
|
|
if locked then
|
|
begin
|
|
if pathqueuelength^<MAXQUEUESIZE-1 then
|
|
begin
|
|
//still room
|
|
|
|
CopyMemory(@pathqueue[pathqueuelength^].tempresults[0], @tempresults[0], maxlevel*sizeof(dword));
|
|
if noLoop then
|
|
CopyMemory(@pathqueue[pathqueuelength^].valuelist[0], @valuelist[0], maxlevel*sizeof(ptruint));
|
|
|
|
pathqueue[pathqueuelength^].startlevel:=level+1;
|
|
pathqueue[pathqueuelength^].valuetofind:=plist.list[j].address;
|
|
|
|
inc(pathqueuelength^);
|
|
ReleaseSemaphore(pathqueueSemaphore, 1, nil);
|
|
addedToQueue:=true;
|
|
end;
|
|
pathqueueCS.Leave;
|
|
end;
|
|
end
|
|
else
|
|
exit;
|
|
end;
|
|
|
|
|
|
if not addedToQueue then
|
|
begin
|
|
//I'll have to do it myself
|
|
rscan(plist.list[j].address,level+1);
|
|
|
|
///done with this branch
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
//!!Out of diskspace or terminated!!
|
|
|
|
if outofdiskspace^ or savestate then //save to the overflowqueueu and return
|
|
begin
|
|
//fill in the temppathqueue and send it to the overflowqueuewriter
|
|
temppathqueue.startlevel:=level+1;
|
|
temppathqueue.valuetofind:=plist.list[j].address;
|
|
|
|
if length(temppathqueue.tempresults)<length(tempresults) then
|
|
setlength(temppathqueue.tempresults, length(tempresults));
|
|
|
|
CopyMemory(@temppathqueue.tempresults[0], @tempresults[0], maxlevel*sizeof(dword));
|
|
|
|
if length(temppathqueue.valuelist)<length(valuelist) then
|
|
setlength(temppathqueue.valuelist, length(valuelist));
|
|
|
|
CopyMemory(@temppathqueue.valuelist[0], @valuelist[0], maxlevel*sizeof(PtrUInt));
|
|
|
|
overflowqueuewriter(self, temppathqueue);
|
|
end
|
|
else
|
|
exit;//it was a terminate
|
|
|
|
//^^^^out of diskspace or save state!^^^^
|
|
|
|
end;
|
|
|
|
if (not staticonly) then //store this results entry
|
|
begin
|
|
nostatic.moduleindex:=$FFFFFFFF;
|
|
nostatic.offset:=plist.list[j].address;
|
|
StorePath(level,-1, plist.list[j].address);
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
//end of the line
|
|
if (not staticonly) then //store this results entry
|
|
begin
|
|
nostatic.moduleindex:=$FFFFFFFF;
|
|
nostatic.offset:=plist.list[j].address;
|
|
StorePath(level, -1, plist.list[j].address);
|
|
end;
|
|
end
|
|
|
|
end; //else don't go deeper
|
|
end
|
|
else
|
|
begin
|
|
//found a static one
|
|
StorePath(level, plist.list[j].staticdata.moduleindex, plist.list[j].staticdata.offset);
|
|
|
|
if onlyOneStaticInPath then DontGoDeeper:=true;
|
|
end;
|
|
end;
|
|
|
|
|
|
if LimitToMaxOffsetsPerNode then //check if the current itteration is less than maxOffsetsPerNode
|
|
begin
|
|
if level>0 then
|
|
inc(DifferentOffsetsInThisNode);
|
|
|
|
if (DifferentOffsetsInThisNode>=maxOffsetsPerNode) then
|
|
exit; //the max node has been reached
|
|
end;
|
|
|
|
|
|
plist:=plist.previous;
|
|
if plist<>nil then
|
|
stopvalue:=plist.pointervalue
|
|
else
|
|
exit; //nothing else to be found
|
|
|
|
end else
|
|
begin
|
|
{$ifdef benchmarkps}
|
|
inc(pathsevaluated);
|
|
{$endif}
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|