Reflection.Mod 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. MODULE Reflection; (** AUTHOR "fof"; PURPOSE "tools for module, stack and process reflection"; *)
  2. IMPORT Modules,Streams,Machine,Heaps,Objects,Kernel,Trace,SYSTEM;
  3. CONST
  4. ShowAllProcs = TRUE;
  5. MaxFrames = 128;
  6. MaxString = 64;
  7. MaxArray = 8;
  8. MaxCols = 70;
  9. Sep = " ";
  10. SepLen = 2;
  11. LineDelay = 0; (* set this value to the number of cycles of an empty for loop that the reflection should wait after a new line
  12. * useful for screen racing when no persistent trace medium is available
  13. * no timer mechanism used because at low level tracing this may exactly not be available because IRQs do not work any more or so.
  14. *)
  15. TYPE
  16. Variable* = RECORD
  17. adr-: ADDRESS;
  18. type-, size-, n-, tdadr-: LONGINT
  19. END;
  20. VAR
  21. modes: ARRAY 25 OF CHAR;
  22. (*
  23. Reference = {OldRef | ProcRef} .
  24. OldRef = 0F8X offset/n name/s {Variable} .
  25. ProcRef = 0F9X offset/n nofPars/n RetType procLev/1 slFlag/1 name/s {Variable} .
  26. RetType = 0X | Var | ArrayType | Record .
  27. ArrayType = 12X | 14X | 15X . (* static array, dynamic array, open array *)
  28. Record = 16X .
  29. Variable = VarMode (Var | ArrayVar | RecordVar ) offset/n name/s .
  30. VarMode = 1X | 3X . (* direct, indirect *)
  31. Var = 1X .. 0FX . (* byte, boolean, char, shortint, integer, longint, real, longreal, set, ptr, proc, string *)
  32. ArrayVar = (81X .. 8EX) dim/n . (* byte, boolean, char, shortint, integer, longint, real, longreal, set, ptr, proc *)
  33. RecordVar = (16X | 1DX) tdadr/n . (* record, recordpointer *)
  34. *)
  35. (** Write a variable value. The v parameter is a variable descriptor obtained with NextVar. Parameter col is incremented with the (approximate) number of characters written. *)
  36. PROCEDURE WriteVar*(w: Streams.Writer; VAR v: Variable; VAR col: LONGINT);
  37. VAR ch: CHAR;
  38. BEGIN
  39. IF v.type = 15 THEN
  40. w.Char(22X);
  41. LOOP
  42. IF (v.n = 0) OR (~CheckHeapAddress(v.adr)) THEN EXIT END;
  43. SYSTEM.GET(v.adr, ch);
  44. INC(v.adr);
  45. IF (ch < " ") OR (ch > "~") THEN EXIT END;
  46. w.Char(ch); INC(col); DEC(v.n)
  47. END;
  48. w.Char(22X); INC(col, 2);
  49. IF ch # 0X THEN w.Char("!") END
  50. ELSE
  51. WHILE v.n > 0 DO
  52. WriteSimpleVar(w, v.adr, v.type, v.tdadr, col);
  53. DEC(v.n); INC(v.adr, v.size);
  54. IF v.n > 0 THEN
  55. w.String(", "); INC(col, 2)
  56. END
  57. END
  58. END
  59. END WriteVar;
  60. PROCEDURE CheckHeapAddress(address: ADDRESS): BOOLEAN;
  61. BEGIN
  62. RETURN Machine.ValidHeapAddress(address);
  63. END CheckHeapAddress;
  64. (* Get a compressed refblk number. *)
  65. PROCEDURE GetNum( refs: Modules.Bytes; VAR i, num: LONGINT );
  66. VAR
  67. n, s, x: LONGINT;
  68. BEGIN
  69. IF NewObjectFile(refs) THEN
  70. (* Copying byte by byte to avoid unaligned memory accesses on ARM *)
  71. SYSTEM.PUT8(ADDRESSOF(num), refs[i]);
  72. SYSTEM.PUT8(ADDRESSOF(num) + 1, refs[i + 1]);
  73. SYSTEM.PUT8(ADDRESSOF(num) + 2, refs[i + 2]);
  74. SYSTEM.PUT8(ADDRESSOF(num) + 3, refs[i + 3]);
  75. INC(i,4);
  76. ELSE
  77. s := 0; n := 0; x := ORD(refs[i]); INC( i );
  78. WHILE x >= 128 DO INC( n, ASH( x - 128, s ) ); INC( s, 7 ); x := ORD(refs[i]); INC( i ) END;
  79. num := n + ASH( x MOD 64 - x DIV 64 * 64, s )
  80. END;
  81. END GetNum;
  82. (** Step to the next variable in the refs block. The name parameter returns empty if no more variables are found. The attributes are returned in v. Parameter refpos is modified. *)
  83. PROCEDURE NextVar*(refs: Modules.Bytes; VAR refpos: LONGINT; base: ADDRESS; VAR name: ARRAY OF CHAR; VAR v: Variable);
  84. VAR x: Variable; j: LONGINT; ch, mode: CHAR;
  85. BEGIN
  86. name[0] := 0X; (* empty name signals end or error *)
  87. IF refpos < LEN(refs^)-1 THEN
  88. mode := refs[refpos]; INC(refpos);
  89. IF (mode >= 1X) & (mode <= 3X) THEN (* var *)
  90. x.type := ORD(refs[refpos]); INC(refpos);
  91. IF x.type > 80H THEN
  92. IF x.type = 83H THEN x.type := 15 ELSE DEC(x.type, 80H) END;
  93. GetNum(refs, refpos, x.n)
  94. ELSIF (x.type = 16H) OR (x.type = 1DH) THEN
  95. GetNum(refs, refpos, x.tdadr); x.n := 1
  96. ELSE
  97. IF x.type = 15 THEN x.n := MaxString (* best guess *) ELSE x.n := 1 END
  98. END;
  99. (* get address *)
  100. GetNum(refs, refpos, j);
  101. x.adr := base + j; (* convert to absolute address *)
  102. IF x.n = 0 THEN (* open array (only on stack, not global variable) *)
  103. SYSTEM.GET(x.adr+4, x.n) (* real LEN from stack *)
  104. END;
  105. IF mode # 1X THEN SYSTEM.GET(x.adr, x.adr) END; (* indirect *)
  106. (* get size *)
  107. CASE x.type OF
  108. 1..4,15: x.size := 1
  109. |5: x.size := 2
  110. |6..7,9,13,14,29: x.size := 4
  111. |8, 16: x.size := 8
  112. |22: x.size := 0; ASSERT(x.n <= 1)
  113. ELSE x.size := -1
  114. END;
  115. IF x.size >= 0 THEN (* ok, get name *)
  116. ch := refs[refpos]; INC(refpos); j := 0;
  117. WHILE ch # 0X DO
  118. IF j < LEN(name)-1 THEN name[j] := ch; INC(j) END; (* truncate long names *)
  119. ch := refs[refpos]; INC(refpos)
  120. END;
  121. name[j] := 0X; v := x (* non-empty name *)
  122. END
  123. END
  124. END
  125. END NextVar;
  126. (** Find the specified global variable and return its descriptor. Returns TRUE iff the variable is found. *)
  127. PROCEDURE FindVar*(mod: Modules.Module; CONST name: ARRAY OF CHAR; VAR v: Variable): BOOLEAN;
  128. VAR refs: Modules.Bytes; refpos: LONGINT; base: ADDRESS; n: ARRAY 64 OF CHAR;
  129. BEGIN
  130. InitVar(mod, refs, refpos, base);
  131. IF refpos # -1 THEN
  132. LOOP
  133. NextVar(refs, refpos, base, n, v);
  134. IF n = "" THEN EXIT END;
  135. IF n = name THEN RETURN TRUE END
  136. END
  137. END;
  138. RETURN FALSE
  139. END FindVar;
  140. (** Find global variables of mod (which may be NIL) and return it in the refs, refpos and base parameters for use by NextVar. If not found, refpos returns -1. *)
  141. PROCEDURE InitVar*(mod: Modules.Module; VAR refs: Modules.Bytes; VAR refpos: LONGINT; VAR base: ADDRESS);
  142. VAR ch: CHAR; startpc,pc,end: ADDRESS;
  143. BEGIN
  144. refpos := -1;
  145. IF mod # NIL THEN
  146. refs := mod.refs; base := mod.sb;
  147. IF (refs # NIL) & (LEN(refs) # 0) THEN
  148. IF FindProcByName(mod,"$$",pc,end) THEN
  149. refpos := FindProc(refs, pc, startpc);
  150. END;
  151. IF refpos # -1 THEN
  152. ch := refs[refpos]; INC(refpos);
  153. WHILE ch # 0X DO ch := refs[refpos]; INC(refpos) END
  154. END
  155. END
  156. END
  157. END InitVar;
  158. PROCEDURE NewObjectFile(refs: Modules.Bytes): BOOLEAN;
  159. BEGIN
  160. RETURN (refs # NIL) & (LEN(refs) >0) & (refs[0]=0FFX);
  161. END NewObjectFile;
  162. (* Find a procedure in the reference block. Return index of name, or -1 if not found. *)
  163. PROCEDURE FindProc(refs: Modules.Bytes; modpc: ADDRESS; VAR startpc: ADDRESS): LONGINT;
  164. VAR pos, len, t, tstart, tend, proc: LONGINT; ch: CHAR; newObjectFile, found: BOOLEAN;
  165. BEGIN
  166. IF (refs=NIL) OR (LEN(refs) = 0) THEN RETURN -1 END;
  167. newObjectFile := NewObjectFile(refs);
  168. proc := -1; pos := 0; len := LEN(refs^);
  169. IF newObjectFile THEN INC(pos) END;
  170. ch := refs[pos]; INC(pos); tstart := 0;
  171. found := FALSE;
  172. WHILE ~found & (pos < len) & ((ch = 0F8X) OR (ch = 0F9X)) DO (* proc *)
  173. GetNum(refs, pos, tstart); (* procedure offset *)
  174. IF newObjectFile THEN
  175. GetNum(refs,pos,tend);
  176. found := (tstart <=modpc) & (tend > modpc)
  177. ELSE
  178. found := tstart > modpc
  179. END;
  180. IF ch = 0F9X THEN
  181. GetNum(refs, pos, t); (* nofPars *)
  182. INC(pos, 3) (* RetType, procLev, slFlag *);
  183. IF newObjectFile THEN INC(pos,6) END;
  184. END;
  185. IF ~found THEN (* not yet found -- remember startpc and position for next iteration *)
  186. startpc := tstart;
  187. proc := pos; (* remember this position, just before the name *)
  188. REPEAT ch := refs[pos]; INC(pos) UNTIL ch = 0X; (* pname *)
  189. IF pos < len THEN
  190. ch := refs[pos]; INC(pos); (* 1X | 3X | 0F8X | 0F9X *)
  191. WHILE (pos < len) & (ch >= 1X) & (ch <= 3X) DO (* var *)
  192. ch := refs[pos]; INC(pos); (* type *)
  193. IF (ch >= 81X) OR (ch = 16X) OR (ch = 1DX) THEN
  194. GetNum(refs, pos, t) (* dim/tdadr *)
  195. END;
  196. GetNum(refs, pos, t); (* vofs *)
  197. REPEAT ch := refs[pos]; INC(pos) UNTIL ch = 0X; (* vname *)
  198. IF pos < len THEN ch := refs[pos]; INC(pos) END (* 1X | 3X | 0F8X | 0F9X *)
  199. END
  200. END
  201. END;
  202. END;
  203. IF newObjectFile THEN
  204. IF found THEN
  205. startpc := tstart; proc := pos;
  206. ELSE proc := -1
  207. END;
  208. END;
  209. RETURN proc
  210. END FindProc;
  211. (* Find a procedure in the reference block. Return index of name, or -1 if not found. *)
  212. PROCEDURE FindProcByName*(mod: Modules.Module; CONST name: ARRAY OF CHAR; VAR from, to: ADDRESS): BOOLEAN;
  213. VAR i, namePos, m, t, temp: LONGINT; ch: CHAR; newObjectFile: BOOLEAN;
  214. refs: Modules.Bytes; success: BOOLEAN;
  215. tstart, tend: ADDRESS;
  216. BEGIN
  217. IF mod = NIL THEN RETURN FALSE END;
  218. refs := mod.refs;
  219. IF (refs=NIL) OR (LEN(refs) = 0) THEN RETURN FALSE END;
  220. newObjectFile := NewObjectFile(refs);
  221. i := 0; m := LEN(refs^);
  222. IF newObjectFile THEN INC(i) END;
  223. ch := refs[i]; INC(i); tstart := 0;
  224. success := FALSE;
  225. WHILE (i < m) & ((ch = 0F8X) OR (ch = 0F9X)) & ~success DO (* proc *)
  226. GetNum(refs, i, temp); (* pofs *)
  227. tstart := temp;
  228. IF newObjectFile THEN GetNum(refs,i,temp); tend := temp END;
  229. IF ch = 0F9X THEN
  230. GetNum(refs, i, t); (* nofPars *)
  231. INC(i, 3) (* RetType, procLev, slFlag *);
  232. IF newObjectFile THEN INC(i,6) END;
  233. END;
  234. namePos := 0; success := TRUE;
  235. REPEAT ch := refs[i]; INC(i); success := success & (ch = name[namePos]); INC(namePos); UNTIL ch = 0X; (* pname *)
  236. IF i < m THEN
  237. ch := refs[i]; INC(i); (* 1X | 3X | 0F8X | 0F9X *)
  238. WHILE (i < m) & (ch >= 1X) & (ch <= 3X) DO (* var *)
  239. ch := refs[i]; INC(i); (* type *)
  240. IF (ch >= 81X) OR (ch = 16X) OR (ch = 1DX) THEN
  241. GetNum(refs, i, t) (* dim/tdadr *)
  242. END;
  243. GetNum(refs, i, t); (* vofs *)
  244. REPEAT ch := refs[i]; INC(i) UNTIL ch = 0X; (* vname *)
  245. IF i < m THEN ch := refs[i]; INC(i) END (* 1X | 3X | 0F8X | 0F9X *)
  246. END
  247. END;
  248. END;
  249. IF success & ~newObjectFile THEN
  250. IF (ch = 0F8X) OR (ch = 0F9X) THEN
  251. GetNum(refs, i, temp); tend := temp;
  252. ELSE
  253. tend :=LEN(mod.code);
  254. END;
  255. INC(tstart, ADDRESSOF(mod.code[0]));
  256. INC(tend, ADDRESSOF(mod.code[0]));
  257. END;
  258. from := tstart; to := tend;
  259. RETURN success
  260. END FindProcByName;
  261. PROCEDURE Wait(w: Streams.Writer);
  262. VAR i: LONGINT;
  263. BEGIN
  264. IF LineDelay > 0 THEN
  265. FOR i := 0 TO LineDelay DO END;
  266. w.Update
  267. END;
  268. END Wait;
  269. (* Display variables. *)
  270. PROCEDURE Variables(w: Streams.Writer; refs: Modules.Bytes; refpos: LONGINT; base: ADDRESS);
  271. VAR v: Variable; j, col: LONGINT; name: ARRAY 64 OF CHAR; etc: BOOLEAN;
  272. CONST dense = FALSE;
  273. BEGIN
  274. LOOP
  275. NextVar(refs, refpos, base, name, v);
  276. IF name[0] = 0X THEN EXIT END;
  277. (* write name *)
  278. IF (col # 0 ) & (v.n > 1) & (v.type # 15) THEN (* Ln before array (except string) *)
  279. w.Ln; col := 0; Wait(w);
  280. END;
  281. w.String(Sep); w.String(name); w.Char("=");
  282. j := 0; WHILE name[j] # 0X DO INC(j) END;
  283. INC(col, SepLen+1+j);
  284. (* write variable *)
  285. IF (v.adr >= -4) & (v.adr < 4096) THEN (* must be NIL VAR parameter *)
  286. w.String("NIL ("); w.Hex(v.adr, -8);
  287. w.Char(")"); INC(col, 14)
  288. ELSE
  289. etc := FALSE;
  290. IF v.type = 15 THEN
  291. IF v.n > MaxString THEN etc := TRUE; v.n := MaxString END
  292. ELSE
  293. IF v.n > MaxArray THEN etc := TRUE; v.n := MaxArray END
  294. END;
  295. IF (v.type <= 8) & ((v.adr > base + 64) OR (v.adr < base - 4096)) & (~CheckHeapAddress( v.adr )) THEN
  296. (* CONST parameter ? *)
  297. w.String( "???" )
  298. ELSE
  299. WriteVar(w, v, col); (* write value *)
  300. END;
  301. IF etc THEN w.String("..."); INC(col, 3) END;
  302. IF ~dense THEN
  303. w.Ln; col := 0; Wait(w);
  304. END;
  305. END;
  306. IF col > MaxCols THEN w.Ln; col := 0; Wait(w); END
  307. END;
  308. IF col # 0 THEN w.Ln; Wait(w) END
  309. END Variables;
  310. (** Write the state of the specified module. *)
  311. PROCEDURE ModuleState*(w: Streams.Writer; mod: Modules.Module);
  312. VAR refpos: LONGINT; base: ADDRESS; refs: Modules.Bytes;
  313. BEGIN
  314. InitVar(mod, refs, refpos, base);
  315. IF refpos # -1 THEN
  316. w.String("State "); w.String(mod.name); w.Char(":"); w.Ln; Wait(w);
  317. Variables(w, refs, refpos, base)
  318. END
  319. END ModuleState;
  320. (* Write the specified procedure name and returns parameters for use with NextVar and Variables. *)
  321. PROCEDURE WriteProc0(w: Streams.Writer; mod: Modules.Module; pc, fp: ADDRESS; VAR refs: Modules.Bytes;
  322. VAR refpos: LONGINT; VAR base: ADDRESS);
  323. VAR ch: CHAR; startpc: ADDRESS;
  324. BEGIN
  325. refpos := -1;
  326. IF mod = NIL THEN
  327. IF pc = 0 THEN w.String("NIL")
  328. ELSE
  329. w.String("Unknown PC="); w.Address(pc); w.Char("H")
  330. END;
  331. IF fp # -1 THEN
  332. w.String(" FP="); w.Address(fp); w.Char("H")
  333. END
  334. ELSE
  335. w.String(mod.name);
  336. IF ~NewObjectFile(mod.refs) THEN
  337. DEC(pc, ADDRESSOF(mod.code[0]));
  338. END;
  339. refs := mod.refs;
  340. IF (refs # NIL) & (LEN(refs) # 0) THEN
  341. refpos := FindProc(refs, pc, startpc);
  342. IF refpos # -1 THEN
  343. w.Char(".");
  344. ch := refs[refpos]; INC(refpos);
  345. IF ch = "$" THEN base := mod.sb ELSE base := fp END; (* for variables *)
  346. WHILE ch # 0X DO w.Char(ch); ch := refs[refpos]; INC(refpos) END;
  347. w.Char(":"); w.Int(LONGINT(pc-startpc),1);
  348. END
  349. END;
  350. w.String(" pc="); w.Int(LONGINT(pc),1); w.String(" ["); w.Address (pc); w.String("H]");
  351. w.String(" = "); w.Int(LONGINT(startpc),1); w.String(" + "); w.Int(LONGINT(pc-startpc),1);
  352. w.String(" crc="); w.Hex(mod.crc,-8);
  353. Wait(w);
  354. END
  355. END WriteProc0;
  356. (** Find procedure name and write it. *)
  357. PROCEDURE WriteProc*(w: Streams.Writer; pc: ADDRESS);
  358. VAR refs: Modules.Bytes; refpos: LONGINT; base: ADDRESS;
  359. BEGIN
  360. WriteProc0(w, Modules.ThisModuleByAdr0(pc), pc, -1, refs, refpos, base)
  361. END WriteProc;
  362. (* Returns the name of the procedure the pc is in. Searchs in m.refs *)
  363. PROCEDURE GetProcedureName*(pc: ADDRESS; VAR name: ARRAY OF CHAR; VAR startpc: ADDRESS);
  364. VAR
  365. methadr, i: LONGINT;
  366. ch: CHAR;
  367. m: Modules.Module;
  368. BEGIN
  369. m := Modules.ThisModuleByAdr0(pc);
  370. IF m # NIL THEN
  371. IF ~NewObjectFile(m.refs) THEN
  372. DEC(pc, ADDRESSOF(m.code[0]));
  373. END;
  374. methadr := FindProc(m.refs, pc, startpc);
  375. IF methadr # -1 THEN
  376. i := 0;
  377. ch := m.refs[methadr]; INC(methadr);
  378. WHILE ch # 0X DO
  379. name[i] := ch;
  380. ch := m.refs[methadr];
  381. INC(methadr);
  382. INC(i);
  383. END;
  384. IF ~NewObjectFile(m.refs) THEN
  385. INC(startpc, ADDRESSOF(m.code[0]));
  386. END;
  387. END;
  388. name[i] := 0X;
  389. ELSE
  390. name := "Unkown"; (* Better: name := "" *)
  391. END;
  392. END GetProcedureName;
  393. (* A simple introspection method, must be adapted if there are any changes to the
  394. refs section in a module. *)
  395. PROCEDURE GetVariableAdr*(pc, fp: ADDRESS; CONST varname: ARRAY OF CHAR): ADDRESS;
  396. VAR
  397. m: Modules.Module;
  398. v: Variable;
  399. pos: LONGINT;
  400. base: ADDRESS;
  401. name: ARRAY 256 OF CHAR;
  402. ch: CHAR;
  403. startpc: ADDRESS;
  404. BEGIN
  405. pos := -1;
  406. m := Modules.ThisModuleByAdr0(pc);
  407. IF m # NIL THEN
  408. IF ~NewObjectFile(m.refs) THEN
  409. DEC(pc, ADDRESSOF(m.code[0]));
  410. END;
  411. pos := FindProc(m.refs, pc, startpc);
  412. IF pos # -1 THEN
  413. ch := m.refs[pos]; INC(pos);
  414. (* for variables *)
  415. IF ch = "$" THEN
  416. base := m.sb;
  417. ELSE
  418. base := fp;
  419. END;
  420. (* Read the name *)
  421. WHILE ch # 0X DO ch := m.refs[pos]; INC(pos) END;
  422. NextVar(m.refs, pos, base, name, v);
  423. WHILE name[0] # 0X DO
  424. IF name = varname THEN
  425. RETURN v.adr;
  426. ELSE
  427. NextVar(m.refs, pos, base, name, v);
  428. END
  429. END
  430. END
  431. END;
  432. RETURN -1;
  433. END GetVariableAdr;
  434. (* "lock free" version of Modules.ThisTypeByAdr *)
  435. PROCEDURE ThisTypeByAdr(adr: ADDRESS; VAR m: Modules.Module; VAR t: Modules.TypeDesc);
  436. BEGIN
  437. IF adr # 0 THEN
  438. SYSTEM.GET (adr + Heaps.TypeDescOffset, adr);
  439. IF CheckHeapAddress(adr) THEN
  440. t := SYSTEM.VAL(Modules.TypeDesc, adr);
  441. m := t.mod;
  442. ELSE
  443. m := NIL; t := NIL
  444. END
  445. ELSE
  446. m := NIL; t := NIL
  447. END
  448. END ThisTypeByAdr;
  449. PROCEDURE WriteType*(w: Streams.Writer; adr: ADDRESS);
  450. VAR module: Modules.Module; typeDesc: Modules.TypeDesc;
  451. BEGIN
  452. IF CheckHeapAddress(adr) THEN
  453. ThisTypeByAdr(adr, module, typeDesc);
  454. IF module # NIL THEN
  455. w.String(module.name);
  456. ELSE
  457. w.String("NIL"); RETURN
  458. END;
  459. w.String(".");
  460. IF typeDesc # NIL THEN
  461. IF typeDesc.name = "" THEN
  462. w.String("ANONYMOUS")
  463. ELSE
  464. w.String(typeDesc.name);
  465. END;
  466. ELSE
  467. w.String("NIL");
  468. END;
  469. ELSE
  470. w.String("UNKNOWN");
  471. END;
  472. END WriteType;
  473. PROCEDURE WriteSimpleVar( w: Streams.Writer; adr, type, tdadr: ADDRESS; VAR col: LONGINT );
  474. VAR ch: CHAR; sval: SHORTINT; ival: INTEGER; lval: LONGINT; rval: REAL; xval: LONGREAL; hval : HUGEINT;
  475. address: ADDRESS; pos0: LONGINT; setval: SET;
  476. BEGIN
  477. pos0 := w.Pos();
  478. IF (adr # 0) OR (type = 22) THEN
  479. CASE type OF
  480. 1, 3: (* BYTE, CHAR *)
  481. SYSTEM.GET( adr, ch );
  482. IF (ch > " ") & (ch <= "~") THEN w.Char( ch ); ELSE w.Hex( ORD( ch ), -2 ); w.Char( "X" ) END;
  483. | 2: (* BOOLEAN *)
  484. SYSTEM.GET( adr, ch );
  485. IF ch = 0X THEN w.String( "FALSE" )
  486. ELSIF ch = 1X THEN w.String( "TRUE" )
  487. ELSE w.Int( ORD( ch ), 1 );
  488. END;
  489. | 4: (* SHORTINT *)
  490. SYSTEM.GET( adr, sval );
  491. w.Int( sval, 1 );
  492. IF sval > 0H THEN w.String(" ("); w.Hex(sval, -2); w.String("H)") END;
  493. | 5: (* INTEGER *)
  494. SYSTEM.GET( adr, ival );
  495. w.Int( ival, 1 );
  496. IF ival > 0H THEN w.String(" (");w.Hex(ival,-4);w.Char("H");w.String(")"); END;
  497. | 6: (* LONGINT *)
  498. SYSTEM.GET( adr, lval );
  499. w.Int( lval, 1 );
  500. IF lval > 0H THEN w.String( " (" ); w.Hex( lval,-8 ); w.String( "H)" ); END;
  501. | 7: (* REAL *)
  502. SYSTEM.GET(adr,rval); SYSTEM.GET(adr,lval);
  503. w.Float(rval,15);
  504. IF lval > 0H THEN w.String(" ("); w.Hex(lval,-8);w.Char( "H" ); w.String(")"); END;
  505. | 8: (* LONGREAL *)
  506. SYSTEM.GET(adr,xval);SYSTEM.GET(adr,hval);
  507. w.Float(xval,15);
  508. IF hval > 0H THEN w.String( " (" ); w.Hex(hval,-16); w.String( "H)" ); END;
  509. | 13,29: (* POINTER *)
  510. SYSTEM.GET( adr, address ); w.Address( address ); w.String( "H" );
  511. (* output type information, if available: *)
  512. w.String(" (");
  513. (* do a check if the address is in the heap range *)
  514. IF CheckHeapAddress(address) THEN
  515. SYSTEM.GET(address + Heaps.TypeDescOffset, address);
  516. WriteType(w,address);
  517. ELSE w.String("NIL");
  518. END;
  519. w.String(")");
  520. | 16: (* HUGEINT *)
  521. SYSTEM.GET( adr , hval );
  522. IF hval = 0 THEN w.Char( '0' );
  523. ELSIF hval > 0 THEN w.Hex( hval, 1 ); w.Char( 'H' )
  524. ELSE w.Hex( hval, -16 );
  525. END;
  526. | 9: (* SET *)
  527. SYSTEM.GET( adr, setval );
  528. w.Set( setval );
  529. | 22: (* RECORD *)
  530. w.String( "Rec@" ); w.Hex( tdadr, -8 ); w.Char( "H" );
  531. | 14: (* PROC *)
  532. SYSTEM.GET( adr, lval ); WriteProc( w, lval );
  533. END;
  534. END;
  535. INC(col,w.Pos()-pos0);
  536. END WriteSimpleVar;
  537. (* Display call trackback. *)
  538. PROCEDURE StackTraceBack*(w: Streams.Writer; pc, bp: ADDRESS; stacklow, stackhigh: ADDRESS; long, overflow: BOOLEAN);
  539. VAR count,refpos: LONGINT; base: ADDRESS; m: Modules.Module; refs: Modules.Bytes;
  540. BEGIN
  541. count := 0; (* frame count *)
  542. REPEAT
  543. m := Modules.ThisModuleByAdr0(pc);
  544. IF (ShowAllProcs OR (m # NIL) OR (count = 0)) & (bp # 0) & (bp >= stacklow) & (bp <= stackhigh) THEN
  545. IF CheckHeapAddress( pc ) THEN
  546. WriteProc0(w, m, pc, bp, refs, refpos, base); w.Ln;Wait(w); w.Update;
  547. IF long & (~overflow OR (count > 0)) THEN (* show variables *)
  548. IF refpos # -1 THEN Variables(w, refs, refpos, base) END;
  549. IF FALSE & (m # NIL) & (base # m.sb) & (count = 0) THEN ModuleState(w, m) END
  550. END;
  551. ELSE
  552. w.String( "Unknown external procedure, pc = " ); w.Address( pc ); w.Ln; Wait(w);
  553. END;
  554. SYSTEM.GET(bp + SIZEOF(ADDRESS), pc); (* return addr from stack *)
  555. SYSTEM.GET(bp, bp); (* follow dynamic link *)
  556. INC(count)
  557. ELSE
  558. bp := 0
  559. END;
  560. UNTIL (bp = 0) OR (count = MaxFrames);
  561. IF bp # 0 THEN w.String("...") END
  562. END StackTraceBack;
  563. (** Write a process's state in one line. *)
  564. PROCEDURE WriteProcess*(w: Streams.Writer; p: Objects.Process);
  565. VAR adr: ADDRESS; mode: LONGINT; m: Modules.Module;
  566. BEGIN
  567. IF p # NIL THEN
  568. w.Int(p.id, 5);
  569. mode := p.mode;
  570. IF (mode >= Objects.Ready) & (mode <= Objects.Terminated) THEN
  571. adr := (mode-Objects.Ready)*4;
  572. FOR adr := adr TO adr+3 DO w.Char(modes[adr]) END
  573. ELSE
  574. w.Char(" "); w.Int(mode, 1)
  575. END;
  576. w.Int(p.procID, 2);
  577. w.Int(p.priority, 2);
  578. w.Update;
  579. w.Address (SYSTEM.VAL(ADDRESS, p.obj));
  580. IF p.obj # NIL THEN
  581. SYSTEM.GET(SYSTEM.VAL(ADDRESS, p.obj) - SIZEOF(ADDRESS), adr);
  582. w.Char(":"); WriteType(w, adr)
  583. END;
  584. w.Update;
  585. w.Char(" "); WriteProc(w, p.state.PC);
  586. IF p.mode = Objects.AwaitingLock THEN
  587. adr := SYSTEM.VAL(ADDRESS, p.waitingOn);
  588. w.Address (adr);
  589. w.Update;
  590. IF adr # 0 THEN (* can be 0 when snapshot is taken *)
  591. SYSTEM.GET(adr - SIZEOF(ADDRESS), adr);
  592. IF adr = SYSTEM.TYPECODE(Modules.Module) THEN
  593. w.Char("-");
  594. m := SYSTEM.VAL(Modules.Module, adr);
  595. w.String(m.name)
  596. ELSE
  597. w.Char(":"); WriteType(w, adr)
  598. END;
  599. w.Update;
  600. END
  601. ELSIF p.mode = Objects.AwaitingCond THEN
  602. w.Char(" "); WriteProc(w, SYSTEM.VAL(ADDRESS, p.condition));
  603. w.Address (p.condFP)
  604. END;
  605. w.Char(" "); w.Set(p.flags)
  606. END
  607. END WriteProcess;
  608. VAR trace: Streams.Writer;
  609. PROCEDURE TraceH(process: Objects.Process; pc, bp: ADDRESS; stacklow, stackhigh: ADDRESS);
  610. BEGIN
  611. trace.String("----------- Process = ");
  612. trace.Address(process);
  613. trace.String(", Object = "); trace.Address(process.obj);
  614. trace.Ln;
  615. StackTraceBack(trace, pc, bp, stacklow ,stackhigh, TRUE, FALSE);
  616. trace.Update;
  617. END TraceH;
  618. (* tracing the stacks of all processes during GC phase (needs to identify and stop all processes) *)
  619. PROCEDURE TraceProcesses*;
  620. BEGIN
  621. Objects.TraceProcessHook := TraceH;
  622. Kernel.GC;
  623. Objects.TraceProcessHook := NIL;
  624. END TraceProcesses;
  625. BEGIN
  626. NEW(trace, Trace.Send, 4096);
  627. modes := " rdy run awl awc awe rip"; (* 4 characters per mode from Objects.Ready to Objects.Terminated *)
  628. END Reflection.