Better detection of error position ?

Just starting out? Need help? Post your questions and find answers here.
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Better detection of error position ?

Post by Joris »

Hi,

How can I get a better position or line detection for errors shown in the debugger, crashing my prog ?

Now I almost finished my prog (20.000 lines) I get errors I didn't experience before.
Sad is that I don't know where the error exactly is created.
The debugger line number shows different and wrong positions like in the code part below : Line 3267 :

[17:47:37] [ERROR] Draw and Move.pbi (Line: 3267)
[17:47:37] [ERROR] Invalid memory access. (read error at address 4128661)
[17:47:42] The Program was killed.

Code: Select all

Procedure MinMax(value.i, bottom.i, top.i)  
  If value>top
    ProcedureReturn top
  ElseIf bottom>value 
    ProcedureReturn bottom
  EndIf
  ProcedureReturn value	   [b];<<<< Here is a (read error at address 4128661) <<< (Line: 3267)[/b]
EndProcedure
I don't know much about the use of a debugger. It shows some actions and errors if there are, but that's quit it.
So ...

Thanks.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Better detection of error position ?

Post by Keya »

one method is to check ""Enable OnError lines support" in Compiler Options
then when you get a crash, your OnErrorCall() routine can read ErrorLine()
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Better detection of error position ?

Post by Lunasole »

Joris wrote: The debugger line number shows different and wrong positions like in the code part below : Line 3267 :
Try running with Purifier, it should detect error after it actually happens, and add PurifierGranularity(1,1,1,1) at the beginning of your code.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Ramihyn_
Enthusiast
Enthusiast
Posts: 314
Joined: Fri Feb 24, 2006 9:40 am

Re: Better detection of error position ?

Post by Ramihyn_ »

If something seemingly harmless crashes on a ProcedureReturn, it is usually because the stack got corrupted earlier in a way that didnt result in a crash immediately. Your bug likely hides in the previous instructions before that call to MinMax().

I am assuming that you used the x86 compiler version, so this is how the prodecure looks like:

Code: Select all

; Procedure MinMax(value.i, bottom.i, top.i) 
_Procedure0:
  PUSH   ebx
  PS0=8
; If value>top
  MOV    ebx,dword [esp+PS0+0]
  CMP    ebx,dword [esp+PS0+8]
  JLE   _EndIf2
; ProcedureReturn top
  MOV    eax,dword [esp+PS0+8]
  JMP   _EndProcedure1
; ElseIf bottom>value
  JMP   _EndIf1
_EndIf2:
  MOV    ebx,dword [esp+PS0+4]
  CMP    ebx,dword [esp+PS0+0]
  JLE   _EndIf3
; ProcedureReturn bottom
  MOV    eax,dword [esp+PS0+4]
  JMP   _EndProcedure1
; EndIf
_EndIf1:
_EndIf3:
; ProcedureReturn value      
  MOV    eax,dword [esp+PS0+0]
  JMP   _EndProcedure1
; EndProcedure
_EndProcedureZero1:
  XOR    eax,eax
_EndProcedure1:
  POP    ebx
  RET    12
Look for everything immediately before that call to MinMax where the stack still worked (procedure calls and returns) and remember that your local variables are on the stack. Move the Minmax call earlier in the source code and see when it stops crashing, look for anything that can corrupt the stack - like a loop that writes into a local array and accidentially overwrites the boundaries. A small index error can do that, while a big error would show up earlier.

Debugging is all about experience - good luck :)

ps: the purifier tip is hopefully showing it too
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Better detection of error position ?

Post by Joris »

Lunasole wrote:... Try running with Purifier, it should detect error after it actually happens, and add PurifierGranularity(1,1,1,1) at the beginning of your code.
I suppose Purifier is a tool ?
Where can I find that ?
Nothing in the IDE, nothing in the PB-help...

Thanks.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Better detection of error position ?

Post by Joris »

Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Better detection of error position ?

Post by Keya »

yes its on the 2nd tab in Compiler Options so it remains somewhat hidden until you first stumble upon it :D
Here's a great blog entry about it! http://www.purebasic.fr/blog/?p=237
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Better detection of error position ?

Post by Joris »

I think I found the problem as I split a double jump with the same variable into two seperate ones.
I experienced this before with a Callback. Infact it's just a better way of coding too. Hopefully it's fixed now.
(I can't recreate the use/situation it crashed before.).

One more question :

OnErrorCall seems to stop the program before 'crashing it'. So saving user-created data in that call is good (but maybe buggy).
Recommended or not ?

Thanks Keya and others.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Better detection of error position ?

Post by Keya »

my understanding is that when youve caught an OnError you should do a minimal report to the user "here's the crash details to email the author" but then immediately terminate the app? please correct me if im wrong! [edit] actually yes the helpfile for OnErrorGoto states "The best practice is just to gather and display information about the error and then exit the program."

Here's a little demo (try without Debugger enabled)

Code: Select all

Procedure.s Hex8(lVal.l)
  ProcedureReturn RSet(Hex(lVal.l,#PB_Long), 8, "0")
EndProcedure
Procedure.s Hex16(qVal.q)
  ProcedureReturn RSet(Hex(qVal.q,#PB_Quad), 16, "0")
EndProcedure

;SET UP THE SEH ERRORHANDLER
OnErrorGoto(?ErrorHandler) 

;Main program...
OpenConsole()
PrintN("Generating an invalid memory access (should crash @ 0x" + Hex8(?_ErrorAddress) + ")")

_ErrorAddress:
! mov eax, [0] ;crash with invalid read address

PrintN("Press any key to exit..."): Input()   ;shouldn't reach this far due to crash
CloseConsole()
End


ErrorHandler:
PrintN("An error has occurred! Details:")
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
  PrintN("Error #" +  Hex8(ErrorCode()) + " @ 0x" + Hex8(ErrorAddress()))
CompilerElseIf #PB_Compiler_Processor = #PB_Processor_x64  
  PrintN("Error #" +  Hex8(ErrorCode()) + " @ 0x" + Hex16(ErrorAddress()))    
CompilerEndIf
PrintN("Press any key to exit..."): Input()
End
and optional helper for ErrorCode() -> description:

Code: Select all

Procedure.s DecodeException(e.i)
  Protected sCode.s
  Select (e & $FFFFFFFF) ;unsigned
    Case #EXCEPTION_ACCESS_VIOLATION: sCode.s = "Access violation"
    Case #EXCEPTION_ARRAY_BOUNDS_EXCEEDED:  sCode.s = "Array bounds exceeded"
    Case #EXCEPTION_BREAKPOINT: sCode.s = "Breakpoint"
    Case #EXCEPTION_CONTINUABLE: sCode.s = "Continuable"
    Case #EXCEPTION_DEBUG_EVENT: sCode.s = "Debug event"
    Case #EXCEPTION_EXECUTE_HANDLER: sCode.s = "Execute handler"
    Case #EXCEPTION_DATATYPE_MISALIGNMENT:  sCode.s = "Data type misalignment"
    Case #EXCEPTION_FLT_DENORMAL_OPERAND: sCode.s = "Floating point denormal operand"
    Case #EXCEPTION_FLT_DIVIDE_BY_ZERO: sCode.s = "Floating point divide by zero"
    Case #EXCEPTION_FLT_INEXACT_RESULT: sCode.s = "Floating point inexact result"
    Case #EXCEPTION_FLT_INVALID_OPERATION:  sCode.s = "Floating point invalid operation"
    Case #EXCEPTION_FLT_OVERFLOW: sCode.s = "Floating point overflow"
    Case #EXCEPTION_FLT_STACK_CHECK:  sCode.s = "Floating point stack check"
    Case #EXCEPTION_FLT_UNDERFLOW:  sCode.s = "Floating point underflow"
    Case #EXCEPTION_IN_PAGE_ERROR:  sCode.s = "Exception in page error"
    Case #EXCEPTION_INT_DIVIDE_BY_ZERO: sCode.s = "Integer divide by zero"
    Case #EXCEPTION_INT_OVERFLOW: sCode.s = "Integer overflow"
    Case #EXCEPTION_ILLEGAL_INSTRUCTION: sCode.s = "Illegal instruction"
    Case #EXCEPTION_PRIV_INSTRUCTION: sCode.s = "Privileged instruction"
    Case #EXCEPTION_SINGLE_STEP:  sCode.s = "Single Step"      
    Case #EXCEPTION_STACK_OVERFLOW: sCode.s = "Stack overflow"
    Case #EXCEPTION_NONCONTINUABLE: sCode.s = "Noncontinuable"
    Case #EXCEPTION_INVALID_HANDLE: sCode.s = "Invalid handle"
    Case #EXCEPTION_INVALID_DISPOSITION: sCode.s = "Invalid disposition"      
    Case #EXCEPTION_GUARD_PAGE: sCode.s = "Guard page"
    Case #EXCEPTION_MAXIMUM_PARAMETERS: sCode.s = "Maximum parameters"
    Case #EXCEPTION_NONCONTINUABLE_EXCEPTION: sCode.s = "Noncontinuable exception"
    Default: sCode.s = "Exception 0x" + Hex(e) + " (" + Str(e)+") <undefined>"
  EndSelect
  ProcedureReturn sCode
EndProcedure
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Better detection of error position ?

Post by Joris »

Keya wrote:my understanding is that when youve caught an OnError you should do a minimal report to the user "here's the crash details to email the author" but then immediately terminate the app? please correct me if im wrong! [edit] actually yes the helpfile for OnErrorGoto states "The best practice is just to gather and display information about the error and then exit the program."
I agree, but to me most of those error messages are too limited to help me, or understand.
"Access violation" yeah ? (it could also be a message from my ex :) )
If no line-numbers are stated then (how could it be), it wont make much sense to me. So maybe I can give it a try with those savings of user data and end the prog after just a messaging the error actions.

Thanks again.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Better detection of error position ?

Post by Keya »

I don't like the idea of Error Line Numbering in a public Release build of my app, but one thing I do which is easy is have a Global LastProcCalled.i, and each procedure in my app simply starts with LastProcCalled = #ID_ProcName (unique to each proc), and then include that number in the errorhandler's crash report so at least you know which procedure the crash was in, plus there's virtually zero performance or size hit :)
I would love this as a feature actually so i dont have to manually do it but im sure Fred's ToDo list is long enough!
Joris wrote:"Access violation" yeah ? (it could also be a message from my ex :) )
you can distinguish errors from your ex by checking first for #EXCEPTION_BREAKPOINT then #EXCEPTION_NONCONTINUABLE
Post Reply