about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-01-08 15:39:53 -0800
committerKartik Agaram <vc@akkartik.com>2019-01-08 15:46:55 -0800
commitf7f0d6318231ff081ed6ff2ef30d8e1823e11c70 (patch)
treeb85f03addb53d757bf3eac59e7c9cc93648d1709
parent96a6bac52d6f4a67932e54d25166bf7961718e9d (diff)
downloadmu-f7f0d6318231ff081ed6ff2ef30d8e1823e11c70.tar.gz
4915
In the process of building next-token I finally added some support for a
debugging situation I've found myself in a couple of times: wondering "what
changed this memory location"?
-rw-r--r--subx/013direct_addressing.cc1
-rw-r--r--subx/039debug.cc30
-rw-r--r--subx/Readme.md6
-rw-r--r--subx/apps/pack.subx187
4 files changed, 213 insertions, 11 deletions
diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc
index 3dcd8333..b7146835 100644
--- a/subx/013direct_addressing.cc
+++ b/subx/013direct_addressing.cc
@@ -56,6 +56,7 @@ uint32_t effective_address_number(uint8_t modrm) {
     exit(1);
   }
   //: other mods are indirect, and they'll set addr appropriately
+  // Found effective_address(addr)
   return addr;
 }
 
diff --git a/subx/039debug.cc b/subx/039debug.cc
index 4d041fa4..816947a2 100644
--- a/subx/039debug.cc
+++ b/subx/039debug.cc
@@ -23,3 +23,33 @@ void load_map(const string& map_filename) {
 :(after "Run One Instruction")
 if (contains_key(Symbol_name, EIP))
   trace(90, "run") << "== label " << get(Symbol_name, EIP) << end();
+
+// If a label starts with '$watch-', make a note of the effective address
+// computed by the next instruction. Start dumping out its contents after
+// every subsequent instruction.
+
+:(after "Run One Instruction")
+dump_watch_points();
+:(before "End Globals")
+map<string, uint32_t> Watch_points;
+:(before "End Reset")
+Watch_points.clear();
+:(code)
+void dump_watch_points() {
+  if (Watch_points.empty()) return;
+  dbg << "watch points:" << end();
+  for (map<string, uint32_t>::iterator p = Watch_points.begin();  p != Watch_points.end();  ++p)
+    dbg << "  " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end();
+}
+
+:(before "End Globals")
+string Watch_this_effective_address;
+:(after "Run One Instruction")
+Watch_this_effective_address = "";
+if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$watch-"))
+  Watch_this_effective_address = get(Symbol_name, EIP);
+:(after "Found effective_address(addr)")
+if (!Watch_this_effective_address.empty()) {
+  dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end();
+  Watch_points[Watch_this_effective_address] = addr;
+}
diff --git a/subx/Readme.md b/subx/Readme.md
index 42f2cedb..93ef21e1 100644
--- a/subx/Readme.md
+++ b/subx/Readme.md
@@ -466,6 +466,12 @@ rudimentary but hopefully still workable toolkit:
   Now the trace should have a lot more detail on which of these labels was
   reached, and precisely when the exit was taken.
 
+* If you find yourself wondering, "when did the contents of this memory
+  address change?", `subx run` has some rudimentary support for _watch
+  points_. Just insert a label starting with `$watch-` before an instruction
+  that writes to the address, and its value will start getting dumped to the
+  trace after every instruction thereafter.
+
 * 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
diff --git a/subx/apps/pack.subx b/subx/apps/pack.subx
index bad21e0f..b5f39009 100644
--- a/subx/apps/pack.subx
+++ b/subx/apps/pack.subx
@@ -121,12 +121,6 @@ $main:end:
 #   slice-equal?(slice, kernel string)
 
 # helpers:
-#   new-stream(length int, elemsize int) -- allocate length*elemsize bytes, initialize first word with length*elemsize
-#   read-line(in : &buffered-file, line : stream byte, err : &buffered-file, ed : &exit-descriptor)
-#   next-word(line : stream byte, out : &slice)
-#     responsible for skipping whitespace and comments
-#   next-token(line : stream byte, delim : byte, out : &slice)
-#     return (0, 0) sentinel on hitting comment or end of array
 #   slice-empty?(in : &slice) -> bool
 #   slice-equal?(in : &slice, s : &kernel-string) -> bool
 #   is-hex-int(in : &slice)
@@ -162,16 +156,187 @@ next-word:  # line : (address stream byte), out : (address slice)
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     # . save registers
-    # skip-whitespace(line)
+    50/push-EAX
+    51/push-ECX
+    56/push-ESI
+    57/push-EDI
+    # ESI = line
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+    # EDI = out
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+    # skip-chars-matching(line, ' ')
     # . . push args
+    68/push  0x20/imm32/space
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     # . . call
-    e8/call  skip-whitespace/disp32
+    e8/call  skip-chars-matching/disp32
     # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-    # if line->data[line->read] == '#' return (&line->data[line->read], &line->data[line->write])
-    # return next-token(line, ' ')
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # out->start = &line->data[line->read]
+    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+#? $watch-1:
+    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+    # if line->data[line->read] == '#': out->end = &line->data[line->write]), skip rest of stream and return
+    # . EAX = line->data[line->read]
+    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+    8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
+    # . compare
+    3d/compare-EAX-with  0x23/imm32/pound
+    75/jump-if-not-equal  $next-word:not-comment/disp8
+    # . out->end = &line->data[line->write]
+    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
+    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+    # . line->read = line->write
+    89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
+    # . return
+    eb/jump  $next-word:end/disp8
+$next-word:not-comment:
+    # otherwise skip-chars-not-matching(line, ' ')
+    # . . push args
+    68/push  0x20/imm32/space
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # . . call
+    e8/call  skip-chars-not-matching/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # out->end = &line->data[line->read]
+    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+$next-word:end:
     # . restore registers
+    5f/pop-to-EDI
+    5e/pop-to-ESI
+    59/pop-to-ECX
+    58/pop-to-EAX
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+test-next-word:
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # var slice/ECX = {0, 0}
+    68/push  0/imm32/end
+    68/push  0/imm32/start
+    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+    # write(_test-stream, "  ab")
+    # . . push args
+    68/push  "  ab"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # next-word(_test-stream, slice)
+    # . . push args
+    51/push-ECX
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  next-word/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+    # . check-ints-equal(slice->start - _test-stream, 14, msg)
+    # . . push args
+    68/push  "F - test-next-word: start"/imm32
+    68/push  0xe/imm32
+    # . . push slice->start - _test-stream
+    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # check-ints-equal(slice->end - _test-stream->data, 4, msg)
+    # . check-ints-equal(slice->end - _test-stream, 16, msg)
+    # . . push args
+    68/push  "F - test-next-word: end"/imm32
+    68/push  0x10/imm32
+    # . . push slice->end - _test-stream
+    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+test-next-word-returns-whole-comment:
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # var slice/ECX = {0, 0}
+    68/push  0/imm32/end
+    68/push  0/imm32/start
+    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+    # write(_test-stream, "  # a")
+    # . . push args
+    68/push  "  # a"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # next-word(_test-stream, slice)
+    # . . push args
+    51/push-ECX
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  next-word/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+    # . check-ints-equal(slice->start - _test-stream, 14, msg)
+    # . . push args
+    68/push  "F - test-next-word-returns-whole-comment: start"/imm32
+    68/push  0xe/imm32
+    # . . push slice->start - _test-stream
+    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # check-ints-equal(slice->end - _test-stream->data, 5, msg)
+    # . check-ints-equal(slice->end - _test-stream, 17, msg)
+    # . . push args
+    68/push  "F - test-next-word-returns-whole-comment: end"/imm32
+    68/push  0x11/imm32
+    # . . push slice->end - _test-stream
+    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     # . epilog
     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     5d/pop-to-EBP