From f7f0d6318231ff081ed6ff2ef30d8e1823e11c70 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 8 Jan 2019 15:39:53 -0800 Subject: 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"? --- subx/013direct_addressing.cc | 1 + subx/039debug.cc | 30 +++++++ subx/Readme.md | 6 ++ subx/apps/pack.subx | 187 ++++++++++++++++++++++++++++++++++++++++--- 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 Watch_points; +:(before "End Reset") +Watch_points.clear(); +:(code) +void dump_watch_points() { + if (Watch_points.empty()) return; + dbg << "watch points:" << end(); + for (map::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 -- cgit 1.4.1-2-gfad0