--- /dev/null
+Debugging requires many of a system components to all work together. The goal
+is to collect some tests that test gdb with spike, and gdb talking to real
+hardware through openocd.
--- /dev/null
+#!/usr/bin/python
+
+import os
+import sys
+import argparse
+import testlib
+import unittest
+import tempfile
+import time
+import random
+import binascii
+
+class DeleteServer(unittest.TestCase):
+ def tearDown(self):
+ del self.server
+
+class InstantHaltTest(DeleteServer):
+ def setUp(self):
+ self.binary = target.compile("debug.c")
+ self.server = target.server(self.binary, halted=True)
+ self.gdb = testlib.Gdb()
+ self.gdb.command("file %s" % self.binary)
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+
+ def test_instant_halt(self):
+ self.assertEqual(0x1000, self.gdb.p("$pc"))
+ # For some reason instret resets to 0.
+ self.assertLess(self.gdb.p("$instret"), 8)
+ self.gdb.command("stepi")
+ self.assertNotEqual(0x1000, self.gdb.p("$pc"))
+
+ def test_change_pc(self):
+ """Change the PC right as we come out of reset."""
+ # 0x13 is nop
+ self.gdb.command("p *((int*) 0x80000000)=0x13")
+ self.gdb.command("p *((int*) 0x80000004)=0x13")
+ self.gdb.command("p *((int*) 0x80000008)=0x13")
+ self.gdb.command("p $pc=0x80000000")
+ self.gdb.command("stepi")
+ self.assertEqual(0x80000004, self.gdb.p("$pc"))
+ self.gdb.command("stepi")
+ self.assertEqual(0x80000008, self.gdb.p("$pc"))
+
+class DebugTest(DeleteServer):
+ def setUp(self):
+ self.binary = target.compile("debug.c")
+ self.server = target.server(self.binary, halted=False)
+ self.gdb = testlib.Gdb()
+ self.gdb.command("file %s" % self.binary)
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+
+ def test_turbostep(self):
+ """Single step a bunch of times."""
+ self.gdb.command("p i=0");
+ last_pc = None
+ for _ in range(100):
+ self.gdb.command("stepi")
+ pc = self.gdb.command("p $pc")
+ self.assertNotEqual(last_pc, pc)
+ last_pc = pc
+
+ def test_exit(self):
+ self.gdb.command("p i=0");
+ output = self.gdb.command("c")
+ self.assertIn("Continuing", output)
+ self.assertIn("Remote connection closed", output)
+
+ def test_breakpoint(self):
+ self.gdb.command("p i=0");
+ self.gdb.command("b print_row")
+ # The breakpoint should be hit exactly 10 times.
+ for i in range(10):
+ output = self.gdb.command("c")
+ self.assertIn("Continuing", output)
+ self.assertIn("length=%d" % i, output)
+ self.assertIn("Breakpoint 1", output)
+ output = self.gdb.command("c")
+ self.assertIn("Continuing", output)
+ self.assertIn("Remote connection closed", output)
+
+ def test_registers(self):
+ self.gdb.command("p i=0");
+ # Try both forms to test gdb.
+ for cmd in ("info all-registers", "info registers all"):
+ output = self.gdb.command(cmd)
+ self.assertNotIn("Could not", output)
+ for reg in ('zero', 'ra', 'sp', 'gp', 'tp'):
+ self.assertIn(reg, output)
+ # mcpuid is one of the few registers that should have the high bit set
+ # (for rv64).
+ # Leave this commented out until gdb and spike agree on the encoding of
+ # mcpuid (which is going to be renamed to misa in any case).
+ #self.assertRegexpMatches(output, ".*mcpuid *0x80")
+
+ # The instret register should always be changing.
+ last_instret = None
+ for _ in range(5):
+ instret = self.gdb.p("$instret")
+ self.assertNotEqual(instret, last_instret)
+ last_instret = instret
+ self.gdb.command("stepi")
+
+ def test_interrupt(self):
+ """Sending gdb ^C while the program is running should cause it to halt."""
+ self.gdb.c(wait=False)
+ time.sleep(0.1)
+ self.gdb.interrupt()
+ self.gdb.command("p i=123");
+ self.gdb.c(wait=False)
+ time.sleep(0.1)
+ self.gdb.interrupt()
+ self.gdb.command("p i=0");
+ output = self.gdb.c()
+ self.assertIn("Continuing", output)
+ self.assertIn("Remote connection closed", output)
+
+class RegsTest(DeleteServer):
+ def setUp(self):
+ self.binary = target.compile("programs/regs.S")
+ self.server = target.server()
+ self.gdb = testlib.Gdb()
+ self.gdb.command("file %s" % self.binary)
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+ self.gdb.command("load")
+
+ def test_write_gprs(self):
+ # Note a0 is missing from this list since it's used to hold the
+ # address.
+ regs = ("ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1",
+ "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4",
+ "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5",
+ "t6")
+
+ self.gdb.command("p $pc=write_regs")
+ for i, r in enumerate(regs):
+ self.gdb.command("p $%s=%d" % (r, (0xdeadbeef<<i)+17))
+ self.gdb.command("p $a0=data")
+ self.gdb.command("b all_done")
+ output = self.gdb.command("c")
+ self.assertIn("Breakpoint 1", output)
+
+ # Just to get this data in the log.
+ self.gdb.command("x/30gx data")
+ self.gdb.command("info registers")
+ for n in range(len(regs)):
+ self.assertEqual(self.gdb.x("data+%d" % (8*n), 'g'),
+ (0xdeadbeef<<n)+17)
+
+ def test_write_csrs(self):
+ # As much a test of gdb as of the simulator.
+ self.gdb.p("$mscratch=0")
+ self.gdb.stepi()
+ self.assertEqual(self.gdb.p("$mscratch"), 0)
+ self.gdb.p("$mscratch=123")
+ self.gdb.stepi()
+ self.assertEqual(self.gdb.p("$mscratch"), 123)
+
+ self.gdb.command("p $fflags=9")
+ self.gdb.command("p $pc=write_regs")
+ self.gdb.command("p $a0=data")
+ self.gdb.command("b all_done")
+ self.gdb.command("c")
+
+ self.assertEqual(9, self.gdb.p("$fflags"))
+ self.assertEqual(9, self.gdb.p("$x1"))
+ self.assertEqual(9, self.gdb.p("$csr1"))
+
+class DownloadTest(DeleteServer):
+ def setUp(self):
+ length = 2**20
+ fd = file("data.c", "w")
+ fd.write("#include <stdint.h>\n")
+ fd.write("uint32_t length = %d;\n" % length)
+ fd.write("uint8_t d[%d] = {\n" % length)
+ self.crc = 0
+ for i in range(length / 16):
+ fd.write(" /* 0x%04x */ " % (i * 16));
+ for _ in range(16):
+ value = random.randrange(1<<8)
+ fd.write("%d, " % value)
+ self.crc = binascii.crc32("%c" % value, self.crc)
+ fd.write("\n");
+ fd.write("};\n");
+ fd.write("uint8_t *data = &d[0];\n");
+ fd.close()
+
+ self.binary = target.compile("checksum.c", "data.c", "start.S",
+ "-mcmodel=medany",
+ "-T", "standalone.lds",
+ "-nostartfiles"
+ )
+ self.server = target.server(None, halted=True)
+ self.gdb = testlib.Gdb()
+ self.gdb.command("file %s" % self.binary)
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+
+ def test_download(self):
+ output = self.gdb.command("load")
+ self.assertNotIn("failed", output)
+ self.assertIn("Transfer rate", output)
+ self.gdb.command("b done")
+ self.gdb.c()
+ result = self.gdb.p("$a0")
+ self.assertEqual(self.crc, result)
+
+class MprvTest(DeleteServer):
+ def setUp(self):
+ self.binary = target.compile("mprv.S", "-T", "standalone.lds",
+ "-nostartfiles")
+ self.server = target.server(None, halted=True)
+ self.gdb = testlib.Gdb()
+ self.gdb.command("file %s" % self.binary)
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+ self.gdb.command("load")
+
+ def test_mprv(self):
+ """Test that the debugger can access memory when MPRV is set."""
+ self.gdb.c(wait=False)
+ self.gdb.interrupt()
+ output = self.gdb.command("p/x *(int*)(((char*)&data)-0x80000000)")
+ self.assertIn("0xbead", output)
+
+class Target(object):
+ def server(self):
+ raise NotImplementedError
+
+ def compile(self, *sources):
+ return testlib.compile(*(sources +
+ ("targets/%s/entry.S" % self.name, "programs/init.c",
+ "-I", "../env",
+ "-T", "targets/%s/link.lds" % self.name,
+ "-nostartfiles")))
+
+class SpikeTarget(Target):
+ name = "spike"
+
+class MicroSemiTarget(Target):
+ name = "m2gl_m2s"
+
+ def server(self):
+ return testlib.Openocd(cmd=parsed.openocd,
+ config="targets/%s/openocd.cfg" % self.name)
+
+targets = [
+ SpikeTarget,
+ MicroSemiTarget
+ ]
+
+def main():
+ parser = argparse.ArgumentParser()
+ group = parser.add_mutually_exclusive_group(required=True)
+ for t in targets:
+ group.add_argument("--%s" % t.name, action="store_const", const=t,
+ dest="target")
+ parser.add_argument("--openocd", help="The OpenOCD command to use.",
+ default="openocd")
+ parser.add_argument("unittest", nargs="*")
+ global parsed
+ parsed = parser.parse_args()
+
+ global target
+ target = parsed.target()
+ unittest.main(argv=[sys.argv[0]] + parsed.unittest)
+
+# TROUBLESHOOTING TIPS
+# If a particular test fails, run just that one test, eg.:
+# ./tests/gdbserver.py MprvTest.test_mprv
+# Then inspect gdb.log and spike.log to see what happened in more detail.
+
+if __name__ == '__main__':
+ sys.exit(main())
--- /dev/null
+#include <stdint.h>
+
+// CRC code from http://www.hackersdelight.org/hdcodetxt/crc.c.txt
+
+// Reverses (reflects) bits in a 32-bit word.
+unsigned reverse(unsigned x) {
+ x = ((x & 0x55555555) << 1) | ((x >> 1) & 0x55555555);
+ x = ((x & 0x33333333) << 2) | ((x >> 2) & 0x33333333);
+ x = ((x & 0x0F0F0F0F) << 4) | ((x >> 4) & 0x0F0F0F0F);
+ x = (x << 24) | ((x & 0xFF00) << 8) |
+ ((x >> 8) & 0xFF00) | (x >> 24);
+ return x;
+}
+
+// ----------------------------- crc32a --------------------------------
+
+/* This is the basic CRC algorithm with no optimizations. It follows the
+logic circuit as closely as possible. */
+
+unsigned int crc32a(uint8_t *message, unsigned int size) {
+ int i, j;
+ unsigned int byte, crc;
+
+ i = 0;
+ crc = 0xFFFFFFFF;
+ while (i < size) {
+ byte = message[i]; // Get next byte.
+ byte = reverse(byte); // 32-bit reversal.
+ for (j = 0; j <= 7; j++) { // Do eight times.
+ if ((int)(crc ^ byte) < 0)
+ crc = (crc << 1) ^ 0x04C11DB7;
+ else crc = crc << 1;
+ byte = byte << 1; // Ready next msg bit.
+ }
+ i = i + 1;
+ }
+ return reverse(~crc);
+}
+
+extern uint8_t *data;
+extern uint32_t length;
+
+uint32_t main()
+{
+ /* Compute a simple checksum. */
+ return crc32a(data, length);
+}
--- /dev/null
+#include <stdio.h>
+
+char c = 'x';
+
+void print_row(int length)
+{
+ for (int x=0; x<length; x++) {
+ printf("%c", c);
+ }
+ printf("\n");
+}
+
+int main()
+{
+ volatile int i = 42;
+ const char *text = "constant\n";
+ int threshold = 7;
+
+ // Wait for the debugger to get us out of this loop.
+ while (i)
+ ;
+
+ printf("%s", text);
+ for (int y=0; y < 10; y++) {
+ print_row(y);
+ }
+}
--- /dev/null
+int main(void);
+
+void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int sp)
+{
+ while (1)
+ ;
+}
+
+void _init()
+{
+ main();
+ while (1)
+ ;
+}
--- /dev/null
+#include "../riscv/encoding.h"
+#define PGSHIFT 12
+
+ .global _start
+
+ .section .text
+_start:
+ # Set up a page table entry that maps 0x0... to 0x8...
+ la t0, page_table
+ srli t0, t0, PGSHIFT
+ csrw CSR_SPTBR, t0
+
+ # update mstatus
+ csrr t1, CSR_MSTATUS
+ li t0, (MSTATUS_MPRV | (VM_SV39 << 24))
+ #li t0, ((VM_SV39 << 24))
+ or t1, t0, t1
+ csrw CSR_MSTATUS, t1
+
+ la t0, (loop - 0x80000000)
+ csrw CSR_MEPC, t0
+
+ # Exit supervisor mode, entering user mode at loop.
+ mret
+
+loop:
+ la t0, data
+ lw t1, 0(t0)
+ j loop
+
+ .section .data
+data:
+ .word 0xbead
+
+ .balign 0x1000
+page_table:
+ .word ((0x80000000 >> 2) | PTE_V | PTE_TYPE_URWX_SRWX)
+ .word 0
--- /dev/null
+ .global main
+main:
+ j main
+
+write_regs:
+ sd x1, 0(a0)
+ sd x2, 8(a0)
+ sd x3, 16(a0)
+ sd x4, 24(a0)
+ sd x5, 32(a0)
+ sd x6, 40(a0)
+ sd x7, 48(a0)
+ sd x8, 56(a0)
+ sd x9, 64(a0)
+ sd x11, 72(a0)
+ sd x12, 80(a0)
+ sd x13, 88(a0)
+ sd x14, 96(a0)
+ sd x15, 104(a0)
+ sd x16, 112(a0)
+ sd x17, 120(a0)
+ sd x18, 128(a0)
+ sd x19, 136(a0)
+ sd x20, 144(a0)
+ sd x21, 152(a0)
+ sd x22, 160(a0)
+ sd x23, 168(a0)
+ sd x24, 176(a0)
+ sd x25, 184(a0)
+ sd x26, 192(a0)
+ sd x27, 200(a0)
+ sd x28, 208(a0)
+ sd x29, 216(a0)
+ sd x30, 224(a0)
+ sd x31, 232(a0)
+
+ csrr x1, 1 # fflags
+
+all_done:
+ j all_done
+
+data:
+ .fill 64, 8, 0
--- /dev/null
+ .global _start
+
+_start:
+ la sp, stack_end
+ jal main
+done:
+ j done
+
+ .data
+stack:
+ .fill 4096, 1, 0
+stack_end:
--- /dev/null
+#ifndef ENTRY_S
+#define ENTRY_S
+
+#include "encoding.h"
+
+#define STACK_SIZE ((1 << 12) - 128)
+
+#ifdef __riscv64
+# define LREG ld
+# define SREG sd
+# define REGBYTES 8
+#else
+# define LREG lw
+# define SREG sw
+# define REGBYTES 4
+#endif
+
+ .section .text.entry
+ .globl _start
+_start:
+ j handle_reset
+
+nmi_vector:
+ j nmi_vector
+
+trap_vector:
+ j trap_entry
+
+handle_reset:
+ la t0, trap_entry
+ csrw mtvec, t0
+ csrwi mstatus, 0
+ csrwi mideleg, 0
+ csrwi medeleg, 0
+ csrwi mie, 0
+
+ # initialize global pointer
+ la gp, _gp
+
+ # initialize stack pointer
+ la sp, stack_top
+
+ # perform the rest of initialization in C
+ j _init
+
+trap_entry:
+ addi sp, sp, -32*REGBYTES
+
+ SREG x1, 1*REGBYTES(sp)
+ SREG x2, 2*REGBYTES(sp)
+ SREG x3, 3*REGBYTES(sp)
+ SREG x4, 4*REGBYTES(sp)
+ SREG x5, 5*REGBYTES(sp)
+ SREG x6, 6*REGBYTES(sp)
+ SREG x7, 7*REGBYTES(sp)
+ SREG x8, 8*REGBYTES(sp)
+ SREG x9, 9*REGBYTES(sp)
+ SREG x10, 10*REGBYTES(sp)
+ SREG x11, 11*REGBYTES(sp)
+ SREG x12, 12*REGBYTES(sp)
+ SREG x13, 13*REGBYTES(sp)
+ SREG x14, 14*REGBYTES(sp)
+ SREG x15, 15*REGBYTES(sp)
+ SREG x16, 16*REGBYTES(sp)
+ SREG x17, 17*REGBYTES(sp)
+ SREG x18, 18*REGBYTES(sp)
+ SREG x19, 19*REGBYTES(sp)
+ SREG x20, 20*REGBYTES(sp)
+ SREG x21, 21*REGBYTES(sp)
+ SREG x22, 22*REGBYTES(sp)
+ SREG x23, 23*REGBYTES(sp)
+ SREG x24, 24*REGBYTES(sp)
+ SREG x25, 25*REGBYTES(sp)
+ SREG x26, 26*REGBYTES(sp)
+ SREG x27, 27*REGBYTES(sp)
+ SREG x28, 28*REGBYTES(sp)
+ SREG x29, 29*REGBYTES(sp)
+ SREG x30, 30*REGBYTES(sp)
+ SREG x31, 31*REGBYTES(sp)
+
+ csrr a0, mcause
+ csrr a1, mepc
+ mv a2, sp
+ jal handle_trap
+ csrw mepc, a0
+
+ # Remain in M-mode after mret
+ li t0, MSTATUS_MPP
+ csrs mstatus, t0
+
+ LREG x1, 1*REGBYTES(sp)
+ LREG x2, 2*REGBYTES(sp)
+ LREG x3, 3*REGBYTES(sp)
+ LREG x4, 4*REGBYTES(sp)
+ LREG x5, 5*REGBYTES(sp)
+ LREG x6, 6*REGBYTES(sp)
+ LREG x7, 7*REGBYTES(sp)
+ LREG x8, 8*REGBYTES(sp)
+ LREG x9, 9*REGBYTES(sp)
+ LREG x10, 10*REGBYTES(sp)
+ LREG x11, 11*REGBYTES(sp)
+ LREG x12, 12*REGBYTES(sp)
+ LREG x13, 13*REGBYTES(sp)
+ LREG x14, 14*REGBYTES(sp)
+ LREG x15, 15*REGBYTES(sp)
+ LREG x16, 16*REGBYTES(sp)
+ LREG x17, 17*REGBYTES(sp)
+ LREG x18, 18*REGBYTES(sp)
+ LREG x19, 19*REGBYTES(sp)
+ LREG x20, 20*REGBYTES(sp)
+ LREG x21, 21*REGBYTES(sp)
+ LREG x22, 22*REGBYTES(sp)
+ LREG x23, 23*REGBYTES(sp)
+ LREG x24, 24*REGBYTES(sp)
+ LREG x25, 25*REGBYTES(sp)
+ LREG x26, 26*REGBYTES(sp)
+ LREG x27, 27*REGBYTES(sp)
+ LREG x28, 28*REGBYTES(sp)
+ LREG x29, 29*REGBYTES(sp)
+ LREG x30, 30*REGBYTES(sp)
+ LREG x31, 31*REGBYTES(sp)
+
+ addi sp, sp, 32*REGBYTES
+ mret
+
+ .bss
+ .align 4
+stack_bottom:
+ .skip STACK_SIZE
+stack_top:
+#endif
--- /dev/null
+OUTPUT_ARCH( "riscv" )
+
+SECTIONS
+{
+ . = 0x60040000;
+ .text :
+ {
+ *(.text.entry)
+ *(.text)
+ }
+
+ /* data segment */
+ .data : { *(.data) }
+
+ .sdata : {
+ _gp = . + 0x800;
+ *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2)
+ *(.srodata*)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+
+ . = 0x80000000;
+
+ /* bss segment */
+ .sbss : {
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ }
+ .bss : { *(.bss) }
+
+ /* End of uninitalized data segement */
+ _end = .;
+ _heap_end = .;
+}
+
--- /dev/null
+adapter_khz 100
+
+source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1525600b
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+
+#reset_config trst_and_srst separate
+# Stupid long so I can see the LEDs
+#adapter_nsrst_delay 2000
+#jtag_ntrst_delay 1000
+#
+init
+#reset
--- /dev/null
+import os.path
+import pexpect
+import shlex
+import subprocess
+import tempfile
+import testlib
+import unittest
+
+# Note that gdb comes with its own testsuite. I was unable to figure out how to
+# run that testsuite against the spike simulator.
+
+def find_file(path):
+ for directory in (os.getcwd(), os.path.dirname(testlib.__file__)):
+ fullpath = os.path.join(directory, path)
+ if os.path.exists(fullpath):
+ return fullpath
+ return None
+
+def compile(*args):
+ """Compile a single .c file into a binary."""
+ dst = os.path.splitext(args[0])[0]
+ cc = os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gcc")
+ cmd = [cc, "-g", "-O", "-o", dst]
+ for arg in args:
+ found = find_file(arg)
+ if found:
+ cmd.append(found)
+ else:
+ cmd.append(arg)
+ cmd = " ".join(cmd)
+ result = os.system(cmd)
+ assert result == 0, "%r failed" % cmd
+ return dst
+
+def unused_port():
+ # http://stackoverflow.com/questions/2838244/get-open-tcp-port-in-python/2838309#2838309
+ import socket
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(("",0))
+ port = s.getsockname()[1]
+ s.close()
+ return port
+
+class Spike(object):
+ def __init__(self, binary, halted=False, with_gdb=True, timeout=None):
+ """Launch spike. Return tuple of its process and the port it's running on."""
+ cmd = []
+ if timeout:
+ cmd += ["timeout", str(timeout)]
+
+ cmd += [find_file("spike")]
+ if halted:
+ cmd.append('-H')
+ if with_gdb:
+ self.port = unused_port()
+ cmd += ['--gdb-port', str(self.port)]
+ cmd.append('pk')
+ if binary:
+ cmd.append(binary)
+ logfile = open("spike.log", "w")
+ self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile,
+ stderr=logfile)
+
+ def __del__(self):
+ try:
+ self.process.kill()
+ self.process.wait()
+ except OSError:
+ pass
+
+ def wait(self, *args, **kwargs):
+ return self.process.wait(*args, **kwargs)
+
+class Openocd(object):
+ def __init__(self, cmd=None, config=None, debug=True):
+ if cmd:
+ cmd = shlex.split(cmd)
+ else:
+ cmd = ["openocd"]
+ if config:
+ cmd += ["-f", find_file(config)]
+ if debug:
+ cmd.append("-d")
+ logfile = open("openocd.log", "w")
+ self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile,
+ stderr=logfile)
+ # TODO: Pick a random port
+ self.port = 3333
+
+ def __del__(self):
+ try:
+ self.process.kill()
+ self.process.wait()
+ except OSError:
+ pass
+
+class Gdb(object):
+ def __init__(self):
+ path = os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb")
+ self.child = pexpect.spawn(path)
+ self.child.logfile = file("gdb.log", "w")
+ self.wait()
+ self.command("set width 0")
+ self.command("set height 0")
+ # Force consistency.
+ self.command("set print entry-values no")
+
+ def wait(self):
+ """Wait for prompt."""
+ self.child.expect("\(gdb\)")
+
+ def command(self, command, timeout=-1):
+ self.child.sendline(command)
+ self.child.expect("\n", timeout=timeout)
+ self.child.expect("\(gdb\)", timeout=timeout)
+ return self.child.before.strip()
+
+ def c(self, wait=True):
+ if wait:
+ return self.command("c")
+ else:
+ self.child.sendline("c")
+ self.child.expect("Continuing")
+
+ def interrupt(self):
+ self.child.send("\003");
+ self.child.expect("\(gdb\)")
+
+ def x(self, address, size='w'):
+ output = self.command("x/%s %s" % (size, address))
+ value = int(output.split(':')[1].strip(), 0)
+ return value
+
+ def p(self, obj):
+ output = self.command("p %s" % obj)
+ value = int(output.split('=')[-1].strip())
+ return value
+
+ def stepi(self):
+ return self.command("stepi")