Tavirgil
asked on
Debugging with MAP file - finding instruction address for a AV (advanced)
Question:
How can I find the instruction line in the program from the AV instruction address using the MAP file.
Introduction:
I am using Delphi 7, however I don't think it's relevant here.
I have created a small test project only for debug purposes that triggers a Access Violation error. From this error I would like to use the instruction address provided by the application error and the corresponding MAP file for the application to find the source code line generating the error.
It's basically one main form with a button that when pressed it tries to write to a pointer which was not allocated -> access violation.
I would also like to get the name of variable which was not initialized.
Source Code:
************************** **(start source code)********************* ***
unit Test;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
end;
TDummyType = record
sBuffer: string;
iLength: integer;
end;
PTDummyType = ^TDummyType;
var
Form1: TForm1;
implementation
procedure TForm1.Button1Click(Sender : TObject);
var
PTest: PTDummyType;
begin
PTest.sBuffer := 'string'; // line that gives the Acess Violation
if (PTest.sBuffer = 'string') then
ShowMessage('cool');
end;
end.
************************** **(end source code)********************* ***
The MAP file looks like this:
************************** **(start map file )************************
Start Length Name Class
0001:00000000 00051C44H .text CODE
0002:00000000 00001194H .data DATA
0002:00001194 00000C05H .bss BSS
..........................
Line numbers for Test(Test.pas) segment .text
36 0001:00051974 37 0001:00051975 38 0001:00051981 39 0001:0005198F
40 0001:00051999 42 0001:000519EC 42 0001:000519F3
Line numbers for Project2(D:\Development\Pr oject2.dpr ) segment .text
9 0001:00051BFC 10 0001:00051C0C 11 0001:00051C18 12 0001:00051C30
13 0001:00051C3C
.................
Program entry point at 0001:00051BFC
************************** **(end map file )************************
Details:
The line {PTest.sBuffer := 'string' } generates an: Access Violation at address 004042A4 in module Project2.exe. Write of address 0042A820.
But this source code line in the MAP file has the address 0001:00051975 at line 37.
Pausing the program and using Search - Find Error with the 004042A4 address indeed gives me the address where the program breaks but that is not attached to a line in the source code.
I want to be able to find the instruction address corresponding to my source code in the map file that was the closest one next to the assembly instruction that generated the AV.
That is lets assume that I can't debug the source code (for example the AV appears on the customer's computer and I can't replicate it in the development environment). From the MAP file and AV instruction address how can I find the line number responsible for the error and maybe the name of the variable not initilized???
Long time ago I had some basic assembly knowlegde but now I can't find a specific guide on get to my instruction address in the MAP file.
Please don't point me to third party tools. I want the low level solution where you need to manually compute offsets and stuff like that.
A good online resource for such a guide would also be accepted as an aswer.
Thank you
How can I find the instruction line in the program from the AV instruction address using the MAP file.
Introduction:
I am using Delphi 7, however I don't think it's relevant here.
I have created a small test project only for debug purposes that triggers a Access Violation error. From this error I would like to use the instruction address provided by the application error and the corresponding MAP file for the application to find the source code line generating the error.
It's basically one main form with a button that when pressed it tries to write to a pointer which was not allocated -> access violation.
I would also like to get the name of variable which was not initialized.
Source Code:
**************************
unit Test;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
end;
TDummyType = record
sBuffer: string;
iLength: integer;
end;
PTDummyType = ^TDummyType;
var
Form1: TForm1;
implementation
procedure TForm1.Button1Click(Sender
var
PTest: PTDummyType;
begin
PTest.sBuffer := 'string'; // line that gives the Acess Violation
if (PTest.sBuffer = 'string') then
ShowMessage('cool');
end;
end.
**************************
The MAP file looks like this:
**************************
Start Length Name Class
0001:00000000 00051C44H .text CODE
0002:00000000 00001194H .data DATA
0002:00001194 00000C05H .bss BSS
..........................
Line numbers for Test(Test.pas) segment .text
36 0001:00051974 37 0001:00051975 38 0001:00051981 39 0001:0005198F
40 0001:00051999 42 0001:000519EC 42 0001:000519F3
Line numbers for Project2(D:\Development\Pr
9 0001:00051BFC 10 0001:00051C0C 11 0001:00051C18 12 0001:00051C30
13 0001:00051C3C
.................
Program entry point at 0001:00051BFC
**************************
Details:
The line {PTest.sBuffer := 'string' } generates an: Access Violation at address 004042A4 in module Project2.exe. Write of address 0042A820.
But this source code line in the MAP file has the address 0001:00051975 at line 37.
Pausing the program and using Search - Find Error with the 004042A4 address indeed gives me the address where the program breaks but that is not attached to a line in the source code.
I want to be able to find the instruction address corresponding to my source code in the map file that was the closest one next to the assembly instruction that generated the AV.
That is lets assume that I can't debug the source code (for example the AV appears on the customer's computer and I can't replicate it in the development environment). From the MAP file and AV instruction address how can I find the line number responsible for the error and maybe the name of the variable not initilized???
Long time ago I had some basic assembly knowlegde but now I can't find a specific guide on get to my instruction address in the MAP file.
Please don't point me to third party tools. I want the low level solution where you need to manually compute offsets and stuff like that.
A good online resource for such a guide would also be accepted as an aswer.
Thank you
http://www.madshi.net
Madshi knows all about it, but i am not sure if he has explanations online.
Madshi knows all about it, but i am not sure if he has explanations online.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Always wondered where that $1000 came from :-)
I might be able to dig out my old stack trace code if it's interesting, if Madshi doesn't publish his source :-)
I might be able to dig out my old stack trace code if it's interesting, if Madshi doesn't publish his source :-)
>> if Madshi doesn't publish his source :-)
Nah!
Btw, the Delphi Jedi JCL has a full stack tracing logic. It's free and comes with full sources. It might not be the best available solution (especially not when being compared with madExcept :-), but it does do its job quite well.
Nah!
Btw, the Delphi Jedi JCL has a full stack tracing logic. It's free and comes with full sources. It might not be the best available solution (especially not when being compared with madExcept :-), but it does do its job quite well.
ASKER
I think I got I know.
So in my case, with the instruction address from the CPU window (for the "PTest.sBuffer := 'string'" line):
$00452975 - $400000 (image base) - $1000 (base code address) = $00051975 wich is exactly the address in the MAP file.
However the AV error gives me an instruction address from System.@LStrAsg function. Only by having a snapshot of the current stack when the AV occurs I can track from which function and at what point in that fucntion the call occured.
Am I on the right track here ?
Ok so let's just say that I create an application, send it to a customer which in turn gets an AV error. There is not way for me to track where the error occured only by the AV report and the MAP file.
I would need some sort of debug units (like madshi exception) compiled with my application in order to also get a snapshot of the stack which then can lead me to the bad instruction in the code.
So I definetly DO NEED a third party tool / units.
So in my case, with the instruction address from the CPU window (for the "PTest.sBuffer := 'string'" line):
$00452975 - $400000 (image base) - $1000 (base code address) = $00051975 wich is exactly the address in the MAP file.
However the AV error gives me an instruction address from System.@LStrAsg function. Only by having a snapshot of the current stack when the AV occurs I can track from which function and at what point in that fucntion the call occured.
Am I on the right track here ?
Ok so let's just say that I create an application, send it to a customer which in turn gets an AV error. There is not way for me to track where the error occured only by the AV report and the MAP file.
I would need some sort of debug units (like madshi exception) compiled with my application in order to also get a snapshot of the stack which then can lead me to the bad instruction in the code.
So I definetly DO NEED a third party tool / units.
Correct - you need to log both the AV address and the call stack. Then you use all this to look stuff up in the map file.
(And you probably need to compile with stack frames and no optimisation, otherwise you'll miss bits of the stack trace)
(And you probably need to compile with stack frames and no optimisation, otherwise you'll miss bits of the stack trace)
> Am I on the right track here ?
Yes.
However, sometimes having the access violation address *is* enough to show you the crashing source code line. E.g. if you don't do a string assignment, but an integer assignment, things will work out without needing a stack trace. But it's simply not reliably this way.
> So I definetly DO NEED a third party tool / units.
Well, you can do it on your own, too, of course. But why should you do that? It would cost you hundreds of hours to implement such a solution. Instead you should probably choose between the available 3rd party tools like JCL (free incl sources) and madExcept (free for non-commercial usage only, commercial usage costs USD 50, commercial version contains sources).
> And you probably need to compile with stack frames and no optimisation, otherwise you'll miss bits of the stack trace
When using madExcept you can leave all settings as they are. No stack frames needed. Optimization doesn't harm. Not sure what JCL needs, though.
Yes.
However, sometimes having the access violation address *is* enough to show you the crashing source code line. E.g. if you don't do a string assignment, but an integer assignment, things will work out without needing a stack trace. But it's simply not reliably this way.
> So I definetly DO NEED a third party tool / units.
Well, you can do it on your own, too, of course. But why should you do that? It would cost you hundreds of hours to implement such a solution. Instead you should probably choose between the available 3rd party tools like JCL (free incl sources) and madExcept (free for non-commercial usage only, commercial usage costs USD 50, commercial version contains sources).
> And you probably need to compile with stack frames and no optimisation, otherwise you'll miss bits of the stack trace
When using madExcept you can leave all settings as they are. No stack frames needed. Optimization doesn't harm. Not sure what JCL needs, though.
ASKER
Got in guys.
I will keep in mind madExcept and JCL.
Much appreciate your help. I would like to split the points to both of you because both of you helped me out. I hope this does not upset neither of you guys.
Thanks again for your time and help.
I will keep in mind madExcept and JCL.
Much appreciate your help. I would like to split the points to both of you because both of you helped me out. I hope this does not upset neither of you guys.
Thanks again for your time and help.
Madshi, do you know of bugs in the JCL code? If so then please report them.
Hi Robert!
I'm not aware of any bugs in JCL. I said that it's not the best solution, because it's missing some things which I find quite important for an exception tracking component. E.g. JCL doesn't catch initialization and finalization exceptions. It also doesn't (automatically) catch thread exceptions. Also it doesn't contain a thread safe exception box. And there are some more details (some more important, some less important) that are missing. But it does catch "normal" exceptions. So it definately is a useful solution. It's just not as good as madExcept... :-)
This is all "as far as I know". Am I wrong anywhere?
P.S: One thing just comes to my mind. The JCL's TD32 debug info parsing is incomplete. It misses some BCB function/procedure names. You can tell the responsible programmer to contact me. I'm willing to share this information.
I'm not aware of any bugs in JCL. I said that it's not the best solution, because it's missing some things which I find quite important for an exception tracking component. E.g. JCL doesn't catch initialization and finalization exceptions. It also doesn't (automatically) catch thread exceptions. Also it doesn't contain a thread safe exception box. And there are some more details (some more important, some less important) that are missing. But it does catch "normal" exceptions. So it definately is a useful solution. It's just not as good as madExcept... :-)
This is all "as far as I know". Am I wrong anywhere?
P.S: One thing just comes to my mind. The JCL's TD32 debug info parsing is incomplete. It misses some BCB function/procedure names. You can tell the responsible programmer to contact me. I'm willing to share this information.
ASKER
Is there a place, book to learn about all the TD32 debug infromation, thread safe exceptions and advanced debuging in general for Delphi, or is this a process where you learn by experience over the years ?
I know for example that I need to dig up my old assembly book to see in which CPU registers does the stack information reside and how to compute function addresses and stuff like that. But when you want to specifically know about advanced debuging in Delphi where would one go ?
I know for example that I need to dig up my old assembly book to see in which CPU registers does the stack information reside and how to compute function addresses and stuff like that. But when you want to specifically know about advanced debuging in Delphi where would one go ?
There were a few articles about exception handling Delphi somewhere. Don't have the links here right now, though. Also there's an article about structured exception handling in Windows by Matt Pietrek. There's a document from Borland about TD32 debug information. But the easiest way to look into all this would probably be to check out the JCL source code.
ASKER
Also if I add a breakpoint on this exact source code line {PTest.sBuffer := 'string' } the CPU Window gives me the address of this instruction as 00452975 which does not match neither the AV error reported instruction 004042A4, nor the instruction address in the MAP file 37 0001:00051975.