MODULE Traps;
IMPORT SYSTEM, Kernel32, Machine, TrapWriters, KernelLog, Streams, Modules, Objects, Kernel, Reflection, SystemVersion;
CONST
RecursiveLimit = 16;
TraceVerbose = FALSE; TestTrap = TRUE;
TrapMaxCharacters = 32*1024;
halt* = Objects.halt; haltUnbreakable* = Objects.haltUnbreakable;
TYPE
VAR
modes: ARRAY 25 OF CHAR;
flags: ARRAY 13 OF CHAR;
trapState: LONGINT;
check: Objects.Process;
PROCEDURE Show*( p: Objects.Process; VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord; long: BOOLEAN );
VAR overflow: BOOLEAN;
desc: ARRAY 128 OF CHAR;
code: LONGINT;
pc: LONGINT;
w: Streams.Writer;
PROCEDURE Flags( w: Streams.Writer; s: SET );
VAR i: SHORTINT; ch: CHAR;
BEGIN
FOR i := 0 TO 11 DO
ch := flags[i];
IF ch # "!" THEN
IF i IN s THEN ch := CAP( ch ) END;
w.Char( ch )
END
END;
w.String( " iopl" ); w.Int( ASH( SYSTEM.VAL( LONGINT, s * {12, 13} ), -12 ), 1 )
END Flags;
PROCEDURE Val( CONST s: ARRAY OF CHAR; val: LONGINT );
BEGIN
w.Char( " " ); w.String( s ); w.Char( "=" ); w.Hex( val, -8 )
END Val;
PROCEDURE StrAppend( VAR to : ARRAY OF CHAR; CONST this: ARRAY OF CHAR );
VAR i, j, l: LONGINT;
BEGIN
i := 0;
WHILE to[i] # 0X DO INC( i ) END;
l := LEN( to ) - 1; j := 0;
WHILE (i < l) & (this[j] # 0X) DO to[i] := this[j]; INC( i ); INC( j ) END;
to[i] := 0X
END StrAppend;
PROCEDURE StrIntToStr( val: LONGINT; VAR str: ARRAY OF CHAR );
VAR i, j: LONGINT;
digits: ARRAY 16 OF LONGINT;
BEGIN
IF val = MIN( LONGINT ) THEN COPY( "-2147483648", str ); RETURN END;
IF val < 0 THEN val := -val; str[0] := "-"; j := 1 ELSE j := 0 END;
i := 0;
REPEAT digits[i] := val MOD 10; INC( i ); val := val DIV 10 UNTIL val = 0;
DEC( i );
WHILE i >= 0 DO str[j] := CHR( digits[i] + ORD( "0" ) ); INC( j ); DEC( i ) END;
str[j] := 0X
END StrIntToStr;
PROCEDURE GetDescription;
VAR code : LONGINT; arg: ARRAY 16 OF CHAR;
BEGIN
IF exc.ExceptionCode = Kernel32.ExceptionGuardPage THEN COPY( "guard page violation", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionBreakPoint THEN
SYSTEM.GET( int.SP, code ); StrIntToStr( code, desc ); StrAppend( desc, " " );
IF code = 1 THEN StrAppend( desc, "WITH guard failed" )
ELSIF code = 2 THEN StrAppend( desc, "CASE invalid" )
ELSIF code = 3 THEN StrAppend( desc, "RETURN missing" )
ELSIF code = 5 THEN StrAppend( desc, "Implicit type guard failed" )
ELSIF code = 6 THEN StrAppend( desc, "Type guard failed" )
ELSIF code = 7 THEN StrAppend( desc, "Index out of range" )
ELSIF code = 8 THEN StrAppend( desc, "ASSERT failed" )
ELSIF code = 9 THEN StrAppend( desc, "Array dimension error" )
ELSIF code=10 THEN StrAppend(desc, "Array allocation error" );
ELSIF code = 13 THEN StrAppend( desc, "Keyboard interrupt" )
ELSIF code = 14 THEN StrAppend( desc, "Out of memory" )
ELSIF code = 15 THEN StrAppend( desc, "Deadlock (active objects)" );
ELSIF code = 23 THEN StrAppend( desc, "Exceptions.Raise" )
ELSE StrAppend( desc, "HALT statement" )
END
ELSIF exc.ExceptionCode = Kernel32.ExceptionSingleStep THEN COPY( "single step", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionAccessViolation THEN COPY( "access violation", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionIllegalInstruction THEN COPY( "illegal instruction", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionArrayBoundsExceeded THEN COPY( "index out of range", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionFltDenormalOperand THEN COPY( "FPU: denormal operand", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionFltDivideByZero THEN COPY( "FPU: divide by zero", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionFltInexactResult THEN COPY( "FPU: inexact result", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionFltInvalidOperation THEN COPY( "FPU: invalid operation", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionFltOverflow THEN COPY( "FPU: overflow", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionFltStackCheck THEN COPY( "FPU: stack check", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionFltUndeflow THEN COPY( "FPU: undeflow", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionIntDivideByZero THEN COPY( "integer division by zero", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionIntOverflow THEN COPY( "integer overflow", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionPrivInstruction THEN COPY( "privileged instruction", desc )
ELSIF exc.ExceptionCode = Kernel32.ExceptionStackOverflow THEN COPY( "stack overflow", desc )
ELSE StrIntToStr( exc.ExceptionCode, arg ); COPY( "exception ", desc ); StrAppend( desc, arg )
END
END GetDescription;
BEGIN
overflow := FALSE;
Machine.Acquire( Machine.KernelLog );
w := TrapWriters.GetWriter();
w.Update;
w.Char( 1X );
INC( trapState );
IF trapState > RecursiveLimit THEN w.String( " [Recursive TRAP]" );
trapState := 0;
ELSE
SYSTEM.GET( int.SP, code );
w.String( "TRAP " ); w.Int( code, 1 ); w.String( " [" ); w.Int( trapState, 1 ); w.String( "]" ); w.String( " PL" );
w.Int( int.CS MOD 4, 2 ); w.Char( " " ); GetDescription();
w.String( desc ); w.Ln; w.Update;
w.String( "System: " ); w.String( Machine.version );
w.String(" Kernel_CRC="); w.Hex(SystemVersion.BootCRC,8);
w.String(" Uptime="); w.Hex(Machine.GetTimer()- Machine.boottime, 8);
IF long THEN
w.Char( 0EX );
w.Ln;
w.String("Processor:");
Val( "CS", int.CS ); Val( "DS", int.DS ); Val( "ES", int.ES ); Val( "SS", int.SS );
Val( "PC", int.PC ); Val( "ESI", int.ESI ); Val( "EDI", int.EDI ); Val( "ESP", int.SP );
Val( "PID", p.id ); Val( "EAX", int.EAX ); Val( "EBX", int.EBX ); Val( "ECX", int.ECX ); Val( "EDX", int.EDX );
Val( "EBP", int.BP ); Val( "FS", int.FS ); Val( "GS", int.GS );
Val( "TMR", Kernel.GetTicks() );
IF SYSTEM.VAL( CHAR, int.DR7 ) # 0X THEN
Val( "DR0", int.DR0 ); Val( "DR1", int.DR1 ); Val( "DR2", int.DR2 ); Val( "DR3", int.DR3 ); Val( "DR6", int.DR6 );
Val( "DR7", int.DR7 ); w.Ln
END;
w.Ln; w.String( " FLAGS: " ); Flags( w, SYSTEM.VAL( SET, int.FLAGS ) );
w.Char( 0FX );
w.Char( " " ); w.Set( SYSTEM.VAL( SET, int.FLAGS ) ); w.Ln;
w.String(" Features="); w.Set(Machine.features); w.Set(Machine.features2); w.Ln;
ELSE w.Ln
END;
w.Update;
w.String( "Process:" ); Reflection.WriteProcess( w, p ); w.Ln;
IF int.PC = 0 THEN SYSTEM.GET( int.SP, pc ) ELSE pc := int.PC END;
w.String( "StackTraceBack:" ); w.Ln;
Reflection.StackTraceBack( w, pc, int.BP, Objects.GetStackBottom(p), long, overflow )
END;
w.String("---------------------------------"); w.Ln;
w.Char(02X);
w.Update;
TrapWriters.Trapped;
FINALLY
Machine.Release( Machine.KernelLog );
trapState := 0
END Show;
PROCEDURE SetLastExceptionState( ex: Kernel32.Context );
END SetLastExceptionState;
PROCEDURE HandleException( VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord; VAR handled: BOOLEAN );
VAR fp, sp, pc, handler: LONGINT;
BEGIN
fp := int.BP; sp := int.SP; pc := int.PC; handler := Modules.GetExceptionHandler( pc );
IF handler # -1 THEN
int.PC := handler; handled := TRUE; SetTrapVariable( pc, fp ); SetLastExceptionState( int )
ELSE
WHILE (fp # 0) & (handler = -1) DO
SYSTEM.GET( fp + 4, pc );
pc := pc - 1;
handler := Modules.GetExceptionHandler( pc );
sp := fp;
SYSTEM.GET( fp, fp )
END;
IF handler = -1 THEN handled := FALSE;
ELSE
int.PC := handler; int.BP := fp; int.SP := sp; SetTrapVariable( pc, fp );
SetLastExceptionState( int ); handled := TRUE
END
END
END HandleException;
PROCEDURE SetTrapVariable( pc, fp: LONGINT );
VAR varadr: LONGINT;
BEGIN
varadr := Reflection.GetVariableAdr( pc, fp, "trap" );
IF varadr # -1 THEN SYSTEM.PUT8( varadr, 1 ) END
END SetTrapVariable;
PROCEDURE Unbreakable( p: Objects.Process; VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord;
VAR handled: BOOLEAN );
VAR ebp, ebpSave, pc, handler, ebpBottom: LONGINT; hasFinally: BOOLEAN;
BEGIN
ebp := int.BP; pc := int.PC; hasFinally := FALSE;
handler := Modules.GetExceptionHandler( pc );
IF handler # -1 THEN int.PC := handler; hasFinally := TRUE; SetTrapVariable( pc, ebp ); END;
ebpSave := ebp;
WHILE (ebp # 0) DO
SYSTEM.GET( ebp, pc );
IF (pc = 0) THEN
ebpBottom := ebp;
END;
SYSTEM.GET( ebp + 4, pc );
handler := Modules.GetExceptionHandler( pc );
IF ~hasFinally THEN int.SP := ebp; END;
SYSTEM.GET( ebp, ebp );
IF (handler # -1) & (ebp # 0) THEN
IF hasFinally THEN
SYSTEM.PUT( ebpSave + 4, handler );
SYSTEM.PUT( ebpSave, ebp );
ebpSave := ebp;
ELSE int.PC := handler; int.BP := ebp; ebpSave := ebp; hasFinally := TRUE;
END;
SetTrapVariable( pc, ebp )
END
END;
IF ~hasFinally THEN
SYSTEM.GET( ebpBottom + 4, pc );
int.PC := pc; int.BP := ebpBottom;
ELSIF ebpSave # ebpBottom THEN
SYSTEM.GET( ebpBottom + 4, pc );
SYSTEM.PUT( ebpSave + 4, pc ); SetLastExceptionState( int )
END;
handled := TRUE;
END Unbreakable;
PROCEDURE Exception( VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord; VAR handled: BOOLEAN );
VAR t: Objects.Process; user, traceTrap: BOOLEAN; exchalt: LONGINT;
BEGIN
t := Objects.CurrentProcess();
check := t;
handled := FALSE;
user := (int.CS MOD 4 > 0 ); SYSTEM.GET( int.SP, exchalt );
traceTrap := FALSE;
Show( t, int, exc, TRUE );
IF exchalt = haltUnbreakable THEN Unbreakable( t, int, exc, handled )
ELSIF ~traceTrap THEN HandleException( int, exc, handled )
END;
IF ~handled THEN
IF ~traceTrap THEN
IF user THEN
IF TraceVerbose THEN
KernelLog.Enter; KernelLog.String( "Jump" ); KernelLog.Hex( t.restartPC, 9 );
KernelLog.Hex( t.restartSP, 9 );
KernelLog.Exit
END;
int.BP := 0; int.SP := t.restartSP;
int.PC := t.restartPC;
ELSE
KernelLog.Enter; KernelLog.String( "Kernel halt" ); KernelLog.Exit; Machine.Shutdown( FALSE )
END
END
END;
IF Objects.PleaseHalt IN t.flags THEN
EXCL( t.flags, Objects.PleaseHalt );
IF Objects.Unbreakable IN t.flags THEN EXCL( t.flags, Objects.Unbreakable ) END;
IF Objects.SelfTermination IN t.flags THEN EXCL( t.flags, Objects.SelfTermination ) END
END;
check := NIL;
FINALLY
END Exception;
PROCEDURE Init;
VAR
s: ARRAY 8 OF CHAR;
BEGIN
IF TestTrap THEN
Machine.GetConfig( "TestTrap", s );
IF s[0] = "1" THEN HALT( 98 ) END
END;
IF TestTrap & (s[0] = "2") THEN HALT( 99 ) END;
Objects.InstallExceptionHandler( Exception ); KernelLog.String( "Traps: TrapHandler installed" ); KernelLog.Ln;
END Init;
PROCEDURE Install*;
BEGIN
TrapWriters.InstallTraceWriter
END Install;
BEGIN
modes := " rdy run awl awc awe rip";
flags := "c!p!a!zstido";
Init
END Traps.
SystemTools.FreeDownTo Traps ~
(*
12.03.1998 pjm Started
06.08.1998 pjm Exported Show and removed AosException upcall installation & Modules lock
10.12.1998 pjm New refblk
23.06.1999 pjm State added
*)
(*
to do:
o stack overflow message is not correctly displayed in case of dynamic arrays (EDI = CR2, ESP # CR2)
o fix KernelLog.Memory calls removed when switching to Streams
o fix use of KernelLog lock in Show
o if allowing modification of variables using their descriptors, it should also have reference to module to avoid gc after free.
*)