mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-02 23:23:48 +02:00
764 lines
18 KiB
ObjectPascal
764 lines
18 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source 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 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code 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. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Functions to parse GDB stacktraces, dismangle FPC identifiers and
|
|
find the stacktrace identifiers in Pascal sources.
|
|
|
|
ToDo:
|
|
- unit names with underscores
|
|
- unit names with points
|
|
- procs starting with underscore
|
|
}
|
|
unit CodetoolGDBTracer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, LazLoggerBase, KeywordFuncLists, contnrs;
|
|
|
|
type
|
|
TCTGDBMangledItemKind = (
|
|
cgmiNone,
|
|
cgmiUnknown, // '??'
|
|
cgmiProgram, // program, e.g. P$identifier
|
|
cgmiCompiler, // compiler function, like start, _start, PASCALMAIN
|
|
cgmiUnit, // unit, e.g. SYSTEM
|
|
cgmiStructure, // class, procedure
|
|
cgmiParameter, // function parameter name
|
|
cgmiParameterType, // function parameter type
|
|
cgmiResultType, // result type
|
|
cgmiError // syntax error
|
|
);
|
|
TCTGDBMangledItemTypes = set of TCTGDBMangledItemKind;
|
|
|
|
{ TCTGDBMangledItem }
|
|
|
|
TCTGDBMangledItem = class
|
|
public
|
|
Kind: TCTGDBMangledItemKind;
|
|
Identifier: string;
|
|
function AsString: string;
|
|
end;
|
|
|
|
TCTArrayOfGDBMangledItems = array of TCTGDBMangledItem;
|
|
|
|
|
|
{ TCTGDBTraceLine }
|
|
|
|
TCTGDBTraceLine = class
|
|
public
|
|
LineNumber: integer; // 1 based
|
|
Source: string;
|
|
Depth: integer;
|
|
MangledIdentifier: string;
|
|
GDBAddress: string; // e.g. '0x007489de'
|
|
GDBFilename: string;
|
|
GDBLine, GDBCol: integer;
|
|
MangledItems: TCTArrayOfGDBMangledItems;
|
|
Error: string;
|
|
ErrorCol: integer;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure WriteToStream(Indent: integer; s: TStream);
|
|
procedure Parse;
|
|
end;
|
|
|
|
{ TCTGDBTracer }
|
|
|
|
TCTGDBTracer = class
|
|
private
|
|
fLines: TObjectList; // list of TCTGDBTraceLine
|
|
FTraceText: string;
|
|
fCurP: PChar;
|
|
fLineStart: PChar;
|
|
fLineNumber: integer;
|
|
function GetCurrentLine: string;
|
|
procedure AddUnknownLine;
|
|
function GetLineCount: integer;
|
|
function GetLines(Index: integer): TCTGDBTraceLine;
|
|
procedure SetTraceText(AValue: string);
|
|
procedure SkipLine;
|
|
procedure TraceToLines;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
property TraceText: string read FTraceText write SetTraceText;
|
|
property LineCount: integer read GetLineCount;
|
|
property Lines[Index: integer]: TCTGDBTraceLine read GetLines;
|
|
function AsString: string;
|
|
procedure WriteToStream(s: TStream);
|
|
procedure ParseLines;
|
|
end;
|
|
|
|
procedure DemangleGDBIdentifier(Mangled: string;
|
|
var MangledItems: TCTArrayOfGDBMangledItems);
|
|
|
|
function dbgs(k: TCTGDBMangledItemKind): string; overload;
|
|
function dbgs(Mangles: TCTArrayOfGDBMangledItems): string; overload;
|
|
|
|
implementation
|
|
|
|
procedure DemangleGDBIdentifier(Mangled: string;
|
|
var MangledItems: TCTArrayOfGDBMangledItems);
|
|
{ examples:
|
|
fpc_raiseexception
|
|
SYSUTILS_RUNERRORTOEXCEPT$LONGINT$POINTER$POINTER
|
|
??
|
|
EXTTOOLEDITDLG_TEXTERNALTOOLMENUITEMS_$__LOAD$TCONFIGSTORAGE$$TMODALRESULT
|
|
PASCALMAIN
|
|
SYSTEM_FPC_SYSTEMMAIN$LONGINT$PPCHAR$PPCHAR
|
|
|
|
P$TESTSTACKTRACE1_TMAINCLASS_$_TSUBCLASS_$__RAISESOMETHING$ANSISTRING
|
|
start
|
|
}
|
|
|
|
procedure Add(Kind: TCTGDBMangledItemKind; const Identifier: string);
|
|
var
|
|
l: Integer;
|
|
Item: TCTGDBMangledItem;
|
|
begin
|
|
Item:=TCTGDBMangledItem.Create;
|
|
Item.Kind:=Kind;
|
|
Item.Identifier:=Identifier;
|
|
l:=length(MangledItems);
|
|
SetLength(MangledItems,l+1);
|
|
MangledItems[l]:=Item;
|
|
end;
|
|
|
|
var
|
|
p: PChar;
|
|
StartP: PChar;
|
|
|
|
function Extract: string;
|
|
begin
|
|
Result:=copy(Mangled,StartP-PChar(Mangled)+1,p-StartP);
|
|
end;
|
|
|
|
procedure ExpectedButFound(const Expected: string);
|
|
begin
|
|
Add(cgmiError,'expected '+Expected+', but found '
|
|
+dbgstr(p^)+' after "'+dbgstr(Mangled,1,p-PChar(Mangled))+'"');
|
|
end;
|
|
|
|
function ReadIdentifier: boolean;
|
|
begin
|
|
if not (p^ in ['a'..'z','A'..'Z']) then begin
|
|
ExpectedButFound('identifier');
|
|
exit(false);
|
|
end;
|
|
StartP:=p;
|
|
while p^ in ['a'..'z','A'..'Z'] do inc(p);
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
Identifier: String;
|
|
begin
|
|
SetLength(MangledItems,0);
|
|
if Mangled='' then exit;
|
|
p:=PChar(Mangled);
|
|
if p^='?' then begin
|
|
// for example: ??
|
|
Add(cgmiUnknown,Mangled);
|
|
exit;
|
|
end;
|
|
if (p^='P') and (p^='$') then begin
|
|
// program, for example: P$TESTSTACKTRACE1
|
|
inc(p,2);
|
|
StartP:=p;
|
|
while p^ in ['a'..'z','A'..'Z'] do inc(p);
|
|
Add(cgmiProgram,Extract);
|
|
end else if p^ in ['A'..'Z','a'..'z'] then begin
|
|
StartP:=p;
|
|
while p^ in ['a'..'z','A'..'Z'] do inc(p);
|
|
Identifier:=Extract;
|
|
if p^=#0 then begin
|
|
// Compiler function, e.g. start, _start, PASCALMAIN
|
|
Add(cgmiCompiler,Identifier);
|
|
exit;
|
|
end;
|
|
// unit, for example SYSTEM
|
|
Add(cgmiUnit,Identifier);
|
|
end else begin
|
|
// syntax error
|
|
Add(cgmiError,'unknown format');
|
|
exit;
|
|
end;
|
|
|
|
repeat
|
|
// read sub identifiers
|
|
if p^='_' then begin
|
|
inc(p);
|
|
if (p^='$') then begin
|
|
// for example _$_ or _$__
|
|
inc(p);
|
|
if p^<>'_' then begin
|
|
ExpectedButFound('_');
|
|
exit;
|
|
end;
|
|
inc(p);
|
|
if p^='_' then begin
|
|
// _$__
|
|
inc(p);
|
|
end;
|
|
end;
|
|
end else if p^='$' then begin
|
|
// parameters, p1$p2$p3
|
|
if MangledItems[length(MangledItems)-1].Kind<>cgmiStructure then begin
|
|
ExpectedButFound('_');
|
|
exit;
|
|
end;
|
|
repeat
|
|
inc(p);
|
|
// read parameter type
|
|
if not ReadIdentifier then exit;
|
|
Add(cgmiParameterType,Extract);
|
|
until (p^<>'$') or (not IsIdentStartChar[p[1]]);
|
|
if (p^='$') and (p[1]='$') then begin
|
|
// function result type
|
|
inc(p,2);
|
|
if not ReadIdentifier then exit;
|
|
Add(cgmiResultType,Extract);
|
|
end;
|
|
continue;
|
|
end else if p^=#0 then begin
|
|
exit;
|
|
end else begin
|
|
ExpectedButFound('_');
|
|
exit;
|
|
end;
|
|
|
|
if not ReadIdentifier then exit;
|
|
Add(cgmiStructure,Extract);
|
|
until false;
|
|
end;
|
|
|
|
function dbgs(k: TCTGDBMangledItemKind): string;
|
|
begin
|
|
case k of
|
|
cgmiNone: Result:='None';
|
|
cgmiUnknown: Result:='Unknown';
|
|
cgmiProgram: Result:='Program';
|
|
cgmiCompiler: Result:='Compiler';
|
|
cgmiUnit: Result:='Unit';
|
|
cgmiStructure: Result:='Structure';
|
|
cgmiParameter: Result:='Parameter';
|
|
cgmiParameterType: Result:='ParameterType';
|
|
cgmiResultType: Result:='ResultType';
|
|
cgmiError: Result:='Error';
|
|
else Result:='?';
|
|
end;
|
|
end;
|
|
|
|
function dbgs(Mangles: TCTArrayOfGDBMangledItems): string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:='[';
|
|
for i:=0 to length(Mangles)-1 do begin
|
|
if i>0 then Result+=',';
|
|
Result+=Mangles[i].AsString;
|
|
end;
|
|
Result+=']';
|
|
end;
|
|
|
|
{ TCTGDBMangledItem }
|
|
|
|
function TCTGDBMangledItem.AsString: string;
|
|
begin
|
|
Result:=dbgs(Kind)+':'+Identifier;
|
|
end;
|
|
|
|
{ TCTGDBTraceLine }
|
|
|
|
constructor TCTGDBTraceLine.Create;
|
|
begin
|
|
Depth:=-1;
|
|
end;
|
|
|
|
destructor TCTGDBTraceLine.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTGDBTraceLine.WriteToStream(Indent: integer; s: TStream);
|
|
|
|
procedure w(const h: string);
|
|
begin
|
|
if h='' then exit;
|
|
s.Write(h[1],length(h));
|
|
end;
|
|
|
|
begin
|
|
// source
|
|
w(Space(Indent));
|
|
w('Source={'+Source+'}');
|
|
w(LineEnding);
|
|
// error
|
|
if Error<>'' then begin
|
|
w(Space(Indent));
|
|
w('Error(');
|
|
w(IntToStr(LineNumber));
|
|
if ErrorCol>0 then begin
|
|
w(',');
|
|
w(IntToStr(ErrorCol));
|
|
end;
|
|
w('): '+Error);
|
|
w(LineEnding);
|
|
end;
|
|
if MangledIdentifier<>'' then begin
|
|
w(Space(Indent));
|
|
w('MangledIdentifier='+MangledIdentifier);
|
|
w(LineEnding);
|
|
if length(MangledItems)>0 then begin
|
|
w(Space(Indent));
|
|
w('Demangled='+dbgs(MangledItems));
|
|
w(LineEnding);
|
|
end;
|
|
end;
|
|
if GDBFilename<>'' then begin
|
|
w(Space(Indent));
|
|
w('GDB Source position='+GDBFilename+'('+IntToStr(GDBLine)+','+IntToStr(GDBCol)+')');
|
|
w(LineEnding);
|
|
end;
|
|
end;
|
|
|
|
procedure TCTGDBTraceLine.Parse;
|
|
{ Examples:
|
|
#0 0x00020e16 in fpc_raiseexception ()
|
|
#2 0x00024e48 in SYSTEM_HANDLEERRORADDRFRAME$LONGINT$POINTER$POINTER ()
|
|
#3 0xbffff548 in ?? ()
|
|
#9 0x00011124 in PASCALMAIN ()
|
|
|
|
#0 DOHANDLEMOUSEACTION (this=0x14afae00, ANACTIONLIST=0x14a96af8,ANINFO=...) at synedit.pp:3000
|
|
}
|
|
var
|
|
p: PChar;
|
|
i: Integer;
|
|
StartP: PChar;
|
|
Level: Integer;
|
|
|
|
procedure UnexpectedError(Expected: string);
|
|
begin
|
|
ErrorCol:=p-PChar(Source)+1;
|
|
Error:='expected ' +Expected+', but found '+DbgStr(p^);
|
|
end;
|
|
|
|
begin
|
|
Depth:=-1;
|
|
MangledIdentifier:='';
|
|
GDBAddress:='';
|
|
GDBFilename:='';
|
|
GDBLine:=0;
|
|
GDBCol:=0;
|
|
|
|
if Source='' then begin
|
|
Error:='Empty line';
|
|
exit;
|
|
end;
|
|
p:=PChar(Source);
|
|
|
|
// read #
|
|
while p^ in [' ',#9] do inc(p);
|
|
if p^<>'#' then begin
|
|
UnexpectedError('#');
|
|
exit;
|
|
end;
|
|
inc(p);
|
|
|
|
// read stack depth number (decimal)
|
|
if not (p^ in ['0'..'9']) then begin
|
|
UnexpectedError('decimal number');
|
|
exit;
|
|
end;
|
|
i:=0;
|
|
while p^ in ['0'..'9'] do begin
|
|
if i>100000 then begin
|
|
UnexpectedError('short decimal number');
|
|
exit;
|
|
end;
|
|
i:=i*10+ord(p^)-ord('0');
|
|
inc(p);
|
|
end;
|
|
Depth:=i;
|
|
|
|
// skip space
|
|
while p^ in [' ',#9] do inc(p);
|
|
|
|
if (p^='0') and (p[1]='x') then begin
|
|
// format: <hexnumber> in <mangledidentifier> ()
|
|
StartP:=p;
|
|
inc(p,2);
|
|
while IsHexNumberChar[p^] do inc(p);
|
|
GDBAddress:=copy(Source,StartP-PChar(Source)+1,p-StartP);
|
|
|
|
// skip space
|
|
while p^ in [' ',#9] do inc(p);
|
|
|
|
// read 'in'
|
|
if (p^<>'i') or (p[1]<>'n') then begin
|
|
UnexpectedError('in');
|
|
exit;
|
|
end;
|
|
inc(p,2);
|
|
|
|
// skip space
|
|
while p^ in [' ',#9] do inc(p);
|
|
end;
|
|
|
|
if (p^ in ['A'..'Z','a'..'z','_','?','$']) then begin
|
|
// format: <gdbidentifier> (<parameter list>) at gdbfilename:gdbline
|
|
// format: <mangledidentifier> ()
|
|
|
|
// read identifier
|
|
StartP:=p;
|
|
while p^ in ['a'..'z','A'..'Z','0'..'9','_','?','$'] do inc(p);
|
|
MangledIdentifier:=copy(Source,StartP-PChar(Source)+1,p-StartP);
|
|
|
|
// skip space
|
|
while p^ in [' ',#9] do inc(p);
|
|
|
|
if p^='(' then begin
|
|
// read parameters
|
|
Level:=0;
|
|
repeat
|
|
case p^ of
|
|
'(':
|
|
begin
|
|
inc(Level);
|
|
inc(p);
|
|
end;
|
|
')':
|
|
begin
|
|
inc(p);
|
|
dec(Level);
|
|
if Level=0 then break;
|
|
end;
|
|
#0:
|
|
exit;
|
|
else
|
|
inc(p);
|
|
end;
|
|
until false;
|
|
// skip space
|
|
while p^ in [' ',#9] do inc(p);
|
|
end;
|
|
|
|
// read 'at'
|
|
if (p^='a') and (p[1]='t') then begin
|
|
inc(p,2);
|
|
|
|
// skip space
|
|
while p^ in [' ',#9] do inc(p);
|
|
|
|
// read gdbfilename:gdbline
|
|
StartP:=p;
|
|
while not (p^ in [#0,':']) do inc(p);
|
|
GDBFilename:=copy(Source,StartP-PChar(Source)+1,p-StartP);
|
|
if p^=#0 then exit;
|
|
inc(p);
|
|
StartP:=p;
|
|
while p^ in ['0'..'9'] do inc(p);
|
|
GDBLine:=StrToIntDef(copy(Source,StartP-PChar(Source)+1,p-StartP),0);
|
|
end else if (p^='f') and (p[1]='r') and (p[2]='o') and (p[3]='m') then begin
|
|
// from <filename>
|
|
end else if p^=#0 then begin
|
|
// no source position => mangled
|
|
DemangleGDBIdentifier(MangledIdentifier,MangledItems);
|
|
end else begin
|
|
UnexpectedError('at');
|
|
exit;
|
|
end;
|
|
|
|
end else begin
|
|
// unknown format
|
|
UnexpectedError('hexnumber');
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
{ TCTGDBTracer }
|
|
|
|
procedure TCTGDBTracer.SetTraceText(AValue: string);
|
|
begin
|
|
if FTraceText=AValue then Exit;
|
|
Clear;
|
|
FTraceText:=AValue;
|
|
TraceToLines;
|
|
end;
|
|
|
|
function TCTGDBTracer.GetLineCount: integer;
|
|
begin
|
|
Result:=fLines.Count;
|
|
end;
|
|
|
|
function TCTGDBTracer.GetLines(Index: integer): TCTGDBTraceLine;
|
|
begin
|
|
Result:=TCTGDBTraceLine(fLines[Index]);
|
|
end;
|
|
|
|
procedure TCTGDBTracer.SkipLine;
|
|
var
|
|
c: Char;
|
|
begin
|
|
while not (fCurP^ in [#0,#10,#13]) do inc(fCurP);
|
|
repeat
|
|
c:=fCurP^;
|
|
if not (c in [#10,#13]) then break;
|
|
inc(fCurP);
|
|
inc(fLineNumber);
|
|
if (fCurP^ in [#10,#13]) and (c<>fCurP^) then
|
|
inc(fCurP);
|
|
until false;
|
|
fLineStart:=fCurP;
|
|
end;
|
|
|
|
function TCTGDBTracer.GetCurrentLine: string;
|
|
var
|
|
LineEnd: PChar;
|
|
begin
|
|
LineEnd:=fCurP;
|
|
while not (LineEnd^ in [#0,#10,#13]) do inc(LineEnd);
|
|
Result:=copy(FTraceText,fLineStart-PChar(FTraceText)+1,LineEnd-fLineStart);
|
|
end;
|
|
|
|
procedure TCTGDBTracer.AddUnknownLine;
|
|
var
|
|
Line: TCTGDBTraceLine;
|
|
begin
|
|
Line:=TCTGDBTraceLine.Create;
|
|
Line.LineNumber:=fLineNumber;
|
|
Line.Source:=GetCurrentLine;
|
|
Line.Error:='invalid format';
|
|
fLines.Add(Line);
|
|
SkipLine;
|
|
end;
|
|
|
|
procedure TCTGDBTracer.TraceToLines;
|
|
{ Example:
|
|
|
|
#0 0x00020e16 in fpc_raiseexception ()
|
|
#1 0x0004cb37 in SYSUTILS_RUNERRORTOEXCEPT$LONGINT$POINTER$POINTER ()
|
|
#3 0xbffff548 in ?? ()
|
|
#4 0x007489de in EXTTOOLEDITDLG_TEXTERNALTOOLMENUITEMS_$__LOAD$TCONFIGSTORAGE$$TMODALRESULT ()
|
|
#7 0x0007e620 in MAIN_TMAINIDE_$__LOADGLOBALOPTIONS ()
|
|
#9 0x00011124 in PASCALMAIN ()
|
|
#10 0x0002f416 in SYSTEM_FPC_SYSTEMMAIN$LONGINT$PPCHAR$PPCHAR ()
|
|
|
|
#0 0x0001136d in P$TESTSTACKTRACE1_TMAINCLASS_$_TSUBCLASS_$__RAISESOMETHING$ANSISTRING ()
|
|
#5 0x0001114a in start ()
|
|
|
|
~"#0 DOHANDLEMOUSEACTION (this=0x14afae00, ANACTIONLIST=0x14a96af8,
|
|
ANINFO=...) at synedit.pp:3000\n"
|
|
~"#1 0x00aea3e9 in FINDANDHANDLEMOUSEACTION (this=0x14afae00,
|
|
ABUTTON=MBLEFT, ASHIFT=..., X=233, Y=241, ACCOUNT=CCSINGLE, ADIR=CDDOWN,
|
|
ANAC
|
|
TIONRESULT=..., AWHEELDELTA=0) at synedit.pp:3307\n"
|
|
~"#3 0x005e083b in DOMOUSEDOWN (this=0x14afae00, MESSAGE=...,
|
|
BUTTON=MBLEFT, SHIFT=...) at include/control.inc:2135\n"
|
|
~"#5 0x0040d096 in DISPATCH (this=0xeebf6d4, MESSAGE=0) at
|
|
../inc/objpas.inc:592\n"
|
|
~"#8 0x00af3b76 in WNDPROC (this=0x14afae00, MSG=...) at synedit.pp:5740\n"
|
|
~"#11 0x7673fd72 in ?? () from C:\\Windows\\system32\\user32.dll\n"
|
|
~"#20 0x0040358f in main () at lazarus.pp:128\n"
|
|
|
|
}
|
|
|
|
procedure ReadLine;
|
|
var
|
|
StartP: PChar;
|
|
Line: String;
|
|
CopyStartP: PChar;
|
|
StartLineNumber: Integer;
|
|
NewLine: TCTGDBTraceLine;
|
|
c: Char;
|
|
|
|
procedure ConcatLine;
|
|
begin
|
|
if fCurP=CopyStartP then exit;
|
|
Line+=copy(FTraceText,CopyStartP-PChar(FTraceText)+1,fCurP-CopyStartP);
|
|
end;
|
|
|
|
begin
|
|
while (fCurP^ in [' ',#9]) do inc(fCurP);
|
|
if fCurP^='~' then begin
|
|
// quoted format ~"<line>"
|
|
inc(fCurP);
|
|
if fCurP^<>'"' then begin
|
|
AddUnknownLine;
|
|
exit;
|
|
end;
|
|
inc(fCurP);
|
|
StartP:=fCurP;
|
|
Line:='';
|
|
CopyStartP:=StartP;
|
|
StartLineNumber:=fLineNumber;
|
|
repeat
|
|
case fCurP^ of
|
|
#0:
|
|
begin
|
|
// missing closing quote
|
|
fCurP:=StartP;
|
|
fLineNumber:=StartLineNumber;
|
|
AddUnknownLine;
|
|
exit;
|
|
end;
|
|
#10,#13:
|
|
begin
|
|
ConcatLine;
|
|
repeat
|
|
c:=fCurP^;
|
|
if not (c in [#10,#13]) then break;
|
|
inc(fLineNumber);
|
|
inc(fCurP);
|
|
if (fCurP^ in [#10,#13]) and (c<>fCurP^) then
|
|
inc(fCurP);
|
|
until false;
|
|
CopyStartP:=fCurP;
|
|
end;
|
|
'\':
|
|
begin
|
|
ConcatLine;
|
|
inc(fCurP);
|
|
case fCurP^ of
|
|
'n': // ignore line breaks
|
|
inc(fCurP);
|
|
#0:
|
|
break;
|
|
else
|
|
Line+=fCurP^;
|
|
end;
|
|
CopyStartP:=fCurP;
|
|
end;
|
|
'"':
|
|
begin
|
|
ConcatLine;
|
|
inc(fCurP);
|
|
break;
|
|
end;
|
|
else
|
|
inc(fCurP);
|
|
end;
|
|
until false;
|
|
NewLine:=TCTGDBTraceLine.Create;
|
|
NewLine.LineNumber:=StartLineNumber;
|
|
NewLine.Source:=Line;
|
|
fLines.Add(NewLine);
|
|
// skip the rest of the line
|
|
SkipLine;
|
|
exit;
|
|
end else if fCurP^='#' then begin
|
|
// non quoted format
|
|
NewLine:=TCTGDBTraceLine.Create;
|
|
NewLine.LineNumber:=fLineNumber;
|
|
NewLine.Source:=GetCurrentLine;
|
|
fLines.Add(NewLine);
|
|
// skip the rest of the line
|
|
SkipLine;
|
|
exit;
|
|
end;
|
|
// unknown format
|
|
AddUnknownLine;
|
|
end;
|
|
|
|
begin
|
|
fLines.Clear;
|
|
if FTraceText='' then exit;
|
|
fLineNumber:=1;
|
|
fCurP:=PChar(FTraceText);
|
|
fLineStart:=fCurP;
|
|
while fCurP^<>#0 do
|
|
ReadLine;
|
|
end;
|
|
|
|
procedure TCTGDBTracer.ParseLines;
|
|
var
|
|
i: Integer;
|
|
Line: TCTGDBTraceLine;
|
|
begin
|
|
for i:=0 to LineCount-1 do begin
|
|
Line:=Lines[i];
|
|
if Line.Error<>'' then continue;
|
|
Line.Parse;
|
|
end;
|
|
end;
|
|
|
|
constructor TCTGDBTracer.Create;
|
|
begin
|
|
fLines:=TObjectList.Create(true);
|
|
end;
|
|
|
|
destructor TCTGDBTracer.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(fLines);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTGDBTracer.Clear;
|
|
begin
|
|
fLines.Clear;
|
|
end;
|
|
|
|
function TCTGDBTracer.AsString: string;
|
|
var
|
|
ms: TMemoryStream;
|
|
begin
|
|
ms:=TMemoryStream.Create;
|
|
try
|
|
WriteToStream(ms);
|
|
ms.Position:=0;
|
|
SetLength(Result,ms.Size);
|
|
if Result<>'' then
|
|
ms.Read(Result[1],length(Result));
|
|
finally
|
|
ms.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TCTGDBTracer.WriteToStream(s: TStream);
|
|
|
|
procedure w(const h: string);
|
|
begin
|
|
if h='' then exit;
|
|
s.Write(h[1],length(h));
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
Line: TCTGDBTraceLine;
|
|
begin
|
|
for i:=0 to LineCount-1 do begin
|
|
Line:=Lines[i];
|
|
w(IntToStr(i+1)+'/'+IntToStr(LineCount)+': ');
|
|
Line.WriteToStream(2,s);
|
|
w(LineEnding);
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|