Page 1 of 1

Better detection of error position ?

Posted: Tue Jul 12, 2016 5:25 pm
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.

Re: Better detection of error position ?

Posted: Tue Jul 12, 2016 5:30 pm
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()

Re: Better detection of error position ?

Posted: Tue Jul 12, 2016 7:11 pm
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.

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 1:44 am
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

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 8:17 am
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.

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 8:29 am
by Joris

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 8:35 am
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

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 8:45 am
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.

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 9:10 am
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

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 10:47 am
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.

Re: Better detection of error position ?

Posted: Wed Jul 13, 2016 10:55 am
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