Windows.Traps.Mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE Traps; (** AUTHOR "pjm"; PURPOSE "Trap handling and symbolic debugging"; *)
  3. IMPORT SYSTEM, Kernel32, Machine, TrapWriters, KernelLog, Streams, Modules, Objects, Kernel, Reflection, SystemVersion;
  4. CONST
  5. RecursiveLimit = 16; (* normally 1 or 2 - how many recursive traps to display before stopping *)
  6. TraceVerbose = FALSE; TestTrap = TRUE;
  7. TrapMaxCharacters = 32*1024;
  8. (* Process termination halt codes *)
  9. halt* = Objects.halt; haltUnbreakable* = Objects.haltUnbreakable;
  10. VAR
  11. modes: ARRAY 25 OF CHAR;
  12. flags: ARRAY 13 OF CHAR;
  13. trapState: LONGINT;
  14. check: Objects.Process;
  15. (** Display trap state. *)
  16. PROCEDURE Show*( p: Objects.Process; VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord; long: BOOLEAN );
  17. VAR overflow: BOOLEAN;
  18. desc: ARRAY 128 OF CHAR;
  19. code: LONGINT;
  20. pc: ADDRESS;
  21. w: Streams.Writer;
  22. (* Write flag values. *)
  23. PROCEDURE Flags( w: Streams.Writer; s: SET );
  24. VAR i: SHORTINT; ch: CHAR;
  25. BEGIN
  26. FOR i := 0 TO 11 DO
  27. ch := flags[i];
  28. IF ch # "!" THEN
  29. IF i IN s THEN ch := CAP( ch ) END;
  30. w.Char( ch )
  31. END
  32. END;
  33. w.String( " iopl" ); w.Int( ASH( SYSTEM.VAL( LONGINT, s * {12, 13} ), -12 ), 1 )
  34. END Flags;
  35. PROCEDURE Val( CONST s: ARRAY OF CHAR; val:ADDRESS );
  36. BEGIN
  37. w.Char( " " ); w.String( s ); w.Char( "=" ); w.Address(val)
  38. END Val;
  39. (** Append this to to. *)
  40. PROCEDURE StrAppend( VAR to (** in/out *) : ARRAY OF CHAR; CONST this: ARRAY OF CHAR );
  41. VAR i, j, l: LONGINT;
  42. BEGIN
  43. i := 0;
  44. WHILE to[i] # 0X DO INC( i ) END;
  45. l := LEN( to ) - 1; j := 0;
  46. WHILE (i < l) & (this[j] # 0X) DO to[i] := this[j]; INC( i ); INC( j ) END;
  47. to[i] := 0X
  48. END StrAppend;
  49. (** Convert an integer into a string. *)
  50. PROCEDURE StrIntToStr( val: LONGINT; VAR str: ARRAY OF CHAR );
  51. VAR i, j: LONGINT;
  52. digits: ARRAY 16 OF LONGINT;
  53. BEGIN
  54. IF val = MIN( LONGINT ) THEN COPY( "-2147483648", str ); RETURN END;
  55. IF val < 0 THEN val := -val; str[0] := "-"; j := 1 ELSE j := 0 END;
  56. i := 0;
  57. REPEAT digits[i] := val MOD 10; INC( i ); val := val DIV 10 UNTIL val = 0;
  58. DEC( i );
  59. WHILE i >= 0 DO str[j] := CHR( digits[i] + ORD( "0" ) ); INC( j ); DEC( i ) END;
  60. str[j] := 0X
  61. END StrIntToStr;
  62. PROCEDURE GetDescription;
  63. VAR code : LONGINT; arg: ARRAY 16 OF CHAR;
  64. BEGIN
  65. IF exc.ExceptionCode = Kernel32.ExceptionGuardPage THEN COPY( "guard page violation", desc )
  66. ELSIF exc.ExceptionCode = Kernel32.ExceptionBreakPoint THEN
  67. SYSTEM.GET( int.SP, code ); StrIntToStr( code, desc ); StrAppend( desc, " " );
  68. IF code = 1 THEN StrAppend( desc, "WITH guard failed" )
  69. ELSIF code = 2 THEN StrAppend( desc, "CASE invalid" )
  70. ELSIF code = 3 THEN StrAppend( desc, "RETURN missing" )
  71. ELSIF code = 5 THEN StrAppend( desc, "Implicit type guard failed" )
  72. ELSIF code = 6 THEN StrAppend( desc, "Type guard failed" )
  73. ELSIF code = 7 THEN StrAppend( desc, "Index out of range" )
  74. ELSIF code = 8 THEN StrAppend( desc, "ASSERT failed" )
  75. ELSIF code = 9 THEN StrAppend( desc, "Array dimension error" )
  76. ELSIF code=10 THEN StrAppend(desc, "Array allocation error" ); (* fof *)
  77. ELSIF code = 13 THEN StrAppend( desc, "Keyboard interrupt" )
  78. ELSIF code = 14 THEN StrAppend( desc, "Out of memory" )
  79. ELSIF code = 15 THEN StrAppend( desc, "Deadlock (active objects)" );
  80. ELSIF code = 16 THEN StrAppend( desc, "Procedure returned" );
  81. ELSIF code = 23 THEN StrAppend( desc, "Exceptions.Raise" )
  82. ELSE StrAppend( desc, "HALT statement" )
  83. END
  84. ELSIF exc.ExceptionCode = Kernel32.ExceptionSingleStep THEN COPY( "single step", desc )
  85. ELSIF exc.ExceptionCode = Kernel32.ExceptionAccessViolation THEN COPY( "access violation", desc )
  86. ELSIF exc.ExceptionCode = Kernel32.ExceptionIllegalInstruction THEN COPY( "illegal instruction", desc )
  87. ELSIF exc.ExceptionCode = Kernel32.ExceptionArrayBoundsExceeded THEN COPY( "index out of range", desc )
  88. ELSIF exc.ExceptionCode = Kernel32.ExceptionFltDenormalOperand THEN COPY( "FPU: denormal operand", desc )
  89. ELSIF exc.ExceptionCode = Kernel32.ExceptionFltDivideByZero THEN COPY( "FPU: divide by zero", desc )
  90. ELSIF exc.ExceptionCode = Kernel32.ExceptionFltInexactResult THEN COPY( "FPU: inexact result", desc )
  91. ELSIF exc.ExceptionCode = Kernel32.ExceptionFltInvalidOperation THEN COPY( "FPU: invalid operation", desc )
  92. ELSIF exc.ExceptionCode = Kernel32.ExceptionFltOverflow THEN COPY( "FPU: overflow", desc )
  93. ELSIF exc.ExceptionCode = Kernel32.ExceptionFltStackCheck THEN COPY( "FPU: stack check", desc )
  94. ELSIF exc.ExceptionCode = Kernel32.ExceptionFltUndeflow THEN COPY( "FPU: undeflow", desc )
  95. ELSIF exc.ExceptionCode = Kernel32.ExceptionIntDivideByZero THEN COPY( "integer division by zero", desc )
  96. ELSIF exc.ExceptionCode = Kernel32.ExceptionIntOverflow THEN COPY( "integer overflow", desc )
  97. ELSIF exc.ExceptionCode = Kernel32.ExceptionPrivInstruction THEN COPY( "privileged instruction", desc )
  98. ELSIF exc.ExceptionCode = Kernel32.ExceptionStackOverflow THEN COPY( "stack overflow", desc )
  99. ELSE StrIntToStr( exc.ExceptionCode, arg ); COPY( "exception ", desc ); StrAppend( desc, arg )
  100. END
  101. END GetDescription;
  102. BEGIN
  103. overflow := FALSE;
  104. Machine.Acquire( Machine.KernelLog ); (* like KernelLog.Enter, but without output *)
  105. w := TrapWriters.GetWriter();
  106. w.Update; (* flush previous output stuck in global writer w *)
  107. w.Char( 1X ); (* "start of trap" *)
  108. INC( trapState );
  109. IF trapState > RecursiveLimit THEN w.String( " [Recursive TRAP]" );
  110. trapState := 0;
  111. ELSE
  112. (* output first line *)
  113. SYSTEM.GET( int.SP, code );
  114. w.String( "TRAP " ); w.Int( code, 1 ); w.String( " [" ); w.Int( trapState, 1 ); w.String( "]" ); w.String( " PL" );
  115. w.Int( int.CS MOD 4, 2 ); w.Char( " " ); GetDescription();
  116. w.String( desc ); w.Ln; w.Update;
  117. w.String( "System: " ); w.String( Machine.version );
  118. w.String(" Kernel_CRC="); w.Hex(SystemVersion.BootCRC,8);
  119. w.String(" Uptime="); w.Hex(Machine.GetTimer()- Machine.boottime, 8);
  120. IF long THEN
  121. w.Char( 0EX ); (* "fixed font" *)
  122. w.Ln;
  123. w.String("Processor:");
  124. (* output values *)
  125. Val( "CS", int.CS ); Val( "DS", int.DS ); Val( "ES", int.ES ); Val( "SS", int.SS );
  126. Val( "PC", int.PC );
  127. #IF I386 THEN
  128. Val( "ESI", int.ESI ); Val( "EDI", int.EDI ); Val( "ESP", int.SP );
  129. Val( "PID", p.id ); Val( "EAX", int.EAX ); Val( "EBX", int.EBX ); Val( "ECX", int.ECX ); Val( "EDX", int.EDX );
  130. #ELSIF AMD64 THEN
  131. Val( "ESI", int.RSI ); Val( "EDI", int.RDI ); Val( "ESP", int.SP );
  132. Val( "PID", p.id ); Val( "EAX", int.RAX ); Val( "EBX", int.RBX ); Val( "ECX", int.RCX ); Val( "EDX", int.RDX );
  133. Val( "ESI", int.RSI ); Val( "EDI", int.RDI ); Val( "ESP", int.SP );
  134. #ELSE
  135. ASSERT(FALSE);
  136. #END
  137. Val( "EBP", int.BP ); Val( "FS", int.FS ); Val( "GS", int.GS );
  138. Val( "TMR", Kernel.GetTicks());
  139. #IF I386 THEN
  140. IF SYSTEM.VAL( CHAR, int.DR7 ) # 0X THEN (* some breakpoints enabled *)
  141. Val( "DR0", int.DR0 ); Val( "DR1", int.DR1 ); Val( "DR2", int.DR2 ); Val( "DR3", int.DR3 ); Val( "DR6", int.DR6 );
  142. Val( "DR7", int.DR7 ); w.Ln
  143. END;
  144. #END
  145. w.Ln; w.String( " FLAGS: " ); Flags( w, SYSTEM.VAL( SET, int.FLAGS ) );
  146. w.Char( 0FX ); (* "proportional font" *)
  147. w.Char( " " ); w.Set( SYSTEM.VAL( SET, int.FLAGS ) ); w.Ln;
  148. w.String(" Features="); w.Set(Machine.features); w.Set(Machine.features2); w.Ln;
  149. ELSE w.Ln
  150. END;
  151. w.Update;
  152. w.String( "Process:" ); Reflection.WriteProcess( w, p ); w.Ln;
  153. IF int.PC = 0 THEN SYSTEM.GET( int.SP, pc ) ELSE pc := int.PC END;
  154. w.String( "StackTraceBack:" ); w.Ln;
  155. Reflection.StackTraceBack( w, pc, int.BP, int.SP, Objects.GetStackBottom(p), long, overflow )
  156. END;
  157. w.String("---------------------------------"); w.Ln;
  158. w.Char(02X); (* "end of trap" *)
  159. w.Update;
  160. TrapWriters.Trapped;
  161. FINALLY
  162. Machine.Release( Machine.KernelLog ); (* like KernelLog.Exit, but without output *)
  163. trapState := 0
  164. END Show;
  165. PROCEDURE SetLastExceptionState( ex: Kernel32.Context );
  166. END SetLastExceptionState;
  167. PROCEDURE CheckBP(fp: ADDRESS): ADDRESS;
  168. VAR n: ADDRESS;
  169. BEGIN
  170. IF (fp # NIL) THEN
  171. SYSTEM.GET(fp, n);
  172. IF ODD(n) THEN RETURN fp + SIZEOF(ADDRESS) END;
  173. END;
  174. RETURN fp;
  175. END CheckBP;
  176. (** Handles an exception. Interrupts are on during this procedure. *)
  177. PROCEDURE HandleException( VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord; VAR handled: BOOLEAN );
  178. VAR fp, newFP, sp, pc, handler: ADDRESS;
  179. BEGIN
  180. fp := int.BP; sp := int.SP; pc := int.PC; handler := Modules.GetExceptionHandler( pc );
  181. IF handler # -1 THEN (* Handler in the current PAF *)
  182. int.PC := handler; handled := TRUE; SetTrapVariable( pc, fp ); SetLastExceptionState( int )
  183. ELSE
  184. WHILE (fp # 0) & (handler = -1) DO
  185. fp := CheckBP(fp);
  186. SYSTEM.GET( fp + SIZEOF(ADDRESS), pc );
  187. pc := pc - 1; (* CALL instruction, machine dependant!!! *)
  188. handler := Modules.GetExceptionHandler( pc );
  189. sp := fp; (* Save the old framepointer into the stack pointer *)
  190. SYSTEM.GET( fp, fp ) (* Unwind PAF *)
  191. END;
  192. IF handler = -1 THEN handled := FALSE;
  193. ELSE
  194. int.PC := handler; int.BP := fp; int.SP := sp; SetTrapVariable( pc, fp );
  195. SetLastExceptionState( int ); handled := TRUE
  196. END
  197. END
  198. END HandleException;
  199. PROCEDURE SetTrapVariable( pc, fp: ADDRESS );
  200. VAR varadr: ADDRESS;
  201. BEGIN
  202. varadr := Reflection.GetVariableAdr( pc, fp, "trap" );
  203. IF varadr # -1 THEN SYSTEM.PUT8( varadr, 1 ) END
  204. END SetTrapVariable;
  205. (* Unbreakable stack trace back with regard to every FINALLY on the way *)
  206. PROCEDURE Unbreakable( p: Objects.Process; VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord;
  207. VAR handled: BOOLEAN );
  208. VAR ebp, ebpSave, pc, handler, ebpBottom: ADDRESS; checkedBP: ADDRESS; hasFinally: BOOLEAN;
  209. BEGIN
  210. ebp := int.BP; pc := int.PC; hasFinally := FALSE;
  211. handler := Modules.GetExceptionHandler( pc );
  212. (* Handler in the current PAF *)
  213. IF handler # -1 THEN int.PC := handler; hasFinally := TRUE; SetTrapVariable( pc, ebp ); END;
  214. (* The first waypoint is the ebp of the top PAF *)
  215. ebpSave := CheckBP(ebp);
  216. WHILE (ebp # 0) DO
  217. (* Did we reach the last PAF? *)
  218. checkedBP := CheckBP(ebp);
  219. SYSTEM.GET( checkedBP, pc );
  220. IF (pc = 0) THEN
  221. ebpBottom := ebp; (* Save the FP of the last PAF *)
  222. END;
  223. (* Get the return pc *)
  224. SYSTEM.GET( checkedBP + SIZEOF(ADDRESS), pc );
  225. handler := Modules.GetExceptionHandler( pc );
  226. (* Save the last framepointer as stackpointer *)
  227. IF ~hasFinally THEN int.SP := ebp; END;
  228. SYSTEM.GET( checkedBP, ebp );
  229. (* Here ebp may be 0. *)
  230. IF (handler # -1) & (ebp # 0) THEN (* If Objects.Terminate has a FINALLY this doesn't work !!! *)
  231. IF hasFinally THEN
  232. (* Connect Finally to Finally *)
  233. SYSTEM.PUT( ebpSave + SIZEOF(ADDRESS), handler ); (* Adapt the return pc *)
  234. SYSTEM.PUT( ebpSave, ebp ); (* Adapt the dynamic link *)
  235. ebpSave := checkedBP;
  236. ELSE
  237. int.PC := handler; int.BP := ebp; ebpSave := checkedBP; hasFinally := TRUE;
  238. END;
  239. SetTrapVariable( pc, ebp )
  240. END
  241. END;
  242. (* Now ebp = 0, bottom of the stack, so link the last known return PC to the Termination *)
  243. IF ~hasFinally THEN
  244. SYSTEM.GET( ebpBottom + SIZEOF(ADDRESS), pc ); (* PC of the Terminate *)
  245. int.PC := pc; int.BP := ebpBottom;
  246. ELSIF ebpSave # ebpBottom THEN
  247. SYSTEM.GET( ebpBottom + SIZEOF(ADDRESS), pc ); (* PC of the Terminate *)
  248. SYSTEM.PUT( ebpSave + SIZEOF(ADDRESS), pc ); SetLastExceptionState( int )
  249. END;
  250. handled := TRUE; (* If FALSE the process could be restarted, may be this is the meaning? *)
  251. END Unbreakable;
  252. (* General exception handler. *)
  253. PROCEDURE Exception( VAR int: Kernel32.Context; VAR exc: Kernel32.ExceptionRecord; VAR handled: BOOLEAN );
  254. VAR t: Objects.Process; user, traceTrap: BOOLEAN; exchalt: LONGINT;
  255. BEGIN (* interrupts off *)
  256. t := Objects.CurrentProcess();
  257. check := t;
  258. handled := FALSE;
  259. user := (int.CS MOD 4 > 0 (* Machine.KernelLevel*) ); SYSTEM.GET( int.SP, exchalt );
  260. traceTrap := FALSE;
  261. Show( t, int, exc, (* exc.halt # MAX(INTEGER)+1*) TRUE ); (* Always show the trap info!*)
  262. IF exchalt = haltUnbreakable THEN Unbreakable( t, int, exc, handled )
  263. ELSIF ~traceTrap THEN HandleException( int, exc, handled )
  264. END;
  265. IF ~handled THEN
  266. (* Taken from Machine to allow the FINALLY in the kernel *)
  267. IF ~traceTrap THEN (* trap *)
  268. IF user THEN (* return to outer level *)
  269. IF TraceVerbose THEN
  270. KernelLog.Enter; KernelLog.String( "Jump" ); KernelLog.Hex( t.restartPC, 9 );
  271. KernelLog.Hex( t.restartSP, 9 ); (* KernelLog.Hex(t.stack.high, 9);*)
  272. KernelLog.Exit
  273. END;
  274. int.BP := 0; int.SP := t.restartSP; (* reset stack *)
  275. int.PC := t.restartPC; (* restart object body or terminate *)
  276. ELSE (* trap was in kernel (interrupt handler) *) (* fixme: recover from trap in stack traceback *)
  277. KernelLog.Enter; KernelLog.String( "Kernel halt" ); KernelLog.Exit; Machine.Shutdown( FALSE )
  278. END
  279. END
  280. END;
  281. IF Objects.PleaseHalt IN t.flags THEN
  282. EXCL( t.flags, Objects.PleaseHalt );
  283. IF Objects.Unbreakable IN t.flags THEN EXCL( t.flags, Objects.Unbreakable ) END;
  284. IF Objects.SelfTermination IN t.flags THEN EXCL( t.flags, Objects.SelfTermination ) END
  285. END;
  286. check := NIL;
  287. FINALLY
  288. (* if trap occurs in this procedure, then go on working right here *)
  289. END Exception;
  290. PROCEDURE Init;
  291. VAR
  292. s: ARRAY 8 OF CHAR;
  293. BEGIN
  294. IF TestTrap THEN
  295. Machine.GetConfig( "TestTrap", s );
  296. IF s[0] = "1" THEN HALT( 98 ) END
  297. END;
  298. IF TestTrap & (s[0] = "2") THEN HALT( 99 ) END;
  299. Objects.InstallExceptionHandler( Exception );
  300. END Init;
  301. PROCEDURE Install*; (* for loading this module *)
  302. BEGIN
  303. TrapWriters.InstallTraceWriter
  304. END Install;
  305. BEGIN
  306. modes := " rdy run awl awc awe rip"; (* 4 characters per mode from Objects.Ready to Objects.Terminated *)
  307. flags := "c!p!a!zstido"; (* bottom flags, !=reserved *)
  308. Init
  309. END Traps.