>My company is currently evaluating the Verdix compile >on Sun platform. One problem we have run into is >that when an exception is raised the only traceback >history that is printed is > > Main Program Abandon Exception ________ > >Other compilers I have used provided a text description >of all the subprograms and line numbers the exception >was propergated thru. > >How do I get the Verdix compiler to do this? > > >Walt Barnes Software Engineer Walt I have been in your shoes! At one time I used the DEC Ada compiler on a VAX and became very dependent on their beautiful exception-crash stack trace. As you stated the Verdix compiler does not provide this useful information. NEAT SOLUTION: A friend and I put together a stack trace program for Sun/OS SPARC Verdix/Rational Ada(See below). This exception handler is really a signal handler. Verdix/Rational uses various UNIX signals for some of their predefined exceptions. This exception handler catches these signals, walks up the call stack, and prints each of the program counters in the call stack. Example output below: ======= Example START ========= Dead from Signal: 4 Code 133 (Constraint Error) ... Program_Counter = 1218B4 Program_Counter = 120704 Program_Counter = 41C7E4 Program_Counter = 25927C Program_Counter = 11851C ======= Example END ========= In the above example the Ada program crashed when an exception was raised by the instruction at address 1218B4. At that time the call stack was 5 calls deep. The program counters can then be used in the debugger to display each of the procedure names in the call stack. To view the procedure name, start the ASCII debugger. Do not start your program executing. For example type: $ a.db my_ada_executable When in the debugger type the commands: > wi 1218B4 > vi (wi = window instruction, vi = view source code). The cursor will be over the line of code that raised the exception. Sometimes the debugger gets confused and can not display the source line. In this case I can usually find the line by trying each of the other program counters until the source appears. I then press the �I� debugger command to show source and machine instructions and scroll down to the next higher program counter. In most cases I can find the bottom of the call stack by continuing this manual method. PROBLEMS: There are several problems with this exception handler. 1) It only catches the predefined exceptions (it does not catch user defined exceptions). 2) When it catches a predefined exception the program crashes (even if there is a handler for the exception. 3) The procedure names are not printed by the handler (i.e. must use the debugger)(this can be fixed by using the UNIX �nm -n� command to get exe symbols; see code comments below). 4) This exception handler only works with the SPARC. 5) Verdix/Rational does not deliver a program like this with their compiler. PLEASE IMPROVE: I developed this exception handler to locate exceptions crashes. I would like to improve it by adding the capability of logging the exception and then jumping to the appropriate exception handler. I work in real-time systems and thus would like to log the exception for debugging and still continue executing my real-time process. I would love help figuring how I can return control to the Ada run-time so that the program can continue processing at the next exception handler. Verdix/Rational provides a pragma called RTS_INTERFACE that allows you to substitute your own exception handler (your custom handler logs the exception and call stack and then calls the predefined exception handler). I have not been able to get this pragma to work for the exception handler (even with Verdix support). A SECOND SOLUTION: The call stack can be viewed using the core dump file. The Verdix/Rational Ada debugger version I am using (version 1.1) does not work with core dumps (I hear that future versions will/do). Instead the dbx tool can be used to view the Ada call stack. To use dbx with a core file follow the steps below. 1) Attach the exception handler as shown in the example below. 2) Change the core file size limit in your .cshrc file. limit coredumpsize 10000 3) Run program and recreate exception. A core file will be created. 4) Run dbx debugger in directory of the core and executable files. dbx 5) Type the dbx command �where� to see the call stack trace back. Good Luck Ted Selig Software System Engineering Delcom Inc. ps I am a happy Verdix/Rational Ada user. --------- SAMPLE MAIN PROGRAM START ------------ with EXCEPTION_HANDLE_PKG; procedure My_Big_Faulty_Program is begin -- Attach signal handler. EXCEPTION_HANDLE_PKG.Attach; -- Execute Haystack with one needle exception. Run_Stuff; ... end My_Big_Faulty_Program ; --------- SAMPLE MAIN PROGRAM END ------------ --------- EXCEPTION HANDLER PACKAGE START ------------ --- You may have to add Special Verdix libraries to get the packages used in the body. with LANGUAGE; with CURRENT_EXCEPTION ; package EXCEPTION_HANDLE_PKG is -- Start exception handling. procedure ATTACH ; -- Print Exception Information if available. procedure PRINT ; -- Return exception name if available. function NAME return string renames CURRENT_EXCEPTION.EXCEPTION_NAME ; -- Abort process and give core dump. function SYSTEM_ABORT return INTEGER ; function KILL( PID : INTEGER ; -- Description : Process ID to Send Signal to. SIG : INTEGER -- Description : Signal to send. ) return INTEGER ; -- Description: Status returned. 0 = success. -1 = failure. function GETPID return INTEGER ; -- Description: Returns process ID. private pragma INTERFACE(C, GETPID); pragma INTERFACE(C, KILL); pragma INTERFACE(C, SYSTEM_ABORT); pragma INTERFACE_NAME(SYSTEM_ABORT, LANGUAGE.C_SUBP_PREFIX & "abort"); end EXCEPTION_HANDLE_PKG ; with IFACE_INTR ; with TEXT_IO ; with SYSTEM ; use SYSTEM ; with UNIX_CALLS; with ERRNO; --with V_I_RAISE ; with UNSIGNED_TYPES ; use UNSIGNED_TYPES ; with HEX ; with raw_dump; with unix_prcs ; with a_strings; use a_strings; package body EXCEPTION_HANDLE_PKG is subtype NAME_T is string(1..100) ; BLANK_NAME_K : constant NAME_T := (others => ' ') ; type EXCEPTION_DATA_T is record SET : BOOLEAN := FALSE ; -- Flage set when data stored. NAME : NAME_T := BLANK_NAME_K ; -- Name of exception. SIGNAL : INTEGER := 0 ; -- The signal just raised. CODE : INTEGER := 0 ; -- The subcode of the signal. CONTEXT : IFACE_INTR.SIGCONTEXT_T -- The Signal Context. := ( sc_onstack => 0, -- sigstack state to restore sc_mask => 0, -- signal mask to restore sc_sp => 0, -- sp to restore sc_pc => 0, -- pc to retore sc_ps => 0 -- psl to restore ) ; end record ; LAST_EXCEPTION : EXCEPTION_DATA_T ; -- Description: Exception data is stored by handler until -- "PRINT" called. package INT_IO is new TEXT_IO.INTEGER_IO(INTEGER) ; procedure PRINT is begin TEXT_IO.NEW_LINE ; if LAST_EXCEPTION.SET then TEXT_IO.PUT_LINE("Dead from " & LAST_EXCEPTION.NAME ) ; TEXT_IO.PUT("Signal: ") ; INT_IO.PUT( ITEM => LAST_EXCEPTION.SIGNAL, WIDTH => 0) ; TEXT_IO.PUT(" Code: ") ; INT_IO.PUT(ITEM => LAST_EXCEPTION.CODE, WIDTH => 0) ; TEXT_IO.NEW_LINE ; TEXT_IO.PUT("Context SC_ONSTACK: ") ; INT_IO.PUT( LAST_EXCEPTION.CONTEXT.SC_ONSTACK, WIDTH => 0, BASE => 16 ) ; TEXT_IO.PUT(" SC_MASK: ") ; INT_IO.PUT( ITEM => LAST_EXCEPTION.CONTEXT.SC_MASK, WIDTH => 0, BASE => 16); TEXT_IO.PUT(" SC_SP: ") ; INT_IO.PUT( ITEM => LAST_EXCEPTION.CONTEXT.SC_SP, WIDTH => 0, BASE => 16 ) ; TEXT_IO.PUT(" SC_PC: ") ; INT_IO.PUT( ITEM => LAST_EXCEPTION.CONTEXT.SC_PC, WIDTH => 0, BASE => 16 ) ; TEXT_IO.PUT(" SC_PS: ") ; INT_IO.PUT( ITEM => LAST_EXCEPTION.CONTEXT.SC_PS, WIDTH => 0, BASE => 16 ) ; TEXT_IO.NEW_LINE ; else TEXT_IO.PUT_LINE(" No exception data stored." ); end if ; TEXT_IO.NEW_LINE ; end PRINT ; procedure HANDLE( SIGNAL : in INTEGER ; -- The signal just raised. CODE : in INTEGER ; -- The subcode of the signal. CONTEXT : in IFACE_INTR.A_SIGCONTEXT -- The Signal Context. ) is pragma OPTIMIZE_CODE(OFF); LAST_SIGNAL : INTEGER ; LAST_MASK : INTEGER ; STATUS : INTEGER := 0; -- EXCEPTION_NAME : constant string -- := NAME & BLANK_NAME_K ; SP : UNSIGNED_TYPES.UNSIGNED_INTEGER := UNSIGNED_TYPES.UNSIGNED_INTEGER(CONTEXT.SC_SP) ; PC : UNSIGNED_TYPES.UNSIGNED_INTEGER := UNSIGNED_TYPES.UNSIGNED_INTEGER(CONTEXT.SC_PC) ; LONGWORD : constant := 4 ; type Process_T is record PC : UNSIGNED_TYPES.UNSIGNED_INTEGER ; Offset : UNSIGNED_TYPES.UNSIGNED_INTEGER ; Symbol : string(1..132) ; end record ; Stack : array(1..20) of Process_T := ( others => ( 0, 0, (others =>' '))); begin LAST_EXCEPTION := ( SET => TRUE, NAME => BLANK_NAME_K, --EXCEPTION_NAME(NAME_T'range), SIGNAL => SIGNAL, CODE => CODE, CONTEXT => CONTEXT.all ) ; PRINT ; --raw_dump(SYSTEM.MEMORY_ADDRESS(SP-200),500); -- Walk the stack. Stack_Walk: for Level in Stack'range loop declare FP : UNSIGNED_TYPES.UNSIGNED_INTEGER ; for FP use at SYSTEM.MEMORY_ADDRESS(SP + 14 * LONGWORD); begin exit Stack_Walk when FP = 0; Stack(Level).PC := PC ; --TEXT_IO.PUT("SP; " & hex.unsigned_to_hex(SP,10)) ; TEXT_IO.PUT(" Program_Counter = " & hex.unsigned_to_hex(PC)) ; --TEXT_IO.PUT(" Frame_Pointer = " & hex.unsigned_to_hex(FP)) ; TEXT_IO.NEW_LINE ; declare NEXT_PC : UNSIGNED_TYPES.UNSIGNED_INTEGER ; for NEXT_PC use at SYSTEM.MEMORY_ADDRESS(SP + (15*LONGWORD)); begin PC := NEXT_PC; end; SP := FP ; end ; end loop Stack_Walk; Get_Symbols: declare Program_Name : constant string := "crash" ; -- TBD Temporary_File : constant string := "tmp" ; -- TBD -- UNIX command to get symbol table. Command : constant string := "nm -n " & Program_Name & --" | grep \"" T \"" > " " > " & Temporary_File ; Status_I : INTEGER ; function system( Command : a_string ) return INTEGER ; pragma interface(C, system); begin TEXT_IO.PUT_LINE(Command); Status_I := system(to_a(Command)); TEXT_IO.PUT_LINE("STATUS = " & INTEGER'IMAGE(STATUS)); end Get_Symbols ; --IFACE_INTR.SIGNAL(SIGTYPE => 17, HANDLER => IFACE_INTR.SIG_DFL) ; -- Enable all signals LAST_MASK := IFACE_INTR.sigsetmask(mask => IFACE_INTR.ENABLE_MASK); TEXT_IO.PUT(" Previous Signal Mask: ") ; INT_IO.PUT( ITEM => LAST_MASK, WIDTH => 0, BASE => 16); TEXT_IO.NEW_LINE; --STATUS := SYSTEM_ABORT ; --if STATUS < 0 then -- TEXT_IO.PUT(" System abort error number: " & -- ERRNO.ERROR_CODES'image(ERRNO.errno)) ; --end if ; TEXT_IO.PUT(" Process ID: " & INTEGER'image(GETPID)); TEXT_IO.NEW_LINE(2); if KILL(PID => GETPID, SIG => 3) < 0 then TEXT_IO.PUT(" Sig 3 error number: " & ERRNO.ERROR_CODES'image(ERRNO.errno)) ; end if ; if KILL(PID => GETPID, SIG => 10) < 0 then TEXT_IO.PUT(" Sig 10 error number: " & ERRNO.ERROR_CODES'image(ERRNO.errno)) ; end if ; if KILL(PID => GETPID, SIG => 11) < 0 then TEXT_IO.PUT(" Sig 11 error number: " & ERRNO.ERROR_CODES'image(ERRNO.errno)) ; end if ; if KILL(PID => GETPID, SIG => 19) < 0 then TEXT_IO.PUT(" Sig 19 error number: " & ERRNO.ERROR_CODES'image(ERRNO.errno)) ; end if ; TEXT_IO.NEW_LINE; -- Reset the routine that catched the signal. --IFACE_INTR.SIGNAL(SIGTYPE => SIGNAL, HANDLER => IFACE_INTR.SIG_DFL) ; --IFACE_INTR.SIGNAL(SIGTYPE => 4, HANDLER => IFACE_INTR.SIG_DFL) ; -- Unblock the signal. --LAST_SIGNAL := IFACE_INTR.SIGBLOCK(16#7FFA_F007#) ; --UNIX_CALLS.SYS_EXIT(-1); --V_I_RAISE.raise_exception(identifier => -- SYSTEM.MEMORY_ADDRESS(UNSIGNED_TYPES.UNSIGNED_INTEGER(CONTEXT.SC_PC))); exception when others => -- Process will die if handler is exited via an exception. null ; end HANDLE ; procedure ATTACH is STATUS : INTEGER := 0 ; -- 0 on success. -- -1 on failure and sets errno to indicate the error. HANDLE_INFO_K : constant IFACE_INTR.SIGVEC_T := (sv_handler => HANDLE'ADDRESS , -- signal handler sv_mask => IFACE_INTR.ENABLE_MASK, --ENABLE_MASK, -- signal mask to apply (disable all signals) sv_onstack => 16#00# -- if non-zero, take on signal stack ) ; SIGHUP : constant INTEGER := IFACE_INTR.SIGHUP ; -- hangup SIGUSR1 : constant INTEGER := 30 ; -- user-defined signal 1 SIGUSR2 : constant INTEGER := 31 ; -- user-defined signal 2 begin -- Set all signals. Note: SIGKILL and SIGSTOP are set -- and ignored by UNIX. -- Contraint error. STATUS := IFACE_INTR.SIGVEC( SIG => IFACE_INTR.SIGILL, --illegal instruction (not reset) 4 VEC => HANDLE_INFO_K'address, OVEC => SYSTEM.NO_ADDR ) ; if ( STATUS /= 0) then TEXT_IO.PUT( "Signal SIGILL Status " ) ; INT_IO.PUT( ITEM => STATUS, WIDTH => 0, BASE => 16 ) ; TEXT_IO.NEW_LINE ; end if; -- Numeric error. STATUS := IFACE_INTR.SIGVEC( SIG => IFACE_INTR.SIGFPE, --floating point exception VEC => HANDLE_INFO_K'address, OVEC => SYSTEM.NO_ADDR ) ; if ( STATUS /= 0) then TEXT_IO.PUT( "Signal SIGFPE Status " ) ; INT_IO.PUT( ITEM => STATUS, WIDTH => 0, BASE => 16 ) ; TEXT_IO.NEW_LINE ; end if; -- Storage error. STATUS := IFACE_INTR.SIGVEC( SIG => IFACE_INTR.SIGSEGV, -- 11 VEC => HANDLE_INFO_K'address, OVEC => SYSTEM.NO_ADDR ) ; if ( STATUS /= 0) then TEXT_IO.PUT( "Signal SIGSEGV Status " ) ; INT_IO.PUT( ITEM => STATUS, WIDTH => 0, BASE => 16 ) ; TEXT_IO.NEW_LINE ; end if; end ATTACH ; end EXCEPTION_HANDLE_PKG ; --------- EXCEPTION HANDLER PACKAGE END ------------