Link to home
Start Free TrialLog in
Avatar of Tavirgil
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\Project2.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
Avatar of Tavirgil
Tavirgil

ASKER

I forgot to mention that I understand that the AV address 004042A4 reported, is in another segment call (I don't know if this is correct how I am stating it) from my Button1Click procedure which holds the source code line that is responsable for the error.

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.

http://www.madshi.net
Madshi knows all about it, but i am not sure if he has explanations online.
SOLUTION
Avatar of andrewjb
andrewjb
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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 :-)
>> 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.
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.
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)
> 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.
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.
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.
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 ?


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.