mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 04:57:59 +02:00
376 lines
11 KiB
ObjectPascal
376 lines
11 KiB
ObjectPascal
(* This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2, 3 or any later version
|
|
of the License (at your option).
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*)
|
|
unit LldbHelper;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, strutils, math,
|
|
// DebuggerIntf
|
|
DbgIntfBaseTypes;
|
|
|
|
function LastPos(ASearch, AString: string): Integer;
|
|
|
|
function StrStartsWith(AString, AStart: string; ACheckStartNotEmpty: Boolean = False): Boolean;
|
|
function StrContains(AString, AFind: string): Boolean;
|
|
function StrMatches(AString: string; const AFind: array of string): Boolean;
|
|
function StrMatches(AString: string; const AFind: array of string; out AGapsContent: TStringArray): Boolean;
|
|
|
|
function ParseThreadLocation(AnInput: String; out AnId: Integer;
|
|
out AnIsCurrent: Boolean; out AName: String; out AnAddr: TDBGPtr;
|
|
out AFuncName: String; out AnArgs: TStringList; out AFile: String;
|
|
out ALine: Integer; out AReminder: String): Boolean;
|
|
function ParseFrameLocation(AnInput: String; out AnId: Integer;
|
|
out AnIsCurrent: Boolean; out AnAddr: TDBGPtr; out AFuncName: String;
|
|
out AnArgs: TStringList; out AFile: String; out ALine: Integer;
|
|
out AReminder: String): Boolean;
|
|
function ParseNewFrameLocation(AnInput: String; out AnId: Integer;
|
|
out AnIsCurrent: Boolean; out AnAddr, AnStack, AnFrame: TDBGPtr; out AFuncName: String;
|
|
out AnArgs: TStringList; out AFile, AFullFile: String; out ALine: Integer;
|
|
out AReminder: String): Boolean;
|
|
function ParseNewThreadLocation(AnInput: String; out AnId: Integer;
|
|
out AnIsCurrent: Boolean; out AName: String; out AnAddr, AnStack, AnFrame: TDBGPtr;
|
|
out AFuncName: String; out AnArgs: TStringList; out AFile, AFullFile: String;
|
|
out ALine: Integer; out AReminder: String): Boolean;
|
|
|
|
function RemoveLineBreaks(const s: String): String;
|
|
|
|
implementation
|
|
|
|
function LastPos(ASearch, AString: string): Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := pos(ASearch, AString);
|
|
Result := i;
|
|
while i > 0 do begin
|
|
Result := i;
|
|
i := PosEx(ASearch, AString, i + 1);
|
|
end;
|
|
end;
|
|
|
|
function StrStartsWith(AString, AStart: string; ACheckStartNotEmpty: Boolean
|
|
): Boolean;
|
|
begin
|
|
Result := ( (not ACheckStartNotEmpty) or (AStart <> '') ) and (LeftStr(AString, Length(AStart)) = AStart);
|
|
end;
|
|
|
|
function StrContains(AString, AFind: string): Boolean;
|
|
begin
|
|
Result := pos(AFind, AString) > 0;
|
|
end;
|
|
|
|
function StrMatches(AString: string; const AFind: array of string): Boolean;
|
|
var
|
|
Dummy: TStringArray;
|
|
begin
|
|
Result := StrMatches(AString, AFind, Dummy);
|
|
end;
|
|
|
|
function StrMatches(AString: string; const AFind: array of string; out
|
|
AGapsContent: TStringArray): Boolean;
|
|
var
|
|
FindIdx, FindLen, j, ResIdx: Integer;
|
|
OpenEnd: Boolean;
|
|
begin
|
|
FindLen := Length(AFind);
|
|
if FindLen = 0 then begin
|
|
Result := False;
|
|
AGapsContent := nil;
|
|
exit;
|
|
end;
|
|
|
|
SetLength(AGapsContent, FindLen - 1);
|
|
Result := StrStartsWith(AString, AFind[0]);
|
|
if not Result then
|
|
exit;
|
|
Delete(AString, 1, Length(AFind[0]));
|
|
|
|
OpenEnd := AFind[FindLen - 1] = '';
|
|
if OpenEnd then
|
|
dec(FindLen);
|
|
|
|
FindIdx := 1;
|
|
ResIdx := 0;
|
|
while (FindIdx < FindLen) do begin
|
|
if AFind[FindIdx] = '' then begin
|
|
// empty string, match as far ahead as possible
|
|
inc(FindIdx);
|
|
j := LastPos(AFind[FindIdx], AString) - 1;
|
|
end
|
|
else
|
|
j := pos(AFind[FindIdx], AString) - 1;
|
|
Result := j >= 0;
|
|
if not Result then
|
|
exit;
|
|
AGapsContent[ResIdx] := copy(AString, 1, j);
|
|
Delete(AString, 1, j + Length(AFind[FindIdx]));
|
|
inc(FindIdx);
|
|
inc(ResIdx);
|
|
end;
|
|
if OpenEnd then begin
|
|
AGapsContent[ResIdx] := AString;
|
|
inc(ResIdx);
|
|
end
|
|
else
|
|
Result := AString = '';
|
|
SetLength(AGapsContent, ResIdx);
|
|
end;
|
|
|
|
(* Examples
|
|
"* thread #1: tid = 0x1a1c, 0x0042951d project1.exe`FORMCREATE(this=0x00151060, SENDER=0x00151060) at unit1.pas:59, stop reason = breakpoint 2.1"
|
|
" thread #2: tid = 0x16ac, 0x7700eb6c ntdll.dll`NtDelayExecution + 12"
|
|
|
|
" * frame #0: 0x7700eb6c ntdll.dll`NtDelayExecution + 12"
|
|
" frame #1: 0x767a5f5b KernelBase.dll`SleepEx + 155"
|
|
" frame #3: 0x004521c9 project1.exe"
|
|
" frame #7: 0x761a8654 kernel32.dll`BaseThreadInitThunk + 36"
|
|
" frame #2: 0x048fa158"
|
|
" frame #1: 0x0041ab6f project1.exe`DOCREATE(this=0x04a91060) at customform.inc:939"
|
|
" frame #5: 0x00402a42 project1.exe`main at project1.lpr:19"
|
|
*)
|
|
|
|
procedure ParseLocation(AnInput: String; out AnAddr: TDBGPtr; out AFuncName: String;
|
|
out AnArgs: TStringList; out AFile: String; out ALine: Integer; out AReminder: String);
|
|
var
|
|
found: TStringArray;
|
|
i, j, k: Integer;
|
|
begin
|
|
if pos(' ', AnInput) = 0 then begin
|
|
AnAddr := StrToInt64Def(AnInput, 0);
|
|
AnInput := '';
|
|
end
|
|
else
|
|
if StrMatches(AnInput, [''{addr}, ' '{remainder}, ''], found) then begin
|
|
AnAddr := StrToInt64Def(found[0], 0);
|
|
AnInput := found[1];
|
|
end
|
|
else
|
|
AnAddr := 0;
|
|
|
|
AnArgs := nil;
|
|
AFile := '';
|
|
ALine := 0;
|
|
AReminder := '';
|
|
if StrMatches(AnInput, [''{exe}, '`'{remainder}, ''], found) then begin
|
|
AnInput := found[1];
|
|
i := pos(' ', AnInput);
|
|
j := pos('(', AnInput);
|
|
k := pos(')', AnInput);
|
|
if ((i = 0) or (i > j)) and (j > 1) and (k > j) then begin
|
|
AFuncName := Copy(AnInput, 1, j-1);
|
|
AnArgs := TStringList.Create;
|
|
AnArgs.CommaText := copy(AnInput, j+1, k-j-1);
|
|
AnInput := Copy(AnInput, k+1, Length(AnInput));
|
|
end
|
|
else begin
|
|
i := Max(i, pos(', ', AnInput));
|
|
if i = 0 then i := Length(AnInput) + 1;
|
|
AFuncName := Copy(AnInput, 1, i-1);
|
|
AnInput := Copy(AnInput, i, Length(AnInput));
|
|
end;
|
|
|
|
if StrStartsWith(AnInput, ' + ') and (Length(AnInput) >= 4) and (AnInput[4] in ['0'..'9']) then begin
|
|
i := 4;
|
|
while (Length(AnInput) > i) and (AnInput[i+1] in ['0'..'9']) do inc(i);
|
|
delete(AnInput, 1, i);
|
|
end;
|
|
|
|
if StrMatches(AnInput, [' at ', ':', ''], found) then begin
|
|
AFile := found[0];
|
|
i := pos(', ', found[1]);
|
|
if i = 0 then i := Length(found[1]) + 1;
|
|
ALine := StrToIntDef(copy(found[1], 1, i-1), 0);
|
|
AReminder := copy(found[1], i, Length(found[1]));
|
|
end
|
|
else
|
|
AReminder := AnInput;
|
|
end
|
|
else begin
|
|
AFuncName := AnInput;
|
|
end;
|
|
end;
|
|
|
|
function ParseThreadLocation(AnInput: String; out AnId: Integer; out
|
|
AnIsCurrent: Boolean; out AName: String; out AnAddr: TDBGPtr; out
|
|
AFuncName: String; out AnArgs: TStringList; out AFile: String; out
|
|
ALine: Integer; out AReminder: String): Boolean;
|
|
var
|
|
found: TStringArray;
|
|
begin
|
|
Result := False;
|
|
AnIsCurrent := (Length(AnInput) > 1) and (AnInput[1] = '*');
|
|
if AnIsCurrent then AnInput[1] := ' ';
|
|
|
|
if not StrMatches(AnInput, [' thread #'{id}, ': '{}, ''], found) then begin
|
|
AnId := -1;
|
|
AName := '';
|
|
ParseLocation('', AnAddr, AFuncName, AnArgs, AFile, ALine, AReminder);
|
|
exit;
|
|
end;
|
|
|
|
AnId := StrToIntDef(found[0], -1);
|
|
AnInput := found[1];
|
|
Result := True;
|
|
|
|
if StrMatches(AnInput, ['tid = '{tid}, ', '{}, ''], found) then begin
|
|
AName := found[0];
|
|
AnInput := found[1];
|
|
end
|
|
else
|
|
AName := '';
|
|
|
|
ParseLocation(AnInput, AnAddr, AFuncName, AnArgs, AFile, ALine, AReminder);
|
|
end;
|
|
|
|
function ParseFrameLocation(AnInput: String; out AnId: Integer; out
|
|
AnIsCurrent: Boolean; out AnAddr: TDBGPtr; out AFuncName: String; out
|
|
AnArgs: TStringList; out AFile: String; out ALine: Integer; out
|
|
AReminder: String): Boolean;
|
|
var
|
|
found: TStringArray;
|
|
begin
|
|
Result := False;
|
|
AnIsCurrent := (Length(AnInput) > 3) and (AnInput[3] = '*');
|
|
if AnIsCurrent then AnInput[3] := ' ';
|
|
|
|
if not StrMatches(AnInput, [' frame #'{id}, ': '{}, ''], found) then begin
|
|
AnId := -1;
|
|
ParseLocation('', AnAddr, AFuncName, AnArgs, AFile, ALine, AReminder);
|
|
exit;
|
|
end;
|
|
|
|
AnId := StrToIntDef(found[0], -1);
|
|
AnInput := found[1];
|
|
Result := True;
|
|
|
|
ParseLocation(AnInput, AnAddr, AFuncName, AnArgs, AFile, ALine, AReminder);
|
|
end;
|
|
|
|
function ParseFrameOrThread(AnInput: String; out AnAddr, AnStack, AnFrame: TDBGPtr;
|
|
out AFuncName: String; out AnArgs: TStringList; out AFile, AFullFile: String;
|
|
out ALine: Integer; out AReminder: String): Boolean;
|
|
var
|
|
found: TStringArray;
|
|
i, j, k: SizeInt;
|
|
begin
|
|
Result := False;
|
|
|
|
if not StrMatches(AnInput, [''{addr}, ', ' {sp}, ', ' {fp},
|
|
' &&//FULL: '{fullfile}, ' &&//SHORT: '{file},' &&//LINE: '{line},
|
|
' &&//MOD: '{mod},' &&//FUNC: '{func}, '',' <<&&//FRAME', ''
|
|
], found) then begin
|
|
AnAddr := 0;
|
|
AFile := '';
|
|
AFullFile := '';
|
|
ALine := -1;
|
|
AFuncName := '';
|
|
AReminder := '';
|
|
AnArgs := nil;
|
|
exit;
|
|
end;
|
|
|
|
AnAddr := StrToInt64Def(found[0], 0);
|
|
AnStack := StrToInt64Def(found[1], 0);
|
|
AnFrame := StrToInt64Def(found[2], 0);
|
|
AFullFile := found[3];
|
|
AFile := found[4];
|
|
ALine := StrToIntDef(found[5], -1);
|
|
AFuncName := found[7];
|
|
AnArgs := nil;
|
|
AReminder := found[8];
|
|
|
|
if AFuncName = '' then begin
|
|
AFuncName := '<'+found[6]+'>';
|
|
end
|
|
else begin
|
|
AnInput := AFuncName;
|
|
i := pos(' ', AnInput);
|
|
j := pos('(', AnInput);
|
|
k := pos(')', AnInput);
|
|
if ((i = 0) or (i > j)) and (j > 1) and (k > j) then begin
|
|
AFuncName := Copy(AnInput, 1, j-1);
|
|
AnArgs := TStringList.Create;
|
|
AnArgs.CommaText := copy(AnInput, j+1, k-j-1);
|
|
AnInput := Copy(AnInput, k+1, Length(AnInput));
|
|
end;
|
|
end;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
function ParseNewFrameLocation(AnInput: String; out AnId: Integer; out
|
|
AnIsCurrent: Boolean; out AnAddr, AnStack, AnFrame: TDBGPtr; out
|
|
AFuncName: String; out AnArgs: TStringList; out AFile, AFullFile: String; out
|
|
ALine: Integer; out AReminder: String): Boolean;
|
|
var
|
|
found: TStringArray;
|
|
begin
|
|
AnIsCurrent := (Length(AnInput) > 3) and (AnInput[3] = '*');
|
|
if AnIsCurrent then AnInput[3] := ' ';
|
|
|
|
if StrMatches(AnInput, [' frame #'{id}, ': '{}, ''], found) then begin
|
|
AnId := StrToIntDef(found[0], -1);
|
|
AnInput := found[1];
|
|
end
|
|
else begin
|
|
AnId := -1;
|
|
AnInput := '';
|
|
end;
|
|
|
|
Result := ParseFrameOrThread(AnInput, AnAddr, AnStack, AnFrame,
|
|
AFuncName, AnArgs, AFile, AFullFile, ALine, AReminder);
|
|
end;
|
|
|
|
function ParseNewThreadLocation(AnInput: String; out AnId: Integer; out
|
|
AnIsCurrent: Boolean; out AName: String; out AnAddr, AnStack,
|
|
AnFrame: TDBGPtr; out AFuncName: String; out AnArgs: TStringList; out AFile,
|
|
AFullFile: String; out ALine: Integer; out AReminder: String): Boolean;
|
|
var
|
|
found: TStringArray;
|
|
begin
|
|
AnIsCurrent := (Length(AnInput) > 1) and (AnInput[1] = '*');
|
|
if AnIsCurrent then AnInput[1] := ' ';
|
|
|
|
if StrMatches(AnInput, [' thread #'{id}, ': tid='{tid}, ': '{}, ''], found) then begin
|
|
AnId := StrToIntDef(found[0], -1);
|
|
AName := found[1];
|
|
AnInput := found[2];
|
|
end
|
|
else begin
|
|
AnId := -1;
|
|
AName := '';
|
|
AnInput := '';
|
|
end;
|
|
|
|
Result := ParseFrameOrThread(AnInput, AnAddr, AnStack, AnFrame,
|
|
AFuncName, AnArgs, AFile, AFullFile, ALine, AReminder);
|
|
end;
|
|
|
|
function RemoveLineBreaks(const s: String): String;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := s;
|
|
UniqueString(Result);
|
|
for i := 1 to Length(Result) do
|
|
if Result[i] in [#0..#31] then
|
|
pchar(Result)[i-1] := ' ';
|
|
end;
|
|
|
|
end.
|
|
|