Add test for memory read from invalid address.
[riscv-tests.git] / debug / testlib.py
1 import os.path
2 import re
3 import shlex
4 import subprocess
5 import time
6
7 import pexpect
8
9 # Note that gdb comes with its own testsuite. I was unable to figure out how to
10 # run that testsuite against the spike simulator.
11
12 def find_file(path):
13 for directory in (os.getcwd(), os.path.dirname(__file__)):
14 fullpath = os.path.join(directory, path)
15 if os.path.exists(fullpath):
16 return fullpath
17 return None
18
19 def compile(args, xlen=32): # pylint: disable=redefined-builtin
20 cc = os.path.expandvars("$RISCV/bin/riscv%d-unknown-elf-gcc" % xlen)
21 cmd = [cc, "-g"]
22 for arg in args:
23 found = find_file(arg)
24 if found:
25 cmd.append(found)
26 else:
27 cmd.append(arg)
28 cmd = " ".join(cmd)
29 result = os.system(cmd)
30 assert result == 0, "%r failed" % cmd
31
32 def unused_port():
33 # http://stackoverflow.com/questions/2838244/get-open-tcp-port-in-python/2838309#2838309
34 import socket
35 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
36 s.bind(("", 0))
37 port = s.getsockname()[1]
38 s.close()
39 return port
40
41 class Spike(object):
42 logname = "spike.log"
43
44 def __init__(self, cmd, binary=None, halted=False, with_gdb=True,
45 timeout=None, xlen=64):
46 """Launch spike. Return tuple of its process and the port it's running
47 on."""
48 if cmd:
49 cmd = shlex.split(cmd)
50 else:
51 cmd = ["spike"]
52 if xlen == 32:
53 cmd += ["--isa", "RV32"]
54
55 if timeout:
56 cmd = ["timeout", str(timeout)] + cmd
57
58 if halted:
59 cmd.append('-H')
60 if with_gdb:
61 self.port = unused_port()
62 cmd += ['--gdb-port', str(self.port)]
63 cmd.append("-m32")
64 cmd.append('pk')
65 if binary:
66 cmd.append(binary)
67 logfile = open(self.logname, "w")
68 logfile.write("+ %s\n" % " ".join(cmd))
69 logfile.flush()
70 self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
71 stdout=logfile, stderr=logfile)
72
73 def __del__(self):
74 try:
75 self.process.kill()
76 self.process.wait()
77 except OSError:
78 pass
79
80 def wait(self, *args, **kwargs):
81 return self.process.wait(*args, **kwargs)
82
83 class VcsSim(object):
84 def __init__(self, simv=None, debug=False):
85 if simv:
86 cmd = shlex.split(simv)
87 else:
88 cmd = ["simv"]
89 cmd += ["+jtag_vpi_enable"]
90 if debug:
91 cmd[0] = cmd[0] + "-debug"
92 cmd += ["+vcdplusfile=output/gdbserver.vpd"]
93 logfile = open("simv.log", "w")
94 logfile.write("+ %s\n" % " ".join(cmd))
95 logfile.flush()
96 listenfile = open("simv.log", "r")
97 listenfile.seek(0, 2)
98 self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
99 stdout=logfile, stderr=logfile)
100 done = False
101 while not done:
102 line = listenfile.readline()
103 if not line:
104 time.sleep(1)
105 if "Listening on port 5555" in line:
106 done = True
107
108 def __del__(self):
109 try:
110 self.process.kill()
111 self.process.wait()
112 except OSError:
113 pass
114
115 class Openocd(object):
116 logname = "openocd.log"
117
118 def __init__(self, cmd=None, config=None, debug=False, otherProcess=None):
119
120 # keep handles to other processes -- don't let them be
121 # garbage collected yet.
122
123 self.otherProcess = otherProcess
124 if cmd:
125 cmd = shlex.split(cmd)
126 else:
127 cmd = ["openocd"]
128 if config:
129 cmd += ["-f", find_file(config)]
130 if debug:
131 cmd.append("-d")
132 logfile = open(Openocd.logname, "w")
133 logfile.write("+ %s\n" % " ".join(cmd))
134 logfile.flush()
135 self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
136 stdout=logfile, stderr=logfile)
137 # TODO: Pick a random port
138 self.port = 3333
139
140 def __del__(self):
141 try:
142 self.process.kill()
143 self.process.wait()
144 except OSError:
145 pass
146
147 class CannotAccess(Exception):
148 def __init__(self, address):
149 Exception.__init__(self)
150 self.address = address
151
152 class Gdb(object):
153 def __init__(self,
154 cmd=os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb")):
155 self.child = pexpect.spawn(cmd)
156 self.child.logfile = open("gdb.log", "w")
157 self.child.logfile.write("+ %s\n" % cmd)
158 self.wait()
159 self.command("set confirm off")
160 self.command("set width 0")
161 self.command("set height 0")
162 # Force consistency.
163 self.command("set print entry-values no")
164
165 def wait(self):
166 """Wait for prompt."""
167 self.child.expect(r"\(gdb\)")
168
169 def command(self, command, timeout=-1):
170 self.child.sendline(command)
171 self.child.expect("\n", timeout=timeout)
172 self.child.expect(r"\(gdb\)", timeout=timeout)
173 return self.child.before.strip()
174
175 def c(self, wait=True):
176 if wait:
177 output = self.command("c")
178 assert "Continuing" in output
179 return output
180 else:
181 self.child.sendline("c")
182 self.child.expect("Continuing")
183
184 def interrupt(self):
185 self.child.send("\003")
186 self.child.expect(r"\(gdb\)", timeout=60)
187 return self.child.before.strip()
188
189 def x(self, address, size='w'):
190 output = self.command("x/%s %s" % (size, address))
191 value = int(output.split(':')[1].strip(), 0)
192 return value
193
194 def p(self, obj):
195 output = self.command("p/x %s" % obj)
196 m = re.search("Cannot access memory at address (0x[0-9a-f]+)", output)
197 if m:
198 raise CannotAccess(int(m.group(1), 0))
199 value = int(output.split('=')[-1].strip(), 0)
200 return value
201
202 def p_string(self, obj):
203 output = self.command("p %s" % obj)
204 value = shlex.split(output.split('=')[-1].strip())[1]
205 return value
206
207 def stepi(self):
208 output = self.command("stepi")
209 return output
210
211 def load(self):
212 output = self.command("load", timeout=60)
213 assert "failed" not in output
214 assert "Transfer rate" in output
215
216 def b(self, location):
217 output = self.command("b %s" % location)
218 assert "not defined" not in output
219 assert "Breakpoint" in output
220 return output
221
222 def hbreak(self, location):
223 output = self.command("hbreak %s" % location)
224 assert "not defined" not in output
225 assert "Hardware assisted breakpoint" in output
226 return output