about summary refs log tree commit diff stats
path: root/subx/056trace.subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-02-02 22:05:11 -0800
committerKartik Agaram <vc@akkartik.com>2019-02-02 22:05:11 -0800
commit03c6f1d3858ff0fdb6af6ce4b61bea51f16b33be (patch)
tree2dfceaa7676f03f09dc3585030b56cdea834483d /subx/056trace.subx
parentcb82bd2364c302acbb96dd6900779e189b36efac (diff)
downloadmu-03c6f1d3858ff0fdb6af6ce4b61bea51f16b33be.tar.gz
4949
Diffstat (limited to 'subx/056trace.subx')
-rw-r--r--subx/056trace.subx357
1 files changed, 357 insertions, 0 deletions
diff --git a/subx/056trace.subx b/subx/056trace.subx
new file mode 100644
index 00000000..3b1a22e5
--- /dev/null
+++ b/subx/056trace.subx
@@ -0,0 +1,357 @@
+# primitives for emitting traces to a 'trace' stream, and for tests to make assertions on its contents
+#
+# A trace stream looks like this:
+#   read : int  # index that we've read until
+#   write : int  # index at which writes go
+#   data : (array byte)  # prefixed by length as usual
+# In a real trace the data will be in a special segment set aside for the purpose.
+#
+# primitives for operating on traces:
+#   - initialize-trace-stream (update global variable)
+#   - trace: stream, string
+#   - die: stream (exit(1) if using real trace)
+#   - check-trace-contains: stream, string/line, string/message (scans only from stream's read pointer, prints message to stderr on failure, updates stream's read pointer)
+#   - scan-to-next-line: stream (advance read pointer past next newline)
+#
+# Traces are very fundamental, so many of the helpers we create here won't be
+# used elsewhere; we'll switch to more bounds-checked variants. But here we get
+# bounds-checking for free; we allocate a completely disjoint segment for trace
+# data, and overflowing it will generate a page fault.
+
+== data
+
+# We'll save the address of the trace segment here.
+Trace-stream:
+    00 00 00 00
+
+# Fake trace-stream for tests.
+# Also illustrates the layout of the real trace-stream (segment).
+_test-trace-stream:
+    # current write index
+    00 00 00 00
+    # current read index
+    00 00 00 00
+    # length (= 8)
+    08 00 00 00
+    # data
+    00 00 00 00 00 00 00 00  # 8 bytes
+
+== code
+#   instruction                     effective address                                                   register    displacement    immediate
+# . op          subop               mod             rm32          base        index         scale       r32
+# . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
+
+# main:
+    # run-tests()
+    e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+    # syscall(exit, Num-test-failures)
+    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+    b8/copy-to-EAX  1/imm32/exit
+    cd/syscall  0x80/imm8
+
+# Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream.
+# The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
+initialize-trace-stream:
+    # EAX = new-segment(0x1000)
+    # . . push args
+    68/push  0x1000/imm32/N
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # copy EAX to *Trace-stream
+    89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+    # Trace-stream->length = 0x1000/N - 12
+    c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         0xff4/imm32       # copy 0xff4 to *(EAX+8)
+    c3/return
+
+# Append a string to the given trace stream.
+# Silently give up if it's already full. Or truncate the string if there isn't enough room.
+trace:  # t : (address trace-stream), line : string
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # . save registers
+    50/push-EAX
+    51/push-ECX
+    52/push-EDX
+    53/push-EBX
+    56/push-ESI
+    57/push-EDI
+    # EDI = t
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+    # ESI = line
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+    # ECX = t->write
+    8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+    # EDX = t->length
+    8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(EDI+8) to EDX
+    # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
+    # . . push line
+    56/push-ESI
+    # . . push &t->data[t->length]
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
+    53/push-EBX
+    # . . push &t->data[t->write]
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
+    53/push-EBX
+    # . . call
+    e8/call  _append-3/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # if EAX == 0 return
+    81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EDX
+    74/jump-if-equal  $trace:end/disp8
+    # t->write += EAX
+    01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+    # refresh ECX = t->write
+    8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+    # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
+    # . . push line
+    68/push  Newline/imm32
+    # . . push &t->data[t->length]
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
+    53/push-EBX
+    # . . push &t->data[t->write]
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
+    53/push-EBX
+    # . . call
+    e8/call  _append-3/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # t->write += EAX
+    01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+$trace:end:
+    # . restore registers
+    5f/pop-to-EDI
+    5e/pop-to-ESI
+    5b/pop-to-EBX
+    5a/pop-to-EDX
+    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
+
+clear-trace-stream:  # t : (address trace-stream)
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # . save registers
+    50/push-EAX
+    51/push-ECX
+    # EAX = t
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+    # ECX = t->length
+    8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EAX+8) to ECX
+    # ECX = &t->data[t->length]
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EAX+ECX+12 to ECX
+    # t->write = 0
+    c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+    # t->read = 0
+    c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
+    # EAX = t->data
+    81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xc/imm32         # add to EAX
+    # while (true)
+$clear-trace-stream:loop:
+    # if EAX >= ECX break
+    39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+    7d/jump-if-greater-or-equal  $clear-trace-stream:end/disp8
+    # *EAX = 0
+    c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+    # EAX += 4
+    81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm32           # add to EAX
+    eb/jump  $clear-trace-stream:loop/disp8
+$clear-trace-stream:end:
+    # . restore registers
+    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
+
+# - tests
+
+test-trace-single:
+    # clear-trace-stream(_test-trace-stream)
+    # . . push args
+    68/push  _test-trace-stream/imm32
+    # . . call
+    e8/call  clear-trace-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # trace(_test-trace-stream, "Ab")
+    # . . push args
+    68/push  "Ab"/imm32
+    68/push  _test-trace-stream/imm32
+    # . . call
+    e8/call  trace/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
+    # . . push args
+    68/push  "F - test-trace-single"/imm32
+    68/push  0x0a6241/imm32/Ab-newline
+    # . . push *_test-trace-stream->data
+    b8/copy-to-EAX  _test-trace-stream/imm32
+    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # end
+    c3/return
+
+test-trace-appends:
+    # clear-trace-stream(_test-trace-stream)
+    # . . push args
+    68/push  _test-trace-stream/imm32
+    # . . call
+    e8/call  clear-trace-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # trace(_test-trace-stream, "C")
+    # . . push args
+    68/push  "C"/imm32
+    68/push  _test-trace-stream/imm32
+    # . . call
+    e8/call  trace/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # trace(_test-trace-stream, "D")
+    # . . push args
+    68/push  "D"/imm32
+    68/push  _test-trace-stream/imm32
+    # . . call
+    e8/call  trace/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
+    # . . push args
+    68/push  "F - test-trace-appends"/imm32
+    68/push  0x0a440a43/imm32/C-newline-D-newline
+    # . . push *_test-trace-stream->data
+    b8/copy-to-EAX  _test-trace-stream/imm32
+    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # end
+    c3/return
+
+test-trace-empty-line:
+    # clear-trace-stream(_test-trace-stream)
+    # . . push args
+    68/push  _test-trace-stream/imm32
+    # . . call
+    e8/call  clear-trace-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # trace(_test-trace-stream, "")
+    # . . push args
+    68/push  ""/imm32
+    68/push  _test-trace-stream/imm32
+    # . . call
+    e8/call  trace/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(*_test-trace-stream->data, 0, msg)
+    # . . push args
+    68/push  "F - test-trace-empty-line"/imm32
+    68/push  0/imm32
+    # . . push *_test-trace-stream->data
+    b8/copy-to-EAX  _test-trace-stream/imm32
+    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # end
+    c3/return
+
+# - helpers
+
+# 3-argument variant of _append
+_append-3:  # out : address, outend : address, s : (array byte) -> num_bytes_appended/EAX
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # . save registers
+    51/push-ECX
+    # _append-4(out, outend, &s->data[0], &s->data[s->length]) -> num_bytes_appended/EAX
+    # . . push &s->data[s->length]
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+    51/push-ECX
+    # . . push &s->data[0]
+    8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
+    51/push-ECX
+    # . . push outend
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+    # . . push out
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # . . call
+    e8/call  _append-4/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+$_append-3:end:
+    # . restore registers
+    59/pop-to-ECX
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+# 4-argument variant of _append
+_append-4:  # out : address, outend : address, in : address, inend : address -> num_bytes_appended/EAX
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # . save registers
+    51/push-ECX
+    52/push-EDX
+    53/push-EBX
+    56/push-ESI
+    57/push-EDI
+    # EAX/num_bytes_appended = 0
+    b8/copy-to-EAX  0/imm32
+    # EDI = out
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+    # EDX = outend
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+    # ESI = in
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0x10/disp8      .                 # copy *(EBP+16) to ESI
+    # ECX = inend
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
+$_append-4:loop:
+    # if ESI/in >= ECX/inend break
+    39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI with ECX
+    7d/jump-if-greater-or-equal  $_append-4:end/disp8
+    # if EDI/out >= EDX/outend break  (for now silently ignore filled up buffer)
+    39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDI with EDX
+    7d/jump-if-greater-or-equal  $_append-4:end/disp8
+    # copy one byte from ESI/in to EDI/out
+    8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/BL    .               .                 # copy byte at *ESI to BL
+    88/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *EDI
+    # updates
+    40/increment-EAX
+    46/increment-ESI
+    47/increment-EDI
+    eb/jump  $_append-4:loop/disp8
+$_append-4:end:
+    # . restore registers
+    5f/pop-to-EDI
+    5e/pop-to-ESI
+    5b/pop-to-EBX
+    5a/pop-to-EDX
+    59/pop-to-ECX
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+# . . vim:nowrap:textwidth=0