DGL
https://delphigl.com/forum/

access violation how to debug?
https://delphigl.com/forum/viewtopic.php?f=19&t=8068
Seite 1 von 1

Autor:  noeska [ Fr Dez 26, 2008 10:57 ]
Betreff des Beitrags:  access violation how to debug?

I am getting an access violation (some number) in an application i am developeing with delphi2005pro.
The problem the access violation is raised after freeandnill(someobjec) is called.

This is the situation. In an onclick event for an button an object is created, used an finaly freeandnil is called. Only after the freeandnill is called the access violation is thrown.
Also i tried stepping through the code all the way butt all went well and after the freeandnill call the exception is there again. Now how do i debug that?

I do suspect something is going wrong in the object. But i am unable to debug it for now.

Any pointers to where i should look debug it in another way?

The offending class/object is the vfs i am developing.

Thanks for your help in advance.

Autor:  Delphic [ Fr Dez 26, 2008 11:05 ]
Betreff des Beitrags: 

turn of any optimizing first and then rebuild the whole. check again, where the access violation occurs and, if it is still in the nowhere after the free and nil call, check the CPU Debug Window (if that still exists in delphi 2005) to get a hint what happens right before the raise of the exception.

Autor:  noeska [ Fr Dez 26, 2008 11:30 ]
Betreff des Beitrags: 

Optimizations are off. And the error is still at the end; of the onclick code for the button.

So the cpu debug view. How do i read that? Or for that matter how do i copy its contents?

Also in the block where the error occurs looks like this:
Code:
  1.  
  2. TObject.InheritsFrom
  3. 004033B0 EB02             jmp $004033b4
  4. 004033B2 8B00             mov eax,[eax]
  5. 004033B4 39D0             cmp eax,edx
  6. 004033B6 7408             jz $004033c0
  7. 004033B8 8B40DC           mov eax,[eax-$24]        <-- this is the line where the code stops
  8.  

Autor:  Delphic [ Fr Dez 26, 2008 12:15 ]
Betreff des Beitrags: 

I don't know how to copy that.

Now what you see there is where within your code you broke down and - interestingly you are in the function TObject.InheritsFrom, checking if an object is an ancestor of a type. What you may be interested in is, if the register eax contains an adress that is very near to the object you just freed and nilled or belongs to the adress of the object that made the call, which could mean that freeing the object also freed the object that made the call (maybe through some recursion in the destructor), which of course is no good idea. Actually, eax at $004033B8 (thats the position within the machine code of your program) contains an adress, which is accessed through the use of the brackets [] in the mov statement. (That's why I think eax is interesting). Anyhow, if eax is 0, that idea doesn't help. (tip for reading assembler: mov is a bad name. actually mov is a put statement; so in your line eax is to be replaced with the value that is to be found at adress eax-$24.)

If that doesn't give you a hint, you can use the stack window (debug stack, stack trace, or whatever it is called in delphi) and try to figure out on which route the call to TObject.InheritsFrom came from your part of the application; in the hope that things do not end in question marks but in function names that would help you (you may be in a mess, if the function where the debugger placed the exception is not in that list); also, while tracing back the stack, you should be able to investigate the variables of the functions on that way (the adress of self may always be interesting, especially in comparsion to the object you just freed).

Things get especially interesting if the stack trace leads you into a lot of questionmarks instead of function names. then the names of the calling functions could not be determined, which may be caused by operating system calls or librarys you used. Then you should check if the object you freed or any child objects was not in use by an asynchronous library - or one of your threads. Checking the threads window before the free, after the free and after the crash may be interesting. No threads should die unexpectedly.

Autor:  noeska [ Fr Dez 26, 2008 12:47 ]
Betreff des Beitrags: 

Now the stacktrace provided some more usefull info:
:7c812aeb kernel32.RaiseException + 0x52
:00403598 NotifyNonDelphiException + $1C
:7c90327a ntdll.RtlConvertUlongToLargeInteger + 0x3c
:7c90e46a ntdll.KiUserExceptionDispatcher + 0xe
TVirtualFileSystem.WriteBlock(9,10,0,$9F818C)
TVirtualFileSystem.WriteVirtualFileStreamBuffer((no value),0,22472,(6, 7))
TVirtualFileStream.Write((no value),22472)
:004159d4 TStream.WriteBuffer + $18
TForm1.bDynWriteClick($9F616C)

Now i have to find the offending value?

But what i dont get how can the code reach the free part and beyond in the bDynWriteClick as it is even able to update a label there before showing the access violation error.

Autor:  noeska [ Fr Dez 26, 2008 13:22 ]
Betreff des Beitrags: 

Somehow the code now seems to stop at the offending part. Maybe due to restarting delphi2005pro? I always thought a rebuild all did a complete rebuild. Not that it make more sense now.

Code:
  1.  
  2. procedure TVirtualFileSystem.WriteBlock(ablockid, anextblockid, aprevblockid: int64; pBuf: Pointer);
  3. begin
  4.   FDataFile.Position := (ablockid * FBlockSize); //goto begin of block
  5.   FDataFile.WriteBuffer(aprevblockid,sizeof(aprevblockid));
  6.   FDataFile.WriteBuffer(anextblockid,sizeof(anextblockid));
  7.   FDataFile.WriteBuffer(pbuf^,FBlockSize-(sizeof(int64)*2) );
  8. end;
  9.  


Somehow FDataFile becomes inaccesible? I dont believe that can happen with FDataFile when writing blocks in a row. So for now i go look what happens with FDataFile between the writeblock calls.

Autor:  Delphic [ Fr Dez 26, 2008 13:23 ]
Betreff des Beitrags: 

Updating the label may be done through the system UI thread and may not invoke an message/event (repaint/redraw) in your application; although, if the the new text of the label is set after the exception occurred, that would be very concerning.

Actually I think you can already read offending values in the stack trace. As far as I remember (I'm not completely sure on that), the first argument of functions within objects/classes in delphi is the self variable, so nil, "no value"(?!) or any low value could already give you an idea what is wrong. I don't know if you already noticed, but you should be able to double click into the stack trace to jump to the last call in that function, that invoked the call to the function further on top of the trace. There you can read the contents of variables, if that helps you. The Point where you lost control to the OS is the KiUserExceptionDispatcher function, which already belongs to the Exception Handling Process of the OS, so the offending call creating the access violation is in TVirtualFileSystem.WriteBlock(9,10,0,$9F818C).

Autor:  Delphic [ Fr Dez 26, 2008 13:27 ]
Betreff des Beitrags: 

noeska hat geschrieben:
Somehow FDataFile becomes inaccesible? I dont believe that can happen with FDataFile when writing blocks in a row. So for now i go look what happens with FDataFile between the writeblock calls.

Then you should pin it into your watches window... Maybe you overwrite it with some nonsense while over-filling some buffer? Calls to "Move()" are prone to that.

Also notice that we started this journey in "TObject.InheritsFrom". So I think restarting Delphi was a good Idea. If things still tend to be very obscure, a restart of your machine may help.

Autor:  noeska [ Fr Dez 26, 2008 14:27 ]
Betreff des Beitrags: 

Whow i found the offending part:

procedure TVirtualFileSystem.GetBlockId(var existingblock: Boolean; iEndBlock: Integer; var blocklistsize: Integer; var BlockList: TBlockList; i: Integer; var b: Int64; var nb: Int64);

You see GetBlockId was created using refactoring in delphi and i forgot to add a var declaration to blocklistsize.

Code:
  1.  
  2.     if BlockList = nil then
  3.     begin
  4.       BlockListSize := 0;
  5.       BlockListSize := BlockListSize + 1;
  6.       SetLength(BlockList, BlockListSize);
  7.     end
  8.     else
  9.     begin
  10.       BlockListSize := BlockListSize + 1;
  11.       SetLength(BlockList, BlockListSize);
  12.     end;
  13.     BlockList[i] := b;


So after 3 runs i goes out of sync with blocklistsize and then blocklist[i] := b manages to destroy the object based on TVirtualFileSystem of wich FDataFile is also an part. I still dont understand why but i do know i did not give GetBlockId the attention it deserverd. I better go clean it up and give better names to the vars used.

@Delphic: Thanks for helping me debugging my code and learn how to use the delphi ide debug tools better. I never used the stacktrace before. Thanks.

Autor:  Delphic [ Fr Dez 26, 2008 14:55 ]
Betreff des Beitrags: 

noeska hat geschrieben:
So after 3 runs i goes out of sync with blocklistsize and then blocklist[i] := b manages to destroy the object based on TVirtualFileSystem of wich FDataFile is also an part. I still dont understand why but i do know i did not give GetBlockId the attention it deserverd. I better go clean it up and give better names to the vars used.

So you wrote over array bounds? You can avoid this sort of problems by ticking "bound checking" (?) in the compiler options. See Debuggint Tutorial#Voreinstellungen . You will catch an IndexOutOfBounds Exception (or something like that) on trying to write - i.e. before you use the corrupted data ;-)

Autor:  noeska [ Fr Dez 26, 2008 15:09 ]
Betreff des Beitrags: 

:oops: whoops i had that on for the executeable but not for the package where my vfs sourcecode is. I always turn on these runtime settings. I wonder why they were not on for the package. Now they are on :-) That out of bounds exception was something i expected to happen, but it did not. Now i know why.

Autor:  Frase [ So Apr 19, 2009 10:46 ]
Betreff des Beitrags: 

just saw this old thread ;)
There's one more thing to add: You can turn on "Debug DCUs" in the Delphi options. This allows you to inspect the source code of the Run Time Library during execution.
That way you should not require the CPU view at all, as almost the complete program code can be debugged within the Delphi IDE itself at the source code level.
Of course, using the CPU view does not harm at all ;) But it's somewhat harder to figure out than using the source.

Hope this helps,
~ Philipp

Seite 1 von 1 Alle Zeiten sind UTC + 1 Stunde
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/