Consider this test code:
Code: Select all
MODULE TestError1;
IMPORT Main;
VAR x, y: INTEGER;
PROCEDURE p1;
VAR z: INTEGER;
BEGIN
y := 0; x := x DIV y
END p1;
PROCEDURE p0;
BEGIN
p1
END p0;
BEGIN
p0
END TestError1.
Code: Select all
integer divided by zero or negative divisor
TestError1.p1 @08002738H, Line: 10
Main.Init @080026F0H, Line: 125
TestError1.p0 @08002766H, Line: 15
TestError1..init @08002776H, Line: 19
Another case with false positives could be if you write code that calculates return addresses, eg. for a debugger, that are kept in local variables, ie. on the stack. I guess in such case we'll know how to interpret the stack trace. So all good.
However, I have run into a case where a procedure call in the chain was missing. Consider this test code:
Code: Select all
MODULE TestError2;
IMPORT Main;
VAR x, y: INTEGER;
PROCEDURE p1;
BEGIN
y := 0; x := x DIV y
END p1;
PROCEDURE p0;
BEGIN
p1
END p0;
BEGIN
p0
END TestError2.
Code: Select all
integer divided by zero or negative divisor
TestError2.p1 @08002738H, Line: 9
TestError2..init @0800277AH, Line: 18
Module Traps catches run-time errors via handler 'SCVTrap', which is activated via checks and SVC statements inserted by the compiler into the program code. 'SCVTrap' initiates the creation of the stack trace thusly:
Code: Select all
PROCEDURE SVCTrap;
BEGIN
(* ... *)
addr := SYSTEM.SP + 40;
StackTrace(addr);
(* ... *)
END SVCTrap;
Code: Select all
stacked by the MCU's exception handling hardware:
+32 PSR
+28 PC
+24 LR
+20 R12
+16 R3
+12 R2
+8 R1
+4 R0
pushed by SVCTrap's prologue:
SP => LR (= EXC_RETURN)
Code: Select all
integer divided by zero or negative divisor
TestError2.p1 @08002738H, Line: 9
TestError2.p0 @0800276AH, Line: 14
TestError2..init @0800277AH, Line: 18
So, depending on the layout of the stack in the procedure call-chain, there can be a one word "gap" between the double-word aligned exception stack frame and the lower end of the stack before the exception. 'SYSTEM.SP + 36' will read that "gap" value, and 'SYSTEM.SP + 40' would be the correct address for the stack track in this case.
Alas, simply always starting from 'SYSTEM.SP + 36' can result in false positives too, as this test code shows:
Code: Select all
MODULE TestError3;
IMPORT Main;
VAR x, y: INTEGER;
PROCEDURE p2;
END p2;
PROCEDURE p1;
BEGIN
p2;
y := 0; x := x DIV y
END p1;
PROCEDURE p0;
VAR z: INTEGER;
BEGIN
z := 0;
p1
END p0;
BEGIN
p0
END TestError3.
Code: Select all
integer divided by zero or negative divisor
TestError3.p1 @08002748H, Line: 13
TestError3.p1 @0800272EH, Line: 11
TestError3.p0 @08002780H, Line: 20
TestError3..init @08002792H, Line: 24
Unfortunately, from "inside" 'SVCTrap', I don't see any possibility to check and decide for either the +36 or the +40 offset, depending on the specific situation, since we don't know the value of SYSTEM.SP right before the exception. Maybe someone smarter than me can figure this one out. I prefer wrong call entries over missing ones, so I set the offset to +36. It's usually possible to quickly make sense of the false positives. Which, as outlined at the top, can happen even if the offset is correct. In any case, the stack trace is enourmously useful, we just need to be aware of the few caveats (and maybe setting the stack offset to +36 from +40).
And yes, the test code examples are somewhat contrived, but that's the nature of test programs. :)
Astrobe's Disassemble Application functionality has proven to be of great value for diagnosing the above issues! For this, I had adapted Traps.ShowStack and Traps.OutStackItem to print "upwards" beyond the exception stack frame to see what Traps.StackTrace scans, and without the app disassembly listing, mapping LR values on the stack to their corresponding procedure calls would have been, hm, "difficult".
Code: Select all
integer divided by zero or negative divisor
TestError3.p1 @08002748H, Line: 13
TestError3.p1 @0800272EH, Line: 11
TestError3.p0 @08002780H, Line: 20
TestError3..init @08002792H, Line: 24
20007CD0H r0 = 00000001H, 1
20007CD4H r1 = 00000000H, 0
20007CD8H r2 = 00000341H, 833
20007CDCH r3 = 00003200H, 12800
20007CE0H r12 = FFFFFFFFH, -1
20007CE4H lr = 08002733H, 134227763
20007CE8H pc = 0800274AH, 134227786
20007CECH psr = 61000200H, 1627390464
20007CF0H s36 = 08002733H, 134227763
20007CF4H s40 = 08002785H, 134227845
20007CF8H s44 = 00000000H, 0
20007CFCH s48 = 08002797H, 134227863
20007D00H s52 = 080027D5H, 134227925