about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-12-08 01:00:30 -0800
committerKartik Agaram <vc@akkartik.com>2018-12-08 01:00:30 -0800
commit3fede2964e3d900db22098b6736b82d4bf22dc14 (patch)
treeaee03871e0f95c1521e74502897c556979967b94 /subx
parente20d5c063661dc63c9b26a3596b75a8ca57c8f79 (diff)
downloadmu-3fede2964e3d900db22098b6736b82d4bf22dc14.tar.gz
4858 - debugging tips
Diffstat (limited to 'subx')
-rw-r--r--subx/Readme.md103
1 files changed, 103 insertions, 0 deletions
diff --git a/subx/Readme.md b/subx/Readme.md
index d4fe14e6..bf429465 100644
--- a/subx/Readme.md
+++ b/subx/Readme.md
@@ -375,6 +375,109 @@ Running `subx` will transparently compile it as necessary.
   building general infrastructure here for all of the x86 instruction set.
   SubX is about programming with a small, regular subset of 32-bit x86.
 
+## A few hints for debugging
+
+Writing programs in SubX is surprisingly pleasant and addictive. Reading
+programs is a work in progress, and hopefully the extensive unit tests help.
+However, _debugging_ programs is where one really faces up to the low-level
+nature of SubX. Even the smallest modifications need testing to make sure they
+work. In my experience, there is no modification so small that I get it working
+on the first attempt. And when it doesn't work, there are no clear error
+messages. Machine code is too simple-minded for that. You can't use a debugger,
+since SubX's simplistic ELF binaries contain no debugging information. So
+debugging requires returning to basics and practicing with a new, more
+rudimentary but hopefully still workable toolkit:
+
+* Start by nailing down a concrete set of steps for reproducibly obtaining the
+  error or erroneous behavior.
+
+* If possible, turn the steps into a failing test. It's not always possible,
+  but SubX's primary goal is to keep improving the variety of tests one can
+  write.
+
+* Start running the single failing test alone. This involves modifying the top
+  of the program (or the final `.subx` file passed in to `subx translate`) by
+  replacing the call to `run-tests` with a call to the appropriate `test-`
+  function.
+
+* Generate a trace for the failing test while running your program in emulated
+  mode (`subx run`):
+  ```
+  $ ./subx translate input.subx -o binary
+  $ ./subx --trace run binary arg1 arg2  2>trace
+  ```
+  The ability to generate a trace is the essential reason for the existence of
+  `subx run` mode. It gives far better visibility into program internals than
+  running natively.
+
+* As a further refinement, it is possible to render label names in the trace
+  by adding a second flag to both the `translate` and `run` commands:
+  ```
+  $ ./subx --map translate input.subx -o binary
+  $ ./subx --map --trace run binary arg1 arg2  2>trace
+  ```
+  `subx --map translate` emits a mapping from label to address in a file
+  called `map`. `subx --map --trace run` reads in the `map` file at the start
+  and prints out any matching label name as it traces each instruction
+  executed.
+
+  Here's a sample of what a trace looks like, with a few boxes highlighted:
+
+  <img alt='trace example' src='../html/subx/trace.png'>
+
+  Each of the green boxes shows the trace emitted for a single instruction.
+  It starts with a line of the form `run: inst: ___` followed by the opcode
+  for the instruction, the state of registers before the instruction executes,
+  and various other facts deduced during execution. Some instructions first
+  print a matching label. In the above screenshot, the red boxes show that
+  address `0x0900005e` maps to label `$loop` and presumably marks the start of
+  some loop. Function names get similar `run: == label` lines.
+
+* One trick when emitting traces with labels:
+  ```
+  $ grep label trace
+  ```
+  This is useful for quickly showing you the control flow for the run, and the
+  function executing when the error occurred. I find it useful to start with
+  this information, only looking at the complete trace after I've gotten
+  oriented on the control flow. Did it get to the loop I just modified? How
+  many times did it go through the loop?
+
+* Once you have SubX displaying labels in traces, it's a short step to modify
+  the program to insert more labels just to gain more insight. For example,
+  consider the following function:
+
+  <img alt='control example -- before' src='../html/subx/control0.png'>
+
+  This function contains a series of jump instructions. If a trace shows
+  `is-hex-lowercase-byte?` being encountered, and then `$is-hex-lowercase-byte?:end`
+  being encountered, it's still ambiguous what happened. Did we hit an early
+  exit, or did we execute all the way through? To clarify this, add temporary
+  labels after each jump:
+
+  <img alt='control example -- after' src='../html/subx/control1.png'>
+
+  Now the trace should have a lot more detail on which of these labels was
+  reached, and precisely when the exit was taken.
+
+* Once we have a sense for precisely which instructions we want to look at,
+  it's time to look at the trace as a whole. Key is the state of registers
+  before each instruction. If a function is receiving bad arguments it becomes
+  natural to inspect what values were pushed on the stack before calling it,
+  tracing back further from there, and so on.
+
+  I occasionally want to see the precise state of the stack segment, in which
+  case I uncomment a commented-out call to `dump_stack()` in the `vm.cc`
+  layer. It makes the trace a lot more verbose and a lot less dense, necessitating
+  a lot more scrolling around, so I keep it turned off most of the time.
+
+Hopefully these hints are enough to get you started. The main thing to
+remember is to not be afraid of modifying the sources. A good debugging
+session gets into a nice rhythm of generating a trace, staring at it for a
+while, modifying the sources, regenerating the trace, and so on. Email
+[me](mailto:mu@akkartik.com) if you'd like another pair of eyes to stare at a
+trace, or if you have questions or complaints.
+
 ## SubX library
 
 A major goal of SubX is testable wrappers for operating system syscalls.
com> 2015-10-28 13:08:26 -0700 committer Kartik K. Agaram <vc@akkartik.com> 2015-10-28 13:19:41 -0700 2299 - check types of ingredients in calls' href='/akkartik/mu/commit/034call.cc?h=main&id=1fa530589eee7b668d936e77c1c430f18907a481'>1fa53058 ^
ec926027 ^
31401373 ^
ec926027 ^



ca01193d ^
363be37f ^
69e14325 ^
5eb49929 ^
e2240eb4 ^

363be37f ^
77cdc6d0 ^

dd2e01e4 ^
77cdc6d0 ^

31401373 ^
d72f3799 ^
0b0cfb6f ^
7284d503 ^
67573caf ^




7284d503 ^

ac0e9db5 ^

31401373 ^
67573caf ^
ec926027 ^
7c8493b3 ^

31401373 ^
795f5244 ^
ec926027 ^
dcfca05e ^

31401373 ^
1fa53058 ^



795f5244 ^
dcfca05e ^
f89378d5 ^
2142ccfc ^

f3760b0f ^
795f5244 ^
2142ccfc ^

f1a6f323 ^
9cf71627 ^
5810092d ^
513bfed8 ^




2142ccfc ^
77cdc6d0 ^

dd2e01e4 ^
77cdc6d0 ^

012d2ee1 ^
3eeea0a2 ^
3b795875 ^
3eeea0a2 ^
9cf71627 ^
5f98a10c ^

2cb36cd0 ^


5f98a10c ^
2cb36cd0 ^

5f98a10c ^
2cb36cd0 ^


5f98a10c ^
2cb36cd0 ^
7284d503 ^
64cf0a59 ^
69e14325 ^


df8bb4c3 ^
f6d47435 ^
5eb49929 ^
31401373 ^
795f5244 ^
5eb49929 ^

f1e953d0 ^
ec926027 ^

ac0e9db5 ^
2e8c5d39 ^
77cdc6d0 ^
dd2e01e4 ^
77cdc6d0 ^


31401373 ^
b75e94b3 ^
9fdda88b ^
5f98a10c ^
8eff7919 ^
f6d47435 ^
f278a15d ^



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167