Mega Code Archive

 
Categories / Delphi / Forms
 

Getting debug information runtime

Title: Getting debug information runtime Question: Converting exception address into source line number and function public name using Map-file Answer: unit xDebug; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, contnrs; type TPtrDef = class public Offset: LongInt; Base: LongInt; function Addr: LongInt; end; TPublicDef = class(TPtrDef) public PublicName: String; end; TLineDef = class(TPtrDef) public UnitName: String; LineNo: Integer; end; _StackRec = record LastEBP: Pointer; CallerAddr: Pointer; end; TxDebug = class(TComponent) private { Private declarations } FStackRec: _StackRec; FFileName: TFileName; FActive: Boolean; { error defenition } FAddress: Pointer; FUnitName: String; FLineNo: Integer; FPublicName: String; procedure SetFileName(const Value: TFileName); procedure SetActive(Value: Boolean); procedure SetAddress(Value: Pointer); protected { Protected declarations } FPublics: TObjectList; FLines: TObjectList; procedure ClearMap; virtual; procedure LoadMap; virtual; procedure LoadPublics(var F: TextFile); virtual; procedure LoadLines(var F: TextFile); virtual; procedure ParsePublic(const S: String); virtual; procedure ParseLine(const S, UnitName: String); virtual; function SearchPtr(Addr: Pointer; FList: TObjectList): TPtrDef; virtual; public { Public declarations } constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure CallStack_Init; function CallStack_Next: Boolean; { PROPERTIES } property UnitName: String read FUnitName; property PublicName: String read FPublicName; property LineNo: Integer read FLineNo; property Address: Pointer read FAddress write SetAddress; published { Published declarations } { PROPERTIES } property FileName: TFileName read FFileName write SetFileName; property Active: Boolean read FActive write SetActive; end; procedure Register; implementation procedure Register; begin RegisterComponents('Legionary', [TxDebug]); end; //////////////////////////////////////////////////////////////////////////////// // SortAddr function SortAddr(Item1, Item2: Pointer): Integer; begin Result:= TPtrDef(Item1).Addr - TPtrDef(Item2).Addr; end; //////////////////////////////////////////////////////////////////////////////// // TPtrDef function TPtrDef.Addr; begin Result:= $00400000 + $1000*Base + Offset; end; //////////////////////////////////////////////////////////////////////////////// // TxDebug constructor TxDebug.Create; begin inherited; FPublics:= TObjectList.Create(True); FLines:= TObjectList.Create(True); FActive:= false; FFileName:= ''; FAddress:= nil; FPublicName:= ''; FUnitName:= ''; FLineNo:= 0; end; destructor TxDebug.Destroy; begin FPublics.Free; FLines.Free; inherited; end; procedure TxDebug.SetFileName; begin Assert(not FActive); if FFileNameValue then begin FFileName:= Value; ClearMap; end; end; procedure TxDebug.ClearMap; begin FPublics.Clear; FLines.Clear; end; procedure TxDebug.LoadMap; var F: TextFile; begin AssignFile(F, FFileName); Reset(F); try LoadPublics(F); Reset(F); LoadLines(F); finally CloseFile(F); end; end; procedure TxDebug.SetActive; begin if ValueFActive then begin if Value then begin LoadMap; FActive:= True; end else begin ClearMap; FActive:= False; end; end; end; procedure TxDebug.LoadPublics; const cStrID = 'Address Publics by Name'; var S: String; begin while not Eof(F) do begin ReadLn(F, s); if Trim(S)=cStrID then Break; end; if not Eof(F) then Readln(F, S); // load publics while not Eof(F) do begin ReadLn(F, S); if Trim(S)='' then Break; ParsePublic(S); end; FPublics.Sort(@SortAddr); end; procedure TxDebug.LoadLines; const cStrID = 'Line numbers for'; var S, SS: String; begin // Address Publics by Name while not Eof(F) do begin while not Eof(F) do begin ReadLn(F, S); if Copy(S, 1, Length(cStrID))=cStrID then Break; end; SS:= Copy(S, Length(cStrID) + 1, Pos('(', S) - length(cStrID) - 1); if not Eof(F) then Readln(F, S); // load publics while not Eof(F) do begin ReadLn(F, S); if Trim(S)='' then Break; ParseLine(S, SS); end; end; FLines.Sort(@SortAddr); end; procedure TxDebug.ParsePublic; var n, l: Integer; base, off: LongInt; cap, ss: String; o: TPublicDef; begin l:= Length(s); if l0 then begin n:= 1; ss:= ''; while (n ':') do begin ss:= ss + s[n]; n:= n + 1; end; n:= n + 1; base:= StrToInt('$'+Trim(ss)); ss:= ''; while (n ' ') do begin ss:= ss + s[n]; n:= n + 1; end; n:= n + 1; off:= StrToInt('$'+Trim(ss)); ss:= ''; while (n begin ss:= ss + s[n]; n:= n + 1; end; cap:= Trim(ss); // finally insert object o:= TPublicDef.Create; o.PublicName:= cap; o.Offset:= off; o.Base:= base; FPublics.Add(o); end; end; procedure TxDebug.ParseLine; var n, l: Integer; ss: String; off, base, line: LongInt; o: TLineDef; begin l:= Length(s); if l0 then begin n:= 1; while n begin // skip spaces while (n // scan line id ss:= ''; while (n ' ') do begin ss:= ss + s[n]; n:= n + 1; end; line:= StrToInt(Trim(ss)); n:= n + 1; ss:= ''; while (n ':') do begin ss:= ss + s[n]; n:= n + 1; end; base:= StrToInt('$'+Trim(ss)); n:= n + 1; ss:= ''; while (n ' ') do begin ss:= ss + s[n]; n:= n + 1; end; off:= StrToInt('$'+Trim(ss)); n:= n + 1; // add object o:= TLineDef.Create; o.Base:= base; o.Offset:= off; o.UnitName:= UnitName; o.LineNo:= line; FLines.Add(o); end; end; end; function TxDebug.SearchPtr; var n, nn: Integer; o: TPtrDef; b: Boolean; begin Result:= nil; b:=false; nn:=-1; for n:=0 to FList.Count-1 do begin o:= TPtrDef(FList.Items[n]); if o.Addr=LongInt(Addr) then begin nn:= n; Break; end; if b and (o.Addr LongInt(Addr)) then begin nn:= n - 1; Break; end; b:= o.Addr end; if nn-1 then Result:= TPtrDef(FList.Items[nn]); end; procedure TxDebug.SetAddress; var pub: TPublicDef; line: TLineDef; begin Assert(FActive); FAddress:= Value; pub:= TPublicDef(SearchPtr(FAddress, FPublics)); line:= TLineDef(SearchPtr(FAddress, FLines)); if Assigned(pub) then FPublicName:= pub.PublicName; if Assigned(line) then begin FUnitName:= line.UnitName; FLineNo:= line.LineNo; end; end; procedure TxDebug.CallStack_Init; var rec: ^_StackRec; begin rec:= @FStackRec; asm push ebx mov eax, rec mov ebx, ss:[ebp] // prior_ebp mov [eax], ebx mov ebx, ss:[ebp+4] // caller_addr mov [eax+4], ebx pop ebx end; SetAddress(FStackRec.CallerAddr); end; function TxDebug.CallStack_Next; var rec: ^_StackRec; begin rec:= @FStackRec; asm push ebx push ebp mov eax, rec mov ebx, [eax] // prior_ebp mov ebp, ebx // save prior_ebp in ebp mov ebx, ss:[ebp] // prior_ebp mov [eax], ebx mov ebx, ss:[ebp+4] // caller_addr mov [eax+4], ebx pop ebp pop ebx end; SetAddress(FStackRec.CallerAddr); Result:= CompareText(PublicName, 'TlsLast')=0; end; end.