Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
476 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (4)


Getting debug information runtimeFormat this article printer-friendly!Bookmark function is only available for registered users!
Product:
Delphi all versions
Category:
Component Writing
Skill Level:
Scoring:
Last Update:
04/13/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet debug map-file exception
Times Scored:
2
Visits:
3565
Uploader: Igor Kurilov
Company:
Reference: N/A
 
Question/Problem/Abstract:
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 FFileName<>Value 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 Value<>FActive 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 l>0 then
  begin
   n:= 1;

   ss:= '';
   while (n <= l) and (s[n]<>':') do
    begin
     ss:= ss + s[n];
     n:= n + 1;
    end;
   n:= n + 1;
   base:= StrToInt('$'+Trim(ss));

   ss:= '';
   while (n <=l) and (s[n]<>' ') do
    begin
     ss:= ss + s[n];
     n:= n + 1;
    end;
   n:= n + 1;
   off:= StrToInt('$'+Trim(ss));

   ss:= '';
   while (n <=l) do
    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 l>0 then
  begin
   n:= 1;
   while n <= l do
    begin
     // skip spaces
     while (n <= l) and (s[n]=' ') do n:=n+1;
     // scan line id
     ss:= '';
     while (n <= l) and (s[n]<>' ') do
      begin
       ss:= ss + s[n];
       n:= n + 1;
      end;
     line:= StrToInt(Trim(ss));
     n:= n + 1;

     ss:= '';
     while (n <=l) and (s[n]<>':') do
      begin
       ss:= ss + s[n];
       n:= n + 1;
      end;
     base:= StrToInt('$'+Trim(ss));
     n:= n + 1;

     ss:= '';
     while (n <= l) and (s[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 < LongInt(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.





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Example please
    John Mollll (Apr 16 2002 4:15PM)

Sounds good but Im not sure how to use it.
Can you give us an explaination please.
Thanks
J.
Respond

RE: Example please
Igor Kurilov (Apr 17 2002 6:47AM)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  xDebug, StdCtrls;

type
  TForm1 = class(TForm)
    xDebug1: TxDebug; // debug component
    Memo1: TMemo;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure DoOnException(Sender: TObject; E: Exception);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.DoOnException(Sender: TObject; E: Exception);
begin
// print debug information
xDebug1.Address:= ExceptAddr;
Memo1.Lines.Add(Format('Exception in unit %s line %d procedure name %s',
        [xDebug1.UnitName,
         xDebug1.LineNo,
         xDebug1.PublicName]));
// print "call stack"
xDebug1.CallStack_Init;
repeat
  Memo1.Lines.Add(xDebug1.PublicName);
until xDebug1.CallStack_Next;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
// load Map file
xDebug1.FileName:= 'project1.map';
xDebug1.Active:= True;
// install exception handler
Application.OnException:= DoOnException;
end;



procedure TForm1.Button1Click(Sender: TObject);
var
x: Extended;
begin
// raise exception
x:= sqrt(-1);
end;

end.

Respond

RE: RE: Example please
georgio schul (Jun 5 2002 2:28PM)

What is unit ContNrs.pas. You Have this unit?
Respond

RE: RE: RE: Example please
Igor Kurilov (Jun 5 2002 3:54PM)

This is standard unit (see \Borland\Delphi6\Source\Rtl\Common\), containing TObjectList class.
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
M. Shkolnik
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)