now i'm simulating the linux version, the try-catch-throw are using the same type of code using setjmp/longjmp functions but the trapping of the system errors, signals in fact are differents on the linux versus windows
i made here an exercice , is it working on linux ? i don't know
is it compiling on 32 and 64 ?
is it trapping the signals ctrol-c (SIGINT) , segmentation fault (SIGSEGV ) etc...
i've put different possibilities of code , inside or outside Try-catch blocks , to test the trapping
Please play with, and better please report what you found.
has to be compiled with console ...
Thanks in advance for your support. :-)
note : as i wanted to simulate how it is working in windows , you will find plenty of #ifdef __FB_WIN32__ on the code
please don't care, a final version (if any) will be cleaned
Code: Select all
#ifdef __FB_WIN32__
# Include once "windows.bi"
#endif
# Include once "crt/setjmp.bi" 'setjmp/longjmp usage
# Include once "crt/string.bi" 'memmove usage
#define SIG_IGN cptr(any ptr, 1)
Const SIGHUP = 1 'Hangup (POSIX)
Const SIGINT = 2 'Terminal interrupt (ANSI)
Const SIGQUIT = 3 'Terminal quit (POSIX)
Const SIGILL = 4 'Illegal instruction (ANSI)
Const SIGTRAP = 5 'Trace trap (POSIX)
Const SIGIOT = 6 'IOT Trap (4.2 BSD)
Const SIGBUS = 7 'BUS error (4.2 BSD)
Const SIGFPE = 8 'Floating point exception (ANSI)
Const SIGKILL = 9 'Kill (can't be caught or ignored) (POSIX)
Const SIGUSR1 = 10 'User defined signal 1 (POSIX)
Const SIGSEGV = 11 'Invalid memory segment access (ANSI)
Const SIGUSR2 = 12 'User defined signal 2 (POSIX)
Const SIGPIPE = 13 'Write on a pipe with no reader, Broken pipe (POSIX)
Const SIGALRM = 14 'Alarm clock (POSIX)
Const SIGTERM = 15 'Termination (ANSI)
Const SIGSTKFLT = 16 'Stack fault
Const SIGCHLD = 17 'Child process has stopped or exited, changed (POSIX)
Const SIGCONT = 18 'Continue executing, if stopped (POSIX)
Const SIGSTOP = 19 'Stop executing (can't be caught or ignored) (POSIX)
Const SIGTSTP = 20 'Terminal stop signal (POSIX)
Const SIGTTIN = 21 'Background process trying to read, from TTY (POSIX)
Const SIGTTOU = 22 'Background process trying to write, to TTY (POSIX)
Const SIGURG = 23 'Urgent condition on socket (4.2 BSD)
Const SIGXCPU = 24 'CPU limit exceeded (4.2 BSD)
Const SIGXFSZ = 25 'File size limit exceeded (4.2 BSD)
Const SIGVTALRM = 26 'Virtual alarm clock (4.2 BSD)
Const SIGPROF = 27 'Profiling alarm clock (4.2 BSD)
Const SIGWINCH = 28 'Window size change (4.3 BSD, Sun)
Const SIGIO = 29 'I/O now possible (4.2 BSD)
Const SIGPWR = 30 'Power failure restart (System V)
Const NSIG = 32
# define VERBOSE 0
' 0 not verbose , else verbose mode
#Define TRY do : my_except_class.e_ptr = get_set_excep(1) : _
my_except_class.e_data[my_except_class.e_pos].icod = setjmp2(my_except_class.e_data[my_except_class.e_pos].jump) : _
If my_except_class.e_data[my_except_class.e_pos].icod = 0 Then : _
if VERBOSE THEN print space(my_except_class.e_pos) & "in Try block , e_pos = " & my_except_class.e_pos
#Macro CATCH(e , _type)
ElseIf my_except_class.e_data[my_except_class.e_pos].got = 1 and my_except_class.e_data[my_except_class.e_pos].icod < 0 then
show_catch(my_except_class.e_data[my_except_class.e_pos])
elseif my_except_class.e_data[my_except_class.e_pos].got = 1 and my_except_class.e_data[my_except_class.e_pos].icod = _type Then
Dim As exdata_type e = my_except_class.e_data[my_except_class.e_pos]
if VERBOSE THEN print space(my_except_class.e_pos) & " in Catch block , e_pos = " & my_except_class.e_pos
#EndMacro
#Macro CATCH_ANY(e)
ElseIf my_except_class.e_data[my_except_class.e_pos].got = 1 and my_except_class.e_data[my_except_class.e_pos].icod < 0 then
show_catch(my_except_class.e_data[my_except_class.e_pos])
ElseIf my_except_class.e_data[my_except_class.e_pos].got = 1 then
Dim As exdata_type e = my_except_class.e_data[my_except_class.e_pos]
if VERBOSE THEN print space(my_except_class.e_pos) & " in Catch_any block , e_pos = " & my_except_class.e_pos
#EndMacro
#Define FINALLY End If: If my_except_class.e_pos Then
#Define END_TRY if my_except_class.e_pos then : my_except_class.e_ptr = get_set_excep(-1) : end if : End If : exit do : loop
#Define THROW(_type) set_throw ( __FILE__, __FUNCTION__, __LINE__, "", _type)
#Define THROW_MSG(_type , mess) set_throw ( __FILE__, __FUNCTION__, __LINE__, mess, _type)
#Define RETHROW set_rethrow()
Type exdata_type
jump As jmp_buf Ptr
icod As long
file As String
proc As String
msg As String
line As long
got As byte
End Type
Type my_except_class
public:
declare constructor()
declare destructor()
declare sub back_except()
declare sub decrease_except()
declare sub increase_except()
Static e_pos As long
Static e_data As exdata_type Ptr
Static e_ptr As my_except_class Ptr
Static countID As long
private:
e_size As long
End Type
declare function raise cdecl alias "raise"(byval signal as long) as long
Declare Function Signal cdecl alias "signal" (ByVal V_Signal As long, byval V_Function As Any Ptr) as Any Ptr
declare function get_set_excep(byval iflag as long = 0) as my_except_class ptr
declare sub show_catch(byref e as exdata_type, byval flag as integer = 1)
declare sub set_throw( byref sfile as string, byref sproc as string, byval iline as long, byref smess as string, byval lcode as long)
declare sub set_rethrow()
#ifdef __FB_WIN32__
declare function setjmp2 cdecl alias "_setjmp"(byval as jmp_buf ptr , byval as any ptr = 0) as long
#else
declare function setjmp2 cdecl alias "setjmp" (byval as jmp_buf ptr) as long
#endif
'# ifdef _EXCEPTIONS_INIT_CODE_
declare sub trap_signals()
Dim As long my_except_class.countID = 0
Dim As long my_except_class.e_pos = 0
Dim As exdata_type Ptr my_except_class.e_data = 0
Dim As my_except_class Ptr my_except_class.e_ptr = 0
Private Constructor my_except_class()
if my_except_class.countID = 0 then
e_size = 4 'initial level setting normally not needed more , but reallocation is installed
my_except_class.e_data = callocate (sizeof(exdata_type) * e_size)
my_except_class.e_data[0].jump = 0
my_except_class.e_data[0].file = ""
my_except_class.e_data[0].proc = ""
my_except_class.e_data[0].msg = ""
my_except_class.e_data[0].line = 0
my_except_class.e_data[0].icod = 0
my_except_class.e_data[0].got = 0
my_except_class.countID = 1
my_except_class.e_pos = 0
end if
End Constructor
Private Destructor my_except_class()
if my_except_class.e_data <> 0 and my_except_class.countID = 1 then
while my_except_class.e_pos >= 0
if my_except_class.e_data[my_except_class.e_pos].jump THEN delete my_except_class.e_data[my_except_class.e_pos].jump
my_except_class.e_pos -= 1
wend
e_size = 0
deallocate(my_except_class.e_data)
end if
my_except_class.countID = 0
my_except_class.e_data = 0
my_except_class.e_pos = 0
End Destructor
Private sub my_except_class.back_except()
if my_except_class.e_pos < 2 then exit sub
if my_except_class.e_data[my_except_class.e_pos].jump THEN delete my_except_class.e_data[my_except_class.e_pos].jump
my_except_class.e_data[my_except_class.e_pos].jump = 0
my_except_class.e_data[my_except_class.e_pos - 1].icod = my_except_class.e_data[my_except_class.e_pos].icod
my_except_class.e_data[my_except_class.e_pos].icod = 0
my_except_class.e_data[my_except_class.e_pos - 1].got = my_except_class.e_data[my_except_class.e_pos].got
my_except_class.e_data[my_except_class.e_pos].got = 0
my_except_class.e_data[my_except_class.e_pos - 1].file = my_except_class.e_data[my_except_class.e_pos].file
my_except_class.e_data[my_except_class.e_pos].file = ""
my_except_class.e_data[my_except_class.e_pos - 1].proc = my_except_class.e_data[my_except_class.e_pos].proc
my_except_class.e_data[my_except_class.e_pos].proc = ""
my_except_class.e_data[my_except_class.e_pos - 1].msg = my_except_class.e_data[my_except_class.e_pos].msg
my_except_class.e_data[my_except_class.e_pos].msg = ""
my_except_class.e_data[my_except_class.e_pos - 1].line = my_except_class.e_data[my_except_class.e_pos].line
my_except_class.e_data[my_except_class.e_pos].line = 0
my_except_class.e_pos -= 1
end sub
Private sub my_except_class.decrease_except()
if my_except_class.e_pos < 1 then exit sub
if my_except_class.e_data[my_except_class.e_pos].jump THEN delete my_except_class.e_data[my_except_class.e_pos].jump
my_except_class.e_data[my_except_class.e_pos].jump = 0
my_except_class.e_data[my_except_class.e_pos].icod = 0
my_except_class.e_data[my_except_class.e_pos].got = 0
my_except_class.e_data[my_except_class.e_pos].file = ""
my_except_class.e_data[my_except_class.e_pos].proc = ""
my_except_class.e_data[my_except_class.e_pos].msg = ""
my_except_class.e_data[my_except_class.e_pos].line = 0
my_except_class.e_pos-= 1
end sub
Private sub my_except_class.increase_except()
if my_except_class.e_pos + 1 = e_size THEN
dim as exdata_type Ptr ptemp2 = callocate(sizeof(exdata_type) * e_size * 2)
if ptemp2 = 0 THEN
#ifdef _INC_WINDOWS
messagebox 0, "Close to abort !", "error .... reallocating", MB_ICONERROR
#else
print "error .... reallocating, terminate now!": print
sleep 5000
#endif
raise(4)
else
memmove(ptemp2, my_except_class.e_data, sizeof(exdata_type) * e_size)
deallocate(my_except_class.e_data)
END IF
my_except_class.e_data = ptemp2
e_size *= 2
if VERBOSE then print "reallocating... done"
END IF
my_except_class.e_pos += 1
my_except_class.e_data[my_except_class.e_pos].jump = new jmp_buf
my_except_class.e_data[my_except_class.e_pos].icod = 0
my_except_class.e_data[my_except_class.e_pos].got = 0
my_except_class.e_data[my_except_class.e_pos].file = ""
my_except_class.e_data[my_except_class.e_pos].proc = ""
my_except_class.e_data[my_except_class.e_pos].msg = ""
my_except_class.e_data[my_except_class.e_pos].line = 0
end sub
Private function get_set_excep(byval iflag as long = 0) as my_except_class ptr
if iflag = 0 and my_except_class.e_ptr = 0 THEN
'print "init0 here "
my_except_class.e_ptr = new my_except_class
elseif iflag < 0 and my_except_class.e_ptr <> 0 then
'print "decrease here "
my_except_class.e_ptr->decrease_except()
elseif iflag > 0 and my_except_class.e_ptr <> 0 then
'print "increase here "
my_except_class.e_ptr->increase_except()
elseif iflag > 0 and my_except_class.e_ptr = 0 then
'print "init1 here "
my_except_class.e_ptr = new my_except_class
my_except_class.e_ptr->increase_except()
END IF
return my_except_class.e_ptr
end function
Private sub set_throw( byref sfile as string, byref sproc as string, byval iline as long, byref smess as string, byval lcode as long)
dim as long _e_pos_ = my_except_class.e_pos
dim as exdata_type Ptr _exdata_type_ptr_ = my_except_class.e_data
_exdata_type_ptr_[_e_pos_].file = sfile
_exdata_type_ptr_[_e_pos_].proc = sproc
_exdata_type_ptr_[_e_pos_].line = iline
_exdata_type_ptr_[_e_pos_].msg = smess
_exdata_type_ptr_[_e_pos_].icod = lcode
_exdata_type_ptr_[_e_pos_].got = 1
if _e_pos_ > 0 THEN
longjmp(_exdata_type_ptr_[_e_pos_].jump, lcode)
else
show_catch(_exdata_type_ptr_[0], 0)
end if
end sub
Private sub set_rethrow()
dim as my_except_class ptr _except_ptr_ = my_except_class.e_ptr
dim as long _e_pos_ = my_except_class.e_pos
dim as exdata_type Ptr _exdata_type_ptr_ = my_except_class.e_data
if _e_pos_ > 1 and _exdata_type_ptr_[_e_pos_].got = 1 THEN
_except_ptr_->back_except()
_e_pos_ -= 1
longjmp(_exdata_type_ptr_[_e_pos_].jump, _exdata_type_ptr_[_e_pos_].icod)
end if
end sub
Private sub show_catch(byref e as exdata_type, byval flag as integer = 1)
dim as string status
dim as long e_pos = my_except_class.e_pos
if flag THEN
if VERBOSE THEN Print space(e_pos) & " e_exceptions message : " ; e.msg
status = "e_exceptions message : " & e.msg
else
if VERBOSE THEN Print space(e_pos) & " error exceptions : Throw outside Try-Catch bloc " & e.msg
status = "error exceptions : Throw outside Try-Catch bloc" & chr(10,10) & "e_exceptions message : " & e.msg
END IF
if VERBOSE THEN Print space(e_pos) & " e_exception code = " ; Str(e.icod)
status &= chr(10,10) & " e_exception code = " & Str(e.icod)
if VERBOSE THEN Print space(e_pos) & " file = " ; e.file
status &= chr(10) & " file = " & e.file
if VERBOSE THEN Print space(e_pos) & " proc = " ; e.proc
status &= chr(10) & " proc = " & e.proc
if VERBOSE THEN Print space(e_pos) & " line = " ; Str(e.line)
status &= chr(10) & " line = " & Str(e.line)
if flag and e.icod > 0 then
#ifdef _INC_WINDOWS
messagebox 0, status, "Catched Exception ", MB_ICONWARNING
#else
print "Catched Exception " : print " " ; status : print
#endif
elseif flag and e.icod < -99 THEN
trap_signals
#ifdef _INC_WINDOWS
messagebox 0, status, "Catched Exception ", MB_ICONWARNING
#else
print "Catched Exception " : print " " ; status : print
#endif
elseif e.icod < 0 and e.icod > -100 THEN
#ifdef _INC_WINDOWS
messagebox 0, status & chr(10, 10) & "Abort now!", "Catched Exception : Error", MB_ICONERROR
#else
print "Catched Exception " : print " " ; status : print : print "Abort now!"
sleep 5000
#endif
end(e.icod)
elseif flag = 0 and e_pos = 0 THEN
trap_signals
#ifdef _INC_WINDOWS
messagebox 0, status & chr(10, 10) & "Abort now!", "Thrown Exception wihout Try_Catch", MB_ICONERROR
#else
print "Thrown Exception wihout Try_Catch " : print " " ; status : print : print "Abort now!"
sleep 5000
#endif
end(e.icod)
end if
end sub
sub sig_SIGFPE cdecl()
Signal(SIGFPE, SIG_IGN )
THROW_MSG(- SIGFPE , "got SIGFPE!")
end sub
sub sig_SIGSEGV cdecl()
Signal(SIGSEGV, SIG_IGN )
THROW_MSG(- SIGSEGV , "got SIGSEGV!")
end sub
sub sig_SIGINT cdecl()
print "in SIGINT"
Signal(SIGINT, SIG_IGN )
THROW_MSG(- (SIGINT +100) , "got SIGINT!")
end sub
sub sig_SIGILL cdecl()
Signal(SIGILL, SIG_IGN )
THROW_MSG(- SIGILL , "got SIGILL!")
end sub
/' sub sig_SIGABRT cdecl()
Signal(SIGABRT, SIG_IGN )
THROW_MSG(- SIGABRT , "got SIGABRT!")
end sub '/
sub sig_SIGTERM cdecl()
Signal(SIGTERM, SIG_IGN )
THROW_MSG(- SIGTERM , "got SIGTERM!")
end sub
/' sub sig_SIGBREAK cdecl()
Signal(SIGBREAK, SIG_IGN )
THROW_MSG(- SIGBREAK , "got SIGBREAK!")
end sub '/
sub sig_SIGSTKFLT cdecl()
Signal(SIGSTKFLT, SIG_IGN )
THROW_MSG(- SIGSTKFLT , "got SIGSTKFLT!")
end sub
sub sig_SIGBUS cdecl()
Signal(SIGBUS, SIG_IGN )
THROW_MSG(- SIGBUS , "got SIGBUS!")
end sub
sub sig_SIGTRAP cdecl()
Signal(SIGTRAP, SIG_IGN )
THROW_MSG(- (SIGTRAP + 100) , "got SIGTRAP!")
end sub
sub sig_SIGTSTP cdecl()
Signal(SIGTSTP, SIG_IGN )
THROW_MSG(- (SIGTSTP + 100) , "got SIGTSTP!")
end sub
sub sig_SIGSTOP cdecl()
Signal(SIGSTOP, SIG_IGN )
THROW_MSG(- (SIGSTOP + 100), "got SIGSTOP!")
end sub
sub sig_SIGUSR1 cdecl()
Signal(SIGUSR1, SIG_IGN )
THROW_MSG(- (SIGUSR1 + 100), "got SIGUSR1!")
end sub
sub sig_SIGUSR2 cdecl()
Signal(SIGUSR2, SIG_IGN )
THROW_MSG(- (SIGUSR2 + 100) , "got SIGUSR2!")
end sub
sub sig_NSIG cdecl()
Signal(NSIG, SIG_IGN )
THROW_MSG(- (NSIG + 100), "got NSIG!")
end sub
sub sig_SIGHUP cdecl()
Signal(SIGHUP, SIG_IGN )
THROW_MSG(- (SIGHUP + 100), "got SIGHUP!")
end sub
sub trap_signals()
signal(SIGSEGV , SIG_IGN)
signal(SIGSEGV , @sig_SIGSEGV)
signal(SIGFPE , SIG_IGN)
signal(SIGFPE , @sig_SIGFPE)
signal(SIGINT , SIG_IGN)
signal(SIGINT , @sig_SIGINT)
signal(SIGILL , SIG_IGN)
signal(SIGILL , @sig_SIGILL)
/' signal(SIGABRT , SIG_IGN)
signal(SIGABRT , @sig_SIGABRT) '/
signal(SIGTERM , SIG_IGN)
signal(SIGTERM , @sig_SIGTERM)
/' signal(SIGBREAK , SIG_IGN)
signal(SIGBREAK , @sig_SIGBREAK) '/
signal(SIGSTKFLT , SIG_IGN)
signal(SIGSTKFLT , @sig_SIGSTKFLT)
'#ifdef __FB_LINUX__
signal(SIGBUS , SIG_IGN)
signal(SIGBUS , @sig_SIGBUS)
'#endif
signal(SIGTRAP , SIG_IGN)
signal(SIGTRAP , @sig_SIGTRAP)
signal(SIGSTOP , SIG_IGN)
signal(SIGSTOP , @sig_SIGSTOP)
signal(SIGTSTP , SIG_IGN)
signal(SIGTSTP , @sig_SIGTSTP)
signal(SIGUSR1 , SIG_IGN)
signal(SIGUSR1 , @sig_SIGUSR1)
signal(SIGUSR2 , SIG_IGN)
signal(SIGUSR2 , @sig_SIGUSR2)
signal(NSIG , SIG_IGN)
signal(NSIG , @sig_NSIG)
signal(SIGHUP , SIG_IGN)
signal(SIGHUP , @sig_SIGHUP)
end sub
'# undef _EXCEPTIONS_INIT_CODE_
if my_except_class.countID = 0 THEN
if VERBOSE then print "initialisation "
my_except_class.e_ptr = get_set_excep()
my_except_class.countID = 1
trap_signals()
end if
'#endif /' _EXCEPTIONS_INIT_CODE_ '/
'**********************************************************************************************
'End of the commun part to put into an include header
'**********************************************************************************************
'**********************************************************************************************
'utility functions / defines for test
'**********************************************************************************************
' define e_exception types
#Define DIVISION_BY_ZERO 221 ' can be any number
#Define FORCED_TO_ZERO 222 ' can be any number
#Define NOTHING_EX 250 ' can be any number
' function that can throw an e_exceptions
Function div1(byval a As Integer , byval b As Integer) As double
if b = 0 and a = 0 THEN
raise(SIGINT) '' Breakpoint (INT 3)
END IF
If b = 0 Then THROW_MSG(DIVISION_BY_ZERO, "Division by zero")
If b > a Then THROW_MSG(FORCED_TO_ZERO, "Forced to zero")
TRY
if b < a Then THROW_MSG(NOTHING_EX, "nothing noticed")
CATCH_ANY(e)
show_catch(e)
FINALLY
Print " div result printed"
END_TRY
Return a / b
End Function
' test function (calls div):
Function add_div(byval a As Integer , byval b As Integer , byval c As Single) As Integer
Return div1(a + b , c)
End Function
' test func:
Sub test()
TRY
TRY
print "test again ctrl-c in try-catch block or any key to continue" : sleep
Print add_div(10 , 5 , 1)
CATCH_ANY(e)
show_catch(e)
FINALLY
Print " div 1 done"
END_TRY
TRY
Print add_div(10 , 5 , 0)
CATCH_ANY(e)
show_catch(e)
FINALLY
Print " div 2 done"
END_TRY
TRY
Print add_div(10 , 5 , 30)
CATCH_ANY(e)
show_catch(e)
FINALLY
Print " div 3 done"
END_TRY
TRY
Print add_div(0 , 0 , 0)
CATCH_ANY(e)
show_catch(e)
FINALLY
Print " div 4 done"
END_TRY
print "put a breakpoint here"
raise(SIGINT) '' Breakpoint (INT 3)
CATCH_ANY(e)
print "catched something global in test()"
show_catch(e)
RETHROW
FINALLY
print "finally test()"
END_TRY
End Sub
sub stack_overflow()
dim as long foo(10000)'allocate something big on the stack
print "loop"
stack_overflow()
end sub
' This function can overflow the call stack.
function sum1to(byval n as ulong) as ulong
if n = 0 then
return 0
else
dim as ulong m = sum1to(n - 1)
return m + n
end if
end function
sub create_segflt()
print : print "test a segmentation fault here"
dim intPtr as integer ptr = 0
intPtr[10] = 1
END SUB
'**********************************************************************************************
'main test code begins here
'**********************************************************************************************
Print " test ctrl-c to abort , any key to continue" : sleep
'test a segmentation fault here
'create_segflt()
'print : print "test a stack_overflow here" 'uncomment these 3 lines
'stack_overflow() ' not trapped in fact, it crashs before been trapped....
'print sum1to(250000) ' not trapped in fact, it crashs before been trapped....
print : print "test function here with nested try_catch"
test()
' the Try..Catch block
TRY
test()
CATCH(e , DIVISION_BY_ZERO)
if e.msg <> "" then show_catch(e)
TRY
test()
CATCH_ANY(e)
show_catch(e)
RETHROW
FINALLY
Print " finally1"
END_TRY
CATCH_ANY(e) ' this will catch any other e_exceptions (that is not DIVISION_BY_ZERO)
show_catch(e)
FINALLY
Print " FINALLY executed allways, no matter if an e_exceptions was thrown or not"
END_TRY
TRY
test()
print "extra test"
dim d0 as double = div1(17, 0)
CATCH(e , DIVISION_BY_ZERO)
show_catch(e)
TRY
test()
CATCH_ANY(e)
show_catch(e)
FINALLY
Print " finally2"
END_TRY
CATCH_ANY(e) ' this will catch any other e_exceptions (that is not DIVISION_BY_ZERO)
show_catch(e)
FINALLY
Print " last finally"
END_TRY
TRY
print "new SIGINT"
raise(SIGINT)
Print " try test ctrl-c to abort , any key to continue" 'the ctrl-c aborts as normal , not trapped
sleep
CATCH_ANY(e)
show_catch(e)
FINALLY
Print " finally before leaving"
END_TRY
Print "press any key to finish" : sleep