mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-05 18:12:39 +02:00
187 lines
4.6 KiB
ObjectPascal
187 lines
4.6 KiB
ObjectPascal
unit LazTracer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Laz_AVL_Tree,
|
|
// LazUtils
|
|
LazLoggerBase, LazUtilities, LazUtilsStrConsts;
|
|
|
|
type
|
|
TStackTracePointers = array of Pointer;
|
|
TLineInfoCacheItem = record
|
|
Addr: Pointer;
|
|
Info: string;
|
|
end;
|
|
PLineInfoCacheItem = ^TLineInfoCacheItem;
|
|
|
|
|
|
procedure RaiseGDBException(const Msg: string);
|
|
procedure RaiseAndCatchException;
|
|
function GetStackTrace(UseCache: boolean): string;
|
|
procedure GetStackTracePointers(var AStack: TStackTracePointers);
|
|
function StackTraceAsString(const AStack: TStackTracePointers;
|
|
UseCache: boolean): string;
|
|
function GetLineInfo(Addr: Pointer; UseCache: boolean): string;
|
|
|
|
|
|
implementation
|
|
|
|
var
|
|
LineInfoCache: TAvlTree = nil;
|
|
|
|
{------------------------------------------------------------------------------
|
|
procedure RaiseGDBException(const Msg: string);
|
|
|
|
Raises an exception.
|
|
Normally gdb does not catch fpc Exception objects, therefore this procedure
|
|
raises a standard "division by zero" exception which is catched by gdb.
|
|
This allows one to stop a program, without extra gdb configuration.
|
|
------------------------------------------------------------------------------}
|
|
procedure RaiseGDBException(const Msg: string);
|
|
begin
|
|
DebugLn(lrsERRORInCode, Msg);
|
|
// creates an exception, that gdb catches:
|
|
DebugLn(lrsCreatingGdbCatchableError);
|
|
DumpStack;
|
|
{$ifndef HASAMIGA} // On Amiga Division by 0 is not catchable, just crash
|
|
if (length(Msg) div (length(Msg) div 10000))=0 then ;
|
|
{$endif}
|
|
end;
|
|
|
|
procedure RaiseAndCatchException;
|
|
begin
|
|
try
|
|
{$ifndef HASAMIGA} // On Amiga Division by 0 is not catchable, just crash
|
|
if (length(lrsERRORInCode) div (length(lrsERRORInCode) div 10000))=0 then ;
|
|
{$else}
|
|
DumpStack;
|
|
{$endif}
|
|
except
|
|
end;
|
|
end;
|
|
|
|
function GetStackTrace(UseCache: boolean): string;
|
|
var
|
|
bp: Pointer;
|
|
addr: Pointer;
|
|
oldbp: Pointer;
|
|
CurAddress: Shortstring;
|
|
begin
|
|
Result:='';
|
|
{ retrieve backtrace info }
|
|
bp:=get_caller_frame(get_frame);
|
|
while bp<>nil do begin
|
|
addr:=get_caller_addr(bp);
|
|
CurAddress:=GetLineInfo(addr,UseCache);
|
|
//DebugLn('GetStackTrace ',CurAddress);
|
|
Result:=Result+CurAddress+LineEnding;
|
|
oldbp:=bp;
|
|
bp:=get_caller_frame(bp);
|
|
if (bp<=oldbp) or (bp>(StackBottom + StackLength)) then
|
|
bp:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure GetStackTracePointers(var AStack: TStackTracePointers);
|
|
var
|
|
Depth: Integer;
|
|
bp: Pointer;
|
|
oldbp: Pointer;
|
|
begin
|
|
// get stack depth
|
|
Depth:=0;
|
|
bp:=get_caller_frame(get_frame);
|
|
while bp<>nil do begin
|
|
inc(Depth);
|
|
oldbp:=bp;
|
|
bp:=get_caller_frame(bp);
|
|
if (bp<=oldbp) or (bp>(StackBottom + StackLength)) then
|
|
bp:=nil;
|
|
end;
|
|
SetLength(AStack,Depth);
|
|
if Depth>0 then begin
|
|
Depth:=0;
|
|
bp:=get_caller_frame(get_frame);
|
|
while bp<>nil do begin
|
|
AStack[Depth]:=get_caller_addr(bp);
|
|
inc(Depth);
|
|
oldbp:=bp;
|
|
bp:=get_caller_frame(bp);
|
|
if (bp<=oldbp) or (bp>(StackBottom + StackLength)) then
|
|
bp:=nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function StackTraceAsString(const AStack: TStackTracePointers;
|
|
UseCache: boolean): string;
|
|
var
|
|
i: Integer;
|
|
CurAddress: String;
|
|
begin
|
|
Result:='';
|
|
for i:=0 to length(AStack)-1 do begin
|
|
CurAddress:=GetLineInfo(AStack[i],UseCache);
|
|
Result:=Result+CurAddress+LineEnding;
|
|
end;
|
|
end;
|
|
|
|
function CompareLineInfoCacheItems(Data1, Data2: Pointer): integer;
|
|
begin
|
|
Result:=ComparePointers(PLineInfoCacheItem(Data1)^.Addr,
|
|
PLineInfoCacheItem(Data2)^.Addr);
|
|
end;
|
|
|
|
function CompareAddrWithLineInfoCacheItem(Addr, Item: Pointer): integer;
|
|
begin
|
|
Result:=ComparePointers(Addr,PLineInfoCacheItem(Item)^.Addr);
|
|
end;
|
|
|
|
function GetLineInfo(Addr: Pointer; UseCache: boolean): string;
|
|
var
|
|
ANode: TAvlTreeNode;
|
|
Item: PLineInfoCacheItem;
|
|
begin
|
|
if UseCache then begin
|
|
if LineInfoCache=nil then
|
|
LineInfoCache:=TAvlTree.Create(@CompareLineInfoCacheItems);
|
|
ANode:=LineInfoCache.FindKey(Addr,@CompareAddrWithLineInfoCacheItem);
|
|
if ANode=nil then begin
|
|
Result:=BackTraceStrFunc(Addr);
|
|
New(Item);
|
|
Item^.Addr:=Addr;
|
|
Item^.Info:=Result;
|
|
LineInfoCache.Add(Item);
|
|
end else begin
|
|
Result:=PLineInfoCacheItem(ANode.Data)^.Info;
|
|
end;
|
|
end else
|
|
Result:=BackTraceStrFunc(Addr);
|
|
end;
|
|
|
|
procedure FreeLineInfoCache;
|
|
var
|
|
ANode: TAvlTreeNode;
|
|
Item: PLineInfoCacheItem;
|
|
begin
|
|
if LineInfoCache=nil then exit;
|
|
ANode:=LineInfoCache.FindLowest;
|
|
while ANode<>nil do begin
|
|
Item:=PLineInfoCacheItem(ANode.Data);
|
|
Dispose(Item);
|
|
ANode:=LineInfoCache.FindSuccessor(ANode);
|
|
end;
|
|
LineInfoCache.Free;
|
|
LineInfoCache:=nil;
|
|
end;
|
|
|
|
|
|
finalization
|
|
FreeLineInfoCache;
|
|
|
|
end.
|
|
|