Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 1 | """ |
| 2 | Base class for gdb-remote test cases. |
| 3 | """ |
| 4 | |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 5 | import errno |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 6 | import os |
| 7 | import os.path |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 8 | import platform |
Todd Fiala | 9e2d329 | 2014-07-09 23:10:43 +0000 | [diff] [blame] | 9 | import random |
Todd Fiala | e220200 | 2014-06-27 22:11:56 +0000 | [diff] [blame] | 10 | import re |
Todd Fiala | 31bde32 | 2014-07-26 20:39:17 +0000 | [diff] [blame] | 11 | import select |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 12 | import sets |
| 13 | import signal |
| 14 | import socket |
| 15 | import subprocess |
| 16 | import sys |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 17 | import tempfile |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 18 | import time |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 19 | import unittest2 |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 20 | from lldbtest import * |
| 21 | from lldbgdbserverutils import * |
| 22 | import logging |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 23 | |
| 24 | class GdbRemoteTestCaseBase(TestBase): |
| 25 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 26 | _TIMEOUT_SECONDS = 5 |
| 27 | |
| 28 | _GDBREMOTE_KILL_PACKET = "$k#6b" |
| 29 | |
| 30 | _LOGGING_LEVEL = logging.WARNING |
| 31 | # _LOGGING_LEVEL = logging.DEBUG |
| 32 | |
Todd Fiala | 7306cf3 | 2014-07-29 22:30:01 +0000 | [diff] [blame] | 33 | # Start the inferior separately, attach to the inferior on the stub command line. |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 34 | _STARTUP_ATTACH = "attach" |
Todd Fiala | 7306cf3 | 2014-07-29 22:30:01 +0000 | [diff] [blame] | 35 | # Start the inferior separately, start the stub without attaching, allow the test to attach to the inferior however it wants (e.g. $vAttach;pid). |
| 36 | _STARTUP_ATTACH_MANUALLY = "attach_manually" |
| 37 | # Start the stub, and launch the inferior with an $A packet via the initial packet stream. |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 38 | _STARTUP_LAUNCH = "launch" |
| 39 | |
| 40 | # GDB Signal numbers that are not target-specific used for common exceptions |
| 41 | TARGET_EXC_BAD_ACCESS = 0x91 |
| 42 | TARGET_EXC_BAD_INSTRUCTION = 0x92 |
| 43 | TARGET_EXC_ARITHMETIC = 0x93 |
| 44 | TARGET_EXC_EMULATION = 0x94 |
| 45 | TARGET_EXC_SOFTWARE = 0x95 |
| 46 | TARGET_EXC_BREAKPOINT = 0x96 |
| 47 | |
| 48 | def setUp(self): |
| 49 | TestBase.setUp(self) |
| 50 | FORMAT = '%(asctime)-15s %(levelname)-8s %(message)s' |
| 51 | logging.basicConfig(format=FORMAT) |
| 52 | self.logger = logging.getLogger(__name__) |
| 53 | self.logger.setLevel(self._LOGGING_LEVEL) |
| 54 | self.test_sequence = GdbRemoteTestSequence(self.logger) |
| 55 | self.set_inferior_startup_launch() |
Todd Fiala | 9e2d329 | 2014-07-09 23:10:43 +0000 | [diff] [blame] | 56 | self.port = self.get_next_port() |
Todd Fiala | 6704119 | 2014-07-11 22:50:13 +0000 | [diff] [blame] | 57 | self.named_pipe_path = None |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 58 | self.named_pipe = None |
| 59 | self.named_pipe_fd = None |
Todd Fiala | f9ad21d | 2014-07-16 16:15:42 +0000 | [diff] [blame] | 60 | self.stub_sends_two_stop_notifications_on_kill = False |
Chaoren Lin | f62b7fc | 2015-05-05 20:34:36 +0000 | [diff] [blame] | 61 | if lldb.platform_url: |
Chaoren Lin | f9c4c42 | 2015-05-06 01:28:04 +0000 | [diff] [blame] | 62 | scheme, host = re.match('(.+)://(.+):\d+', lldb.platform_url).groups() |
Chaoren Lin | 28b8ea1 | 2015-05-07 01:42:53 +0000 | [diff] [blame] | 63 | if scheme == 'adb': |
| 64 | self.stub_device = host |
| 65 | self.stub_hostname = 'localhost' |
| 66 | else: |
| 67 | self.stub_device = None |
| 68 | self.stub_hostname = host |
Tamas Berghammer | de78679 | 2015-03-30 10:52:32 +0000 | [diff] [blame] | 69 | else: |
| 70 | self.stub_hostname = "localhost" |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 71 | |
Todd Fiala | 9e2d329 | 2014-07-09 23:10:43 +0000 | [diff] [blame] | 72 | def get_next_port(self): |
| 73 | return 12000 + random.randint(0,3999) |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 74 | |
| 75 | def reset_test_sequence(self): |
| 76 | self.test_sequence = GdbRemoteTestSequence(self.logger) |
| 77 | |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 78 | def create_named_pipe(self): |
| 79 | # Create a temp dir and name for a pipe. |
| 80 | temp_dir = tempfile.mkdtemp() |
| 81 | named_pipe_path = os.path.join(temp_dir, "stub_port_number") |
| 82 | |
| 83 | # Create the named pipe. |
| 84 | os.mkfifo(named_pipe_path) |
| 85 | |
| 86 | # Open the read side of the pipe in non-blocking mode. This will return right away, ready or not. |
| 87 | named_pipe_fd = os.open(named_pipe_path, os.O_RDONLY | os.O_NONBLOCK) |
| 88 | |
| 89 | # Create the file for the named pipe. Note this will follow semantics of |
| 90 | # a non-blocking read side of a named pipe, which has different semantics |
| 91 | # than a named pipe opened for read in non-blocking mode. |
| 92 | named_pipe = os.fdopen(named_pipe_fd, "r") |
| 93 | self.assertIsNotNone(named_pipe) |
| 94 | |
| 95 | def shutdown_named_pipe(): |
| 96 | # Close the pipe. |
| 97 | try: |
| 98 | named_pipe.close() |
| 99 | except: |
| 100 | print "failed to close named pipe" |
| 101 | None |
| 102 | |
| 103 | # Delete the pipe. |
| 104 | try: |
| 105 | os.remove(named_pipe_path) |
| 106 | except: |
| 107 | print "failed to delete named pipe: {}".format(named_pipe_path) |
| 108 | None |
| 109 | |
| 110 | # Delete the temp directory. |
| 111 | try: |
| 112 | os.rmdir(temp_dir) |
| 113 | except: |
| 114 | print "failed to delete temp dir: {}, directory contents: '{}'".format(temp_dir, os.listdir(temp_dir)) |
| 115 | None |
| 116 | |
| 117 | # Add the shutdown hook to clean up the named pipe. |
| 118 | self.addTearDownHook(shutdown_named_pipe) |
| 119 | |
| 120 | # Clear the port so the stub selects a port number. |
| 121 | self.port = 0 |
| 122 | |
| 123 | return (named_pipe_path, named_pipe, named_pipe_fd) |
| 124 | |
| 125 | def get_stub_port_from_named_socket(self, read_timeout_seconds=5): |
| 126 | # Wait for something to read with a max timeout. |
| 127 | (ready_readers, _, _) = select.select([self.named_pipe_fd], [], [], read_timeout_seconds) |
| 128 | self.assertIsNotNone(ready_readers, "write side of pipe has not written anything - stub isn't writing to pipe.") |
| 129 | self.assertNotEqual(len(ready_readers), 0, "write side of pipe has not written anything - stub isn't writing to pipe.") |
| 130 | |
| 131 | # Read the port from the named pipe. |
| 132 | stub_port_raw = self.named_pipe.read() |
| 133 | self.assertIsNotNone(stub_port_raw) |
| 134 | self.assertNotEqual(len(stub_port_raw), 0, "no content to read on pipe") |
| 135 | |
| 136 | # Trim null byte, convert to int. |
| 137 | stub_port_raw = stub_port_raw[:-1] |
| 138 | stub_port = int(stub_port_raw) |
| 139 | self.assertTrue(stub_port > 0) |
| 140 | |
| 141 | return stub_port |
| 142 | |
| 143 | def init_llgs_test(self, use_named_pipe=True): |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 144 | if lldb.remote_platform: |
| 145 | # Remote platforms don't support named pipe based port negotiation |
| 146 | use_named_pipe = False |
| 147 | |
| 148 | platform = self.dbg.GetSelectedPlatform() |
| 149 | |
| 150 | shell_command = lldb.SBPlatformShellCommand("echo $PPID") |
| 151 | err = platform.Run(shell_command) |
| 152 | if err.Fail(): |
| 153 | raise Exception("remote_platform.RunShellCommand('echo $PPID') failed: %s" % err) |
| 154 | pid = shell_command.GetOutput().strip() |
| 155 | |
| 156 | shell_command = lldb.SBPlatformShellCommand("readlink /proc/%s/exe" % pid) |
| 157 | err = platform.Run(shell_command) |
| 158 | if err.Fail(): |
| 159 | raise Exception("remote_platform.RunShellCommand('readlink /proc/%d/exe') failed: %s" % (pid, err)) |
Ying Chen | cd81914 | 2015-05-08 01:47:17 +0000 | [diff] [blame] | 160 | # If the binary has been deleted, the link name has " (deleted)" appended. |
| 161 | # Remove if it's there. |
Ying Chen | c543052 | 2015-05-08 01:25:10 +0000 | [diff] [blame] | 162 | self.debug_monitor_exe = re.sub(r' \(deleted\)$', '', shell_command.GetOutput().strip()) |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 163 | else: |
| 164 | self.debug_monitor_exe = get_lldb_server_exe() |
| 165 | if not self.debug_monitor_exe: |
| 166 | self.skipTest("lldb-server exe not found") |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 167 | |
Vince Harron | f1ff3c1 | 2015-05-10 18:02:53 +0000 | [diff] [blame] | 168 | self.debug_monitor_extra_args = ["gdbserver"] |
Vince Harron | 1f16037 | 2015-05-21 18:51:20 +0000 | [diff] [blame] | 169 | |
| 170 | if len(lldbtest_config.channels) > 0: |
| 171 | self.debug_monitor_extra_args.append("--log-file={}-server.log".format(self.log_basename)) |
| 172 | self.debug_monitor_extra_args.append("--log-channels={}".format(":".join(lldbtest_config.channels))) |
| 173 | |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 174 | if use_named_pipe: |
| 175 | (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 176 | |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 177 | def init_debugserver_test(self, use_named_pipe=True): |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 178 | self.debug_monitor_exe = get_debugserver_exe() |
| 179 | if not self.debug_monitor_exe: |
| 180 | self.skipTest("debugserver exe not found") |
Vince Harron | 1f16037 | 2015-05-21 18:51:20 +0000 | [diff] [blame] | 181 | self.debug_monitor_extra_args = ["--log-file={}-server.log".format(self.log_basename), "--log-flags=0x800000"] |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 182 | if use_named_pipe: |
| 183 | (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() |
Todd Fiala | f9ad21d | 2014-07-16 16:15:42 +0000 | [diff] [blame] | 184 | # The debugserver stub has a race on handling the 'k' command, so it sends an X09 right away, then sends the real X notification |
| 185 | # when the process truly dies. |
| 186 | self.stub_sends_two_stop_notifications_on_kill = True |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 187 | |
Chaoren Lin | 28b8ea1 | 2015-05-07 01:42:53 +0000 | [diff] [blame] | 188 | def forward_adb_port(self, source, target, direction, device): |
| 189 | adb = [ 'adb' ] + ([ '-s', device ] if device else []) + [ direction ] |
Tamas Berghammer | 27c8d36 | 2015-03-13 14:32:25 +0000 | [diff] [blame] | 190 | def remove_port_forward(): |
Chaoren Lin | 28b8ea1 | 2015-05-07 01:42:53 +0000 | [diff] [blame] | 191 | subprocess.call(adb + [ "--remove", "tcp:%d" % source]) |
| 192 | |
| 193 | subprocess.call(adb + [ "tcp:%d" % source, "tcp:%d" % target]) |
Tamas Berghammer | 27c8d36 | 2015-03-13 14:32:25 +0000 | [diff] [blame] | 194 | self.addTearDownHook(remove_port_forward) |
| 195 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 196 | def create_socket(self): |
| 197 | sock = socket.socket() |
| 198 | logger = self.logger |
| 199 | |
Tamas Berghammer | de78679 | 2015-03-30 10:52:32 +0000 | [diff] [blame] | 200 | triple = self.dbg.GetSelectedPlatform().GetTriple() |
| 201 | if re.match(".*-.*-.*-android", triple): |
Chaoren Lin | 28b8ea1 | 2015-05-07 01:42:53 +0000 | [diff] [blame] | 202 | self.forward_adb_port(self.port, self.port, "forward", self.stub_device) |
Tamas Berghammer | de78679 | 2015-03-30 10:52:32 +0000 | [diff] [blame] | 203 | |
| 204 | connect_info = (self.stub_hostname, self.port) |
| 205 | sock.connect(connect_info) |
| 206 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 207 | def shutdown_socket(): |
| 208 | if sock: |
| 209 | try: |
Robert Flack | 8cc4cf1 | 2015-03-06 14:36:33 +0000 | [diff] [blame] | 210 | # send the kill packet so lldb-server shuts down gracefully |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 211 | sock.sendall(GdbRemoteTestCaseBase._GDBREMOTE_KILL_PACKET) |
| 212 | except: |
| 213 | logger.warning("failed to send kill packet to debug monitor: {}; ignoring".format(sys.exc_info()[0])) |
| 214 | |
| 215 | try: |
| 216 | sock.close() |
| 217 | except: |
| 218 | logger.warning("failed to close socket to debug monitor: {}; ignoring".format(sys.exc_info()[0])) |
| 219 | |
| 220 | self.addTearDownHook(shutdown_socket) |
| 221 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 222 | return sock |
| 223 | |
| 224 | def set_inferior_startup_launch(self): |
| 225 | self._inferior_startup = self._STARTUP_LAUNCH |
| 226 | |
| 227 | def set_inferior_startup_attach(self): |
| 228 | self._inferior_startup = self._STARTUP_ATTACH |
| 229 | |
Todd Fiala | 7306cf3 | 2014-07-29 22:30:01 +0000 | [diff] [blame] | 230 | def set_inferior_startup_attach_manually(self): |
| 231 | self._inferior_startup = self._STARTUP_ATTACH_MANUALLY |
| 232 | |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 233 | def get_debug_monitor_command_line_args(self, attach_pid=None): |
Tamas Berghammer | de78679 | 2015-03-30 10:52:32 +0000 | [diff] [blame] | 234 | if lldb.remote_platform: |
| 235 | commandline_args = self.debug_monitor_extra_args + ["*:{}".format(self.port)] |
| 236 | else: |
| 237 | commandline_args = self.debug_monitor_extra_args + ["localhost:{}".format(self.port)] |
| 238 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 239 | if attach_pid: |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 240 | commandline_args += ["--attach=%d" % attach_pid] |
Todd Fiala | 6704119 | 2014-07-11 22:50:13 +0000 | [diff] [blame] | 241 | if self.named_pipe_path: |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 242 | commandline_args += ["--named-pipe", self.named_pipe_path] |
| 243 | return commandline_args |
| 244 | |
| 245 | def run_platform_command(self, cmd): |
| 246 | platform = self.dbg.GetSelectedPlatform() |
| 247 | shell_command = lldb.SBPlatformShellCommand(cmd) |
| 248 | err = platform.Run(shell_command) |
| 249 | return (err, shell_command.GetOutput()) |
Todd Fiala | 31bde32 | 2014-07-26 20:39:17 +0000 | [diff] [blame] | 250 | |
| 251 | def launch_debug_monitor(self, attach_pid=None, logfile=None): |
| 252 | # Create the command line. |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 253 | commandline_args = self.get_debug_monitor_command_line_args(attach_pid=attach_pid) |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 254 | |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 255 | # Start the server. |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 256 | server = self.spawnSubprocess(self.debug_monitor_exe, commandline_args, install_remote=False) |
| 257 | self.addTearDownHook(self.cleanupSubprocesses) |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 258 | self.assertIsNotNone(server) |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 259 | |
| 260 | # If we're receiving the stub's listening port from the named pipe, do that here. |
| 261 | if self.named_pipe: |
| 262 | self.port = self.get_stub_port_from_named_socket() |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 263 | |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 264 | return server |
| 265 | |
| 266 | def connect_to_debug_monitor(self, attach_pid=None): |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 267 | if self.named_pipe: |
| 268 | # Create the stub. |
| 269 | server = self.launch_debug_monitor(attach_pid=attach_pid) |
| 270 | self.assertIsNotNone(server) |
| 271 | |
| 272 | def shutdown_debug_monitor(): |
| 273 | try: |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 274 | server.terminate() |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 275 | except: |
Ilia K | 7c1d91d | 2015-04-15 13:08:35 +0000 | [diff] [blame] | 276 | logger.warning("failed to terminate server for debug monitor: {}; ignoring".format(sys.exc_info()[0])) |
Todd Fiala | 24189d4 | 2014-07-14 06:24:44 +0000 | [diff] [blame] | 277 | self.addTearDownHook(shutdown_debug_monitor) |
| 278 | |
| 279 | # Schedule debug monitor to be shut down during teardown. |
| 280 | logger = self.logger |
| 281 | |
| 282 | # Attach to the stub and return a socket opened to it. |
| 283 | self.sock = self.create_socket() |
| 284 | return server |
| 285 | |
| 286 | # We're using a random port algorithm to try not to collide with other ports, |
| 287 | # and retry a max # times. |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 288 | attempts = 0 |
| 289 | MAX_ATTEMPTS = 20 |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 290 | |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 291 | while attempts < MAX_ATTEMPTS: |
Todd Fiala | 9e2d329 | 2014-07-09 23:10:43 +0000 | [diff] [blame] | 292 | server = self.launch_debug_monitor(attach_pid=attach_pid) |
| 293 | |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 294 | # Schedule debug monitor to be shut down during teardown. |
| 295 | logger = self.logger |
| 296 | def shutdown_debug_monitor(): |
Todd Fiala | 9e2d329 | 2014-07-09 23:10:43 +0000 | [diff] [blame] | 297 | try: |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 298 | server.terminate() |
| 299 | except: |
| 300 | logger.warning("failed to terminate server for debug monitor: {}; ignoring".format(sys.exc_info()[0])) |
| 301 | self.addTearDownHook(shutdown_debug_monitor) |
| 302 | |
Tamas Berghammer | de78679 | 2015-03-30 10:52:32 +0000 | [diff] [blame] | 303 | connect_attemps = 0 |
| 304 | MAX_CONNECT_ATTEMPTS = 10 |
| 305 | |
| 306 | while connect_attemps < MAX_CONNECT_ATTEMPTS: |
| 307 | # Create a socket to talk to the server |
| 308 | try: |
| 309 | self.sock = self.create_socket() |
| 310 | return server |
| 311 | except socket.error as serr: |
| 312 | # We're only trying to handle connection refused. |
| 313 | if serr.errno != errno.ECONNREFUSED: |
| 314 | raise serr |
| 315 | time.sleep(0.5) |
| 316 | connect_attemps += 1 |
| 317 | |
| 318 | # We should close the server here to be safe. |
| 319 | server.terminate() |
Todd Fiala | 9e2d329 | 2014-07-09 23:10:43 +0000 | [diff] [blame] | 320 | |
| 321 | # Increment attempts. |
| 322 | print("connect to debug monitor on port %d failed, attempt #%d of %d" % (self.port, attempts + 1, MAX_ATTEMPTS)) |
| 323 | attempts += 1 |
| 324 | |
| 325 | # And wait a random length of time before next attempt, to avoid collisions. |
| 326 | time.sleep(random.randint(1,5)) |
| 327 | |
| 328 | # Now grab a new port number. |
| 329 | self.port = self.get_next_port() |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 330 | |
| 331 | raise Exception("failed to create a socket to the launched debug monitor after %d tries" % attempts) |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 332 | |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 333 | def launch_process_for_attach(self, inferior_args=None, sleep_seconds=3, exe_path=None): |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 334 | # We're going to start a child process that the debug monitor stub can later attach to. |
| 335 | # This process needs to be started so that it just hangs around for a while. We'll |
| 336 | # have it sleep. |
Todd Fiala | 58a2f66 | 2014-08-12 17:02:07 +0000 | [diff] [blame] | 337 | if not exe_path: |
| 338 | exe_path = os.path.abspath("a.out") |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 339 | |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 340 | args = [] |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 341 | if inferior_args: |
| 342 | args.extend(inferior_args) |
| 343 | if sleep_seconds: |
| 344 | args.append("sleep:%d" % sleep_seconds) |
| 345 | |
Ilia K | 7c1d91d | 2015-04-15 13:08:35 +0000 | [diff] [blame] | 346 | inferior = self.spawnSubprocess(exe_path, args) |
| 347 | def shutdown_process_for_attach(): |
| 348 | try: |
| 349 | inferior.terminate() |
| 350 | except: |
| 351 | logger.warning("failed to terminate inferior process for attach: {}; ignoring".format(sys.exc_info()[0])) |
| 352 | self.addTearDownHook(shutdown_process_for_attach) |
| 353 | return inferior |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 354 | |
Todd Fiala | 58a2f66 | 2014-08-12 17:02:07 +0000 | [diff] [blame] | 355 | def prep_debug_monitor_and_inferior(self, inferior_args=None, inferior_sleep_seconds=3, inferior_exe_path=None): |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 356 | """Prep the debug monitor, the inferior, and the expected packet stream. |
| 357 | |
| 358 | Handle the separate cases of using the debug monitor in attach-to-inferior mode |
| 359 | and in launch-inferior mode. |
| 360 | |
| 361 | For attach-to-inferior mode, the inferior process is first started, then |
| 362 | the debug monitor is started in attach to pid mode (using --attach on the |
| 363 | stub command line), and the no-ack-mode setup is appended to the packet |
| 364 | stream. The packet stream is not yet executed, ready to have more expected |
| 365 | packet entries added to it. |
| 366 | |
| 367 | For launch-inferior mode, the stub is first started, then no ack mode is |
| 368 | setup on the expected packet stream, then the verified launch packets are added |
| 369 | to the expected socket stream. The packet stream is not yet executed, ready |
| 370 | to have more expected packet entries added to it. |
| 371 | |
| 372 | The return value is: |
| 373 | {inferior:<inferior>, server:<server>} |
| 374 | """ |
| 375 | inferior = None |
| 376 | attach_pid = None |
| 377 | |
Todd Fiala | 7306cf3 | 2014-07-29 22:30:01 +0000 | [diff] [blame] | 378 | if self._inferior_startup == self._STARTUP_ATTACH or self._inferior_startup == self._STARTUP_ATTACH_MANUALLY: |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 379 | # Launch the process that we'll use as the inferior. |
Todd Fiala | 58a2f66 | 2014-08-12 17:02:07 +0000 | [diff] [blame] | 380 | inferior = self.launch_process_for_attach(inferior_args=inferior_args, sleep_seconds=inferior_sleep_seconds, exe_path=inferior_exe_path) |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 381 | self.assertIsNotNone(inferior) |
| 382 | self.assertTrue(inferior.pid > 0) |
Todd Fiala | 7306cf3 | 2014-07-29 22:30:01 +0000 | [diff] [blame] | 383 | if self._inferior_startup == self._STARTUP_ATTACH: |
| 384 | # In this case, we want the stub to attach via the command line, so set the command line attach pid here. |
| 385 | attach_pid = inferior.pid |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 386 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 387 | if self._inferior_startup == self._STARTUP_LAUNCH: |
| 388 | # Build launch args |
Todd Fiala | 58a2f66 | 2014-08-12 17:02:07 +0000 | [diff] [blame] | 389 | if not inferior_exe_path: |
| 390 | inferior_exe_path = os.path.abspath("a.out") |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 391 | |
| 392 | if lldb.remote_platform: |
Chaoren Lin | 3e2bdb4 | 2015-05-11 17:53:39 +0000 | [diff] [blame] | 393 | remote_path = lldbutil.append_to_remote_wd(os.path.basename(inferior_exe_path)) |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 394 | remote_file_spec = lldb.SBFileSpec(remote_path, False) |
| 395 | err = lldb.remote_platform.Install(lldb.SBFileSpec(inferior_exe_path, True), remote_file_spec) |
| 396 | if err.Fail(): |
| 397 | raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (inferior_exe_path, remote_path, err)) |
| 398 | inferior_exe_path = remote_path |
| 399 | |
Todd Fiala | 58a2f66 | 2014-08-12 17:02:07 +0000 | [diff] [blame] | 400 | launch_args = [inferior_exe_path] |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 401 | if inferior_args: |
| 402 | launch_args.extend(inferior_args) |
| 403 | |
Tamas Berghammer | 04f51d1 | 2015-03-11 13:51:07 +0000 | [diff] [blame] | 404 | # Launch the debug monitor stub, attaching to the inferior. |
| 405 | server = self.connect_to_debug_monitor(attach_pid=attach_pid) |
| 406 | self.assertIsNotNone(server) |
| 407 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 408 | # Build the expected protocol stream |
| 409 | self.add_no_ack_remote_stream() |
| 410 | if self._inferior_startup == self._STARTUP_LAUNCH: |
| 411 | self.add_verified_launch_packets(launch_args) |
| 412 | |
| 413 | return {"inferior":inferior, "server":server} |
| 414 | |
Todd Fiala | 31bde32 | 2014-07-26 20:39:17 +0000 | [diff] [blame] | 415 | def expect_socket_recv(self, sock, expected_content_regex, timeout_seconds): |
| 416 | response = "" |
| 417 | timeout_time = time.time() + timeout_seconds |
| 418 | |
| 419 | while not expected_content_regex.match(response) and time.time() < timeout_time: |
| 420 | can_read, _, _ = select.select([sock], [], [], timeout_seconds) |
| 421 | if can_read and sock in can_read: |
| 422 | recv_bytes = sock.recv(4096) |
| 423 | if recv_bytes: |
| 424 | response += recv_bytes |
| 425 | |
| 426 | self.assertTrue(expected_content_regex.match(response)) |
| 427 | |
| 428 | def expect_socket_send(self, sock, content, timeout_seconds): |
| 429 | request_bytes_remaining = content |
| 430 | timeout_time = time.time() + timeout_seconds |
| 431 | |
| 432 | while len(request_bytes_remaining) > 0 and time.time() < timeout_time: |
| 433 | _, can_write, _ = select.select([], [sock], [], timeout_seconds) |
| 434 | if can_write and sock in can_write: |
| 435 | written_byte_count = sock.send(request_bytes_remaining) |
| 436 | request_bytes_remaining = request_bytes_remaining[written_byte_count:] |
| 437 | self.assertEquals(len(request_bytes_remaining), 0) |
| 438 | |
| 439 | def do_handshake(self, stub_socket, timeout_seconds=5): |
| 440 | # Write the ack. |
| 441 | self.expect_socket_send(stub_socket, "+", timeout_seconds) |
| 442 | |
| 443 | # Send the start no ack mode packet. |
| 444 | NO_ACK_MODE_REQUEST = "$QStartNoAckMode#b0" |
| 445 | bytes_sent = stub_socket.send(NO_ACK_MODE_REQUEST) |
| 446 | self.assertEquals(bytes_sent, len(NO_ACK_MODE_REQUEST)) |
| 447 | |
| 448 | # Receive the ack and "OK" |
| 449 | self.expect_socket_recv(stub_socket, re.compile(r"^\+\$OK#[0-9a-fA-F]{2}$"), timeout_seconds) |
| 450 | |
| 451 | # Send the final ack. |
| 452 | self.expect_socket_send(stub_socket, "+", timeout_seconds) |
| 453 | |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 454 | def add_no_ack_remote_stream(self): |
| 455 | self.test_sequence.add_log_lines( |
| 456 | ["read packet: +", |
| 457 | "read packet: $QStartNoAckMode#b0", |
| 458 | "send packet: +", |
| 459 | "send packet: $OK#9a", |
| 460 | "read packet: +"], |
| 461 | True) |
| 462 | |
| 463 | def add_verified_launch_packets(self, launch_args): |
| 464 | self.test_sequence.add_log_lines( |
| 465 | ["read packet: %s" % build_gdbremote_A_packet(launch_args), |
| 466 | "send packet: $OK#00", |
| 467 | "read packet: $qLaunchSuccess#a5", |
| 468 | "send packet: $OK#00"], |
| 469 | True) |
| 470 | |
| 471 | def add_thread_suffix_request_packets(self): |
| 472 | self.test_sequence.add_log_lines( |
Stephane Sezer | 22ed42e | 2014-11-13 21:39:24 +0000 | [diff] [blame] | 473 | ["read packet: $QThreadSuffixSupported#e4", |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 474 | "send packet: $OK#00", |
| 475 | ], True) |
| 476 | |
| 477 | def add_process_info_collection_packets(self): |
| 478 | self.test_sequence.add_log_lines( |
Stephane Sezer | 22ed42e | 2014-11-13 21:39:24 +0000 | [diff] [blame] | 479 | ["read packet: $qProcessInfo#dc", |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 480 | { "direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"process_info_raw"} }], |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 481 | True) |
| 482 | |
| 483 | _KNOWN_PROCESS_INFO_KEYS = [ |
| 484 | "pid", |
| 485 | "parent-pid", |
| 486 | "real-uid", |
| 487 | "real-gid", |
| 488 | "effective-uid", |
| 489 | "effective-gid", |
| 490 | "cputype", |
| 491 | "cpusubtype", |
| 492 | "ostype", |
Todd Fiala | c540dd0 | 2014-08-26 18:21:02 +0000 | [diff] [blame] | 493 | "triple", |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 494 | "vendor", |
| 495 | "endian", |
| 496 | "ptrsize" |
| 497 | ] |
| 498 | |
| 499 | def parse_process_info_response(self, context): |
| 500 | # Ensure we have a process info response. |
| 501 | self.assertIsNotNone(context) |
| 502 | process_info_raw = context.get("process_info_raw") |
| 503 | self.assertIsNotNone(process_info_raw) |
| 504 | |
| 505 | # Pull out key:value; pairs. |
| 506 | process_info_dict = { match.group(1):match.group(2) for match in re.finditer(r"([^:]+):([^;]+);", process_info_raw) } |
| 507 | |
| 508 | # Validate keys are known. |
| 509 | for (key, val) in process_info_dict.items(): |
| 510 | self.assertTrue(key in self._KNOWN_PROCESS_INFO_KEYS) |
| 511 | self.assertIsNotNone(val) |
| 512 | |
| 513 | return process_info_dict |
| 514 | |
| 515 | def add_register_info_collection_packets(self): |
| 516 | self.test_sequence.add_log_lines( |
| 517 | [ { "type":"multi_response", "query":"qRegisterInfo", "append_iteration_suffix":True, |
| 518 | "end_regex":re.compile(r"^\$(E\d+)?#[0-9a-fA-F]{2}$"), |
| 519 | "save_key":"reg_info_responses" } ], |
| 520 | True) |
| 521 | |
| 522 | def parse_register_info_packets(self, context): |
| 523 | """Return an array of register info dictionaries, one per register info.""" |
| 524 | reg_info_responses = context.get("reg_info_responses") |
| 525 | self.assertIsNotNone(reg_info_responses) |
| 526 | |
| 527 | # Parse register infos. |
| 528 | return [parse_reg_info_response(reg_info_response) for reg_info_response in reg_info_responses] |
| 529 | |
Todd Fiala | 50a211b | 2014-06-14 22:00:36 +0000 | [diff] [blame] | 530 | def expect_gdbremote_sequence(self, timeout_seconds=None): |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 531 | if not timeout_seconds: |
| 532 | timeout_seconds = self._TIMEOUT_SECONDS |
| 533 | return expect_lldb_gdbserver_replay(self, self.sock, self.test_sequence, timeout_seconds, self.logger) |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 534 | |
| 535 | _KNOWN_REGINFO_KEYS = [ |
| 536 | "name", |
| 537 | "alt-name", |
| 538 | "bitsize", |
| 539 | "offset", |
| 540 | "encoding", |
| 541 | "format", |
| 542 | "set", |
| 543 | "gcc", |
| 544 | "dwarf", |
| 545 | "generic", |
| 546 | "container-regs", |
| 547 | "invalidate-regs" |
| 548 | ] |
| 549 | |
| 550 | def assert_valid_reg_info(self, reg_info): |
| 551 | # Assert we know about all the reginfo keys parsed. |
| 552 | for key in reg_info: |
| 553 | self.assertTrue(key in self._KNOWN_REGINFO_KEYS) |
| 554 | |
| 555 | # Check the bare-minimum expected set of register info keys. |
| 556 | self.assertTrue("name" in reg_info) |
| 557 | self.assertTrue("bitsize" in reg_info) |
| 558 | self.assertTrue("offset" in reg_info) |
| 559 | self.assertTrue("encoding" in reg_info) |
| 560 | self.assertTrue("format" in reg_info) |
| 561 | |
| 562 | def find_pc_reg_info(self, reg_infos): |
| 563 | lldb_reg_index = 0 |
| 564 | for reg_info in reg_infos: |
| 565 | if ("generic" in reg_info) and (reg_info["generic"] == "pc"): |
| 566 | return (lldb_reg_index, reg_info) |
| 567 | lldb_reg_index += 1 |
| 568 | |
| 569 | return (None, None) |
| 570 | |
| 571 | def add_lldb_register_index(self, reg_infos): |
| 572 | """Add a "lldb_register_index" key containing the 0-baed index of each reg_infos entry. |
| 573 | |
| 574 | We'll use this when we want to call packets like P/p with a register index but do so |
| 575 | on only a subset of the full register info set. |
| 576 | """ |
| 577 | self.assertIsNotNone(reg_infos) |
| 578 | |
| 579 | reg_index = 0 |
| 580 | for reg_info in reg_infos: |
| 581 | reg_info["lldb_register_index"] = reg_index |
| 582 | reg_index += 1 |
| 583 | |
| 584 | def add_query_memory_region_packets(self, address): |
| 585 | self.test_sequence.add_log_lines( |
| 586 | ["read packet: $qMemoryRegionInfo:{0:x}#00".format(address), |
| 587 | {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"memory_region_response"} }], |
| 588 | True) |
| 589 | |
Todd Fiala | c30281a | 2014-06-14 03:03:23 +0000 | [diff] [blame] | 590 | def parse_key_val_dict(self, key_val_text, allow_dupes=True): |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 591 | self.assertIsNotNone(key_val_text) |
| 592 | kv_dict = {} |
| 593 | for match in re.finditer(r";?([^:]+):([^;]+)", key_val_text): |
Todd Fiala | c30281a | 2014-06-14 03:03:23 +0000 | [diff] [blame] | 594 | key = match.group(1) |
| 595 | val = match.group(2) |
| 596 | if key in kv_dict: |
| 597 | if allow_dupes: |
| 598 | if type(kv_dict[key]) == list: |
| 599 | kv_dict[key].append(val) |
| 600 | else: |
| 601 | # Promote to list |
| 602 | kv_dict[key] = [kv_dict[key], val] |
| 603 | else: |
| 604 | self.fail("key '{}' already present when attempting to add value '{}' (text='{}', dict={})".format(key, val, key_val_text, kv_dict)) |
| 605 | else: |
| 606 | kv_dict[key] = val |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 607 | return kv_dict |
| 608 | |
| 609 | def parse_memory_region_packet(self, context): |
| 610 | # Ensure we have a context. |
| 611 | self.assertIsNotNone(context.get("memory_region_response")) |
| 612 | |
| 613 | # Pull out key:value; pairs. |
| 614 | mem_region_dict = self.parse_key_val_dict(context.get("memory_region_response")) |
| 615 | |
| 616 | # Validate keys are known. |
| 617 | for (key, val) in mem_region_dict.items(): |
| 618 | self.assertTrue(key in ["start", "size", "permissions", "error"]) |
| 619 | self.assertIsNotNone(val) |
| 620 | |
| 621 | # Return the dictionary of key-value pairs for the memory region. |
| 622 | return mem_region_dict |
| 623 | |
| 624 | def assert_address_within_memory_region(self, test_address, mem_region_dict): |
| 625 | self.assertIsNotNone(mem_region_dict) |
| 626 | self.assertTrue("start" in mem_region_dict) |
| 627 | self.assertTrue("size" in mem_region_dict) |
| 628 | |
| 629 | range_start = int(mem_region_dict["start"], 16) |
| 630 | range_size = int(mem_region_dict["size"], 16) |
| 631 | range_end = range_start + range_size |
| 632 | |
| 633 | if test_address < range_start: |
| 634 | self.fail("address 0x{0:x} comes before range 0x{1:x} - 0x{2:x} (size 0x{3:x})".format(test_address, range_start, range_end, range_size)) |
| 635 | elif test_address >= range_end: |
| 636 | self.fail("address 0x{0:x} comes after range 0x{1:x} - 0x{2:x} (size 0x{3:x})".format(test_address, range_start, range_end, range_size)) |
| 637 | |
| 638 | def add_threadinfo_collection_packets(self): |
| 639 | self.test_sequence.add_log_lines( |
| 640 | [ { "type":"multi_response", "first_query":"qfThreadInfo", "next_query":"qsThreadInfo", |
| 641 | "append_iteration_suffix":False, "end_regex":re.compile(r"^\$(l)?#[0-9a-fA-F]{2}$"), |
| 642 | "save_key":"threadinfo_responses" } ], |
| 643 | True) |
| 644 | |
| 645 | def parse_threadinfo_packets(self, context): |
| 646 | """Return an array of thread ids (decimal ints), one per thread.""" |
| 647 | threadinfo_responses = context.get("threadinfo_responses") |
| 648 | self.assertIsNotNone(threadinfo_responses) |
| 649 | |
| 650 | thread_ids = [] |
| 651 | for threadinfo_response in threadinfo_responses: |
| 652 | new_thread_infos = parse_threadinfo_response(threadinfo_response) |
| 653 | thread_ids.extend(new_thread_infos) |
| 654 | return thread_ids |
| 655 | |
| 656 | def wait_for_thread_count(self, thread_count, timeout_seconds=3): |
| 657 | start_time = time.time() |
| 658 | timeout_time = start_time + timeout_seconds |
| 659 | |
| 660 | actual_thread_count = 0 |
| 661 | while actual_thread_count < thread_count: |
| 662 | self.reset_test_sequence() |
| 663 | self.add_threadinfo_collection_packets() |
| 664 | |
| 665 | context = self.expect_gdbremote_sequence() |
| 666 | self.assertIsNotNone(context) |
| 667 | |
| 668 | threads = self.parse_threadinfo_packets(context) |
| 669 | self.assertIsNotNone(threads) |
| 670 | |
| 671 | actual_thread_count = len(threads) |
| 672 | |
| 673 | if time.time() > timeout_time: |
| 674 | raise Exception( |
| 675 | 'timed out after {} seconds while waiting for theads: waiting for at least {} threads, found {}'.format( |
| 676 | timeout_seconds, thread_count, actual_thread_count)) |
| 677 | |
| 678 | return threads |
| 679 | |
| 680 | def add_set_breakpoint_packets(self, address, do_continue=True, breakpoint_kind=1): |
| 681 | self.test_sequence.add_log_lines( |
| 682 | [# Set the breakpoint. |
| 683 | "read packet: $Z0,{0:x},{1}#00".format(address, breakpoint_kind), |
| 684 | # Verify the stub could set it. |
| 685 | "send packet: $OK#00", |
| 686 | ], True) |
| 687 | |
| 688 | if (do_continue): |
| 689 | self.test_sequence.add_log_lines( |
| 690 | [# Continue the inferior. |
Stephane Sezer | 22ed42e | 2014-11-13 21:39:24 +0000 | [diff] [blame] | 691 | "read packet: $c#63", |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 692 | # Expect a breakpoint stop report. |
| 693 | {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, |
| 694 | ], True) |
| 695 | |
| 696 | def add_remove_breakpoint_packets(self, address, breakpoint_kind=1): |
| 697 | self.test_sequence.add_log_lines( |
| 698 | [# Remove the breakpoint. |
| 699 | "read packet: $z0,{0:x},{1}#00".format(address, breakpoint_kind), |
| 700 | # Verify the stub could unset it. |
| 701 | "send packet: $OK#00", |
| 702 | ], True) |
| 703 | |
| 704 | def add_qSupported_packets(self): |
| 705 | self.test_sequence.add_log_lines( |
| 706 | ["read packet: $qSupported#00", |
| 707 | {"direction":"send", "regex":r"^\$(.*)#[0-9a-fA-F]{2}", "capture":{1: "qSupported_response"}}, |
| 708 | ], True) |
| 709 | |
| 710 | _KNOWN_QSUPPORTED_STUB_FEATURES = [ |
| 711 | "augmented-libraries-svr4-read", |
| 712 | "PacketSize", |
| 713 | "QStartNoAckMode", |
Todd Fiala | 8aae4f4 | 2014-06-13 23:34:17 +0000 | [diff] [blame] | 714 | "QThreadSuffixSupported", |
Todd Fiala | 43ab82c | 2014-06-15 23:33:09 +0000 | [diff] [blame] | 715 | "QListThreadsInStopReply", |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 716 | "qXfer:auxv:read", |
| 717 | "qXfer:libraries:read", |
| 718 | "qXfer:libraries-svr4:read", |
Ying Chen | 294c926 | 2015-05-26 23:08:09 +0000 | [diff] [blame] | 719 | "qXfer:features:read", |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 720 | ] |
| 721 | |
| 722 | def parse_qSupported_response(self, context): |
| 723 | self.assertIsNotNone(context) |
| 724 | |
| 725 | raw_response = context.get("qSupported_response") |
| 726 | self.assertIsNotNone(raw_response) |
| 727 | |
| 728 | # For values with key=val, the dict key and vals are set as expected. For feature+, feature- and feature?, the |
| 729 | # +,-,? is stripped from the key and set as the value. |
| 730 | supported_dict = {} |
| 731 | for match in re.finditer(r";?([^=;]+)(=([^;]+))?", raw_response): |
| 732 | key = match.group(1) |
| 733 | val = match.group(3) |
| 734 | |
| 735 | # key=val: store as is |
| 736 | if val and len(val) > 0: |
| 737 | supported_dict[key] = val |
| 738 | else: |
| 739 | if len(key) < 2: |
| 740 | raise Exception("singular stub feature is too short: must be stub_feature{+,-,?}") |
| 741 | supported_type = key[-1] |
| 742 | key = key[:-1] |
| 743 | if not supported_type in ["+", "-", "?"]: |
| 744 | raise Exception("malformed stub feature: final character {} not in expected set (+,-,?)".format(supported_type)) |
| 745 | supported_dict[key] = supported_type |
| 746 | # Ensure we know the supported element |
| 747 | if not key in self._KNOWN_QSUPPORTED_STUB_FEATURES: |
| 748 | raise Exception("unknown qSupported stub feature reported: %s" % key) |
| 749 | |
| 750 | return supported_dict |
| 751 | |
| 752 | def run_process_then_stop(self, run_seconds=1): |
| 753 | # Tell the stub to continue. |
| 754 | self.test_sequence.add_log_lines( |
Stephane Sezer | b6e8192 | 2014-11-20 18:50:46 +0000 | [diff] [blame] | 755 | ["read packet: $vCont;c#a8"], |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 756 | True) |
| 757 | context = self.expect_gdbremote_sequence() |
| 758 | |
| 759 | # Wait for run_seconds. |
| 760 | time.sleep(run_seconds) |
| 761 | |
| 762 | # Send an interrupt, capture a T response. |
| 763 | self.reset_test_sequence() |
| 764 | self.test_sequence.add_log_lines( |
| 765 | ["read packet: {}".format(chr(03)), |
| 766 | {"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result"} }], |
| 767 | True) |
| 768 | context = self.expect_gdbremote_sequence() |
| 769 | self.assertIsNotNone(context) |
| 770 | self.assertIsNotNone(context.get("stop_result")) |
| 771 | |
| 772 | return context |
| 773 | |
| 774 | def select_modifiable_register(self, reg_infos): |
| 775 | """Find a register that can be read/written freely.""" |
| 776 | PREFERRED_REGISTER_NAMES = sets.Set(["rax",]) |
| 777 | |
| 778 | # First check for the first register from the preferred register name set. |
| 779 | alternative_register_index = None |
| 780 | |
| 781 | self.assertIsNotNone(reg_infos) |
| 782 | for reg_info in reg_infos: |
| 783 | if ("name" in reg_info) and (reg_info["name"] in PREFERRED_REGISTER_NAMES): |
| 784 | # We found a preferred register. Use it. |
| 785 | return reg_info["lldb_register_index"] |
| 786 | if ("generic" in reg_info) and (reg_info["generic"] == "fp"): |
| 787 | # A frame pointer register will do as a register to modify temporarily. |
| 788 | alternative_register_index = reg_info["lldb_register_index"] |
| 789 | |
| 790 | # We didn't find a preferred register. Return whatever alternative register |
| 791 | # we found, if any. |
| 792 | return alternative_register_index |
| 793 | |
| 794 | def extract_registers_from_stop_notification(self, stop_key_vals_text): |
| 795 | self.assertIsNotNone(stop_key_vals_text) |
| 796 | kv_dict = self.parse_key_val_dict(stop_key_vals_text) |
| 797 | |
| 798 | registers = {} |
| 799 | for (key, val) in kv_dict.items(): |
Stephane Sezer | 5109a79 | 2014-11-14 09:46:21 +0000 | [diff] [blame] | 800 | if re.match(r"^[0-9a-fA-F]+$", key): |
Todd Fiala | e50b2e4 | 2014-06-13 19:11:33 +0000 | [diff] [blame] | 801 | registers[int(key, 16)] = val |
| 802 | return registers |
| 803 | |
| 804 | def gather_register_infos(self): |
| 805 | self.reset_test_sequence() |
| 806 | self.add_register_info_collection_packets() |
| 807 | |
| 808 | context = self.expect_gdbremote_sequence() |
| 809 | self.assertIsNotNone(context) |
| 810 | |
| 811 | reg_infos = self.parse_register_info_packets(context) |
| 812 | self.assertIsNotNone(reg_infos) |
| 813 | self.add_lldb_register_index(reg_infos) |
| 814 | |
| 815 | return reg_infos |
| 816 | |
| 817 | def find_generic_register_with_name(self, reg_infos, generic_name): |
| 818 | self.assertIsNotNone(reg_infos) |
| 819 | for reg_info in reg_infos: |
| 820 | if ("generic" in reg_info) and (reg_info["generic"] == generic_name): |
| 821 | return reg_info |
| 822 | return None |
| 823 | |
Todd Fiala | 8d7ab8c | 2014-06-17 16:04:45 +0000 | [diff] [blame] | 824 | def decode_gdbremote_binary(self, encoded_bytes): |
| 825 | decoded_bytes = "" |
| 826 | i = 0 |
| 827 | while i < len(encoded_bytes): |
| 828 | if encoded_bytes[i] == "}": |
| 829 | # Handle escaped char. |
| 830 | self.assertTrue(i + 1 < len(encoded_bytes)) |
| 831 | decoded_bytes += chr(ord(encoded_bytes[i+1]) ^ 0x20) |
| 832 | i +=2 |
| 833 | elif encoded_bytes[i] == "*": |
| 834 | # Handle run length encoding. |
| 835 | self.assertTrue(len(decoded_bytes) > 0) |
| 836 | self.assertTrue(i + 1 < len(encoded_bytes)) |
| 837 | repeat_count = ord(encoded_bytes[i+1]) - 29 |
| 838 | decoded_bytes += decoded_bytes[-1] * repeat_count |
| 839 | i += 2 |
| 840 | else: |
| 841 | decoded_bytes += encoded_bytes[i] |
| 842 | i += 1 |
| 843 | return decoded_bytes |
| 844 | |
| 845 | def build_auxv_dict(self, endian, word_size, auxv_data): |
| 846 | self.assertIsNotNone(endian) |
| 847 | self.assertIsNotNone(word_size) |
| 848 | self.assertIsNotNone(auxv_data) |
| 849 | |
| 850 | auxv_dict = {} |
| 851 | |
| 852 | while len(auxv_data) > 0: |
| 853 | # Chop off key. |
| 854 | raw_key = auxv_data[:word_size] |
| 855 | auxv_data = auxv_data[word_size:] |
| 856 | |
| 857 | # Chop of value. |
| 858 | raw_value = auxv_data[:word_size] |
| 859 | auxv_data = auxv_data[word_size:] |
| 860 | |
| 861 | # Convert raw text from target endian. |
| 862 | key = unpack_endian_binary_string(endian, raw_key) |
| 863 | value = unpack_endian_binary_string(endian, raw_value) |
| 864 | |
| 865 | # Handle ending entry. |
| 866 | if key == 0: |
| 867 | self.assertEquals(value, 0) |
| 868 | return auxv_dict |
| 869 | |
| 870 | # The key should not already be present. |
| 871 | self.assertFalse(key in auxv_dict) |
| 872 | auxv_dict[key] = value |
| 873 | |
| 874 | self.fail("should not reach here - implies required double zero entry not found") |
| 875 | return auxv_dict |
Todd Fiala | 5188673 | 2014-06-17 22:01:27 +0000 | [diff] [blame] | 876 | |
| 877 | def read_binary_data_in_chunks(self, command_prefix, chunk_length): |
| 878 | """Collect command_prefix{offset:x},{chunk_length:x} until a single 'l' or 'l' with data is returned.""" |
| 879 | offset = 0 |
| 880 | done = False |
| 881 | decoded_data = "" |
| 882 | |
| 883 | while not done: |
| 884 | # Grab the next iteration of data. |
| 885 | self.reset_test_sequence() |
| 886 | self.test_sequence.add_log_lines([ |
| 887 | "read packet: ${}{:x},{:x}:#00".format(command_prefix, offset, chunk_length), |
Todd Fiala | 4c24eba | 2014-06-19 17:35:40 +0000 | [diff] [blame] | 888 | {"direction":"send", "regex":re.compile(r"^\$([^E])(.*)#[0-9a-fA-F]{2}$", re.MULTILINE|re.DOTALL), "capture":{1:"response_type", 2:"content_raw"} } |
Todd Fiala | 5188673 | 2014-06-17 22:01:27 +0000 | [diff] [blame] | 889 | ], True) |
| 890 | |
| 891 | context = self.expect_gdbremote_sequence() |
| 892 | self.assertIsNotNone(context) |
| 893 | |
| 894 | response_type = context.get("response_type") |
| 895 | self.assertIsNotNone(response_type) |
| 896 | self.assertTrue(response_type in ["l", "m"]) |
| 897 | |
| 898 | # Move offset along. |
| 899 | offset += chunk_length |
| 900 | |
| 901 | # Figure out if we're done. We're done if the response type is l. |
| 902 | done = response_type == "l" |
| 903 | |
| 904 | # Decode binary data. |
| 905 | content_raw = context.get("content_raw") |
| 906 | if content_raw and len(content_raw) > 0: |
| 907 | self.assertIsNotNone(content_raw) |
| 908 | decoded_data += self.decode_gdbremote_binary(content_raw) |
| 909 | return decoded_data |
Todd Fiala | 4c24eba | 2014-06-19 17:35:40 +0000 | [diff] [blame] | 910 | |
| 911 | def add_interrupt_packets(self): |
| 912 | self.test_sequence.add_log_lines([ |
| 913 | # Send the intterupt. |
| 914 | "read packet: {}".format(chr(03)), |
| 915 | # And wait for the stop notification. |
| 916 | {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})(.*)#[0-9a-fA-F]{2}$", "capture":{1:"stop_signo", 2:"stop_key_val_text" } }, |
| 917 | ], True) |
| 918 | |
| 919 | def parse_interrupt_packets(self, context): |
| 920 | self.assertIsNotNone(context.get("stop_signo")) |
| 921 | self.assertIsNotNone(context.get("stop_key_val_text")) |
Todd Fiala | 9846d45 | 2014-06-20 17:39:24 +0000 | [diff] [blame] | 922 | return (int(context["stop_signo"], 16), self.parse_key_val_dict(context["stop_key_val_text"])) |
| 923 | |
| 924 | def add_QSaveRegisterState_packets(self, thread_id): |
| 925 | if thread_id: |
| 926 | # Use the thread suffix form. |
| 927 | request = "read packet: $QSaveRegisterState;thread:{:x}#00".format(thread_id) |
| 928 | else: |
| 929 | request = "read packet: $QSaveRegisterState#00" |
| 930 | |
| 931 | self.test_sequence.add_log_lines([ |
| 932 | request, |
| 933 | {"direction":"send", "regex":r"^\$(E?.*)#[0-9a-fA-F]{2}$", "capture":{1:"save_response" } }, |
| 934 | ], True) |
| 935 | |
| 936 | def parse_QSaveRegisterState_response(self, context): |
| 937 | self.assertIsNotNone(context) |
| 938 | |
| 939 | save_response = context.get("save_response") |
| 940 | self.assertIsNotNone(save_response) |
| 941 | |
| 942 | if len(save_response) < 1 or save_response[0] == "E": |
| 943 | # error received |
| 944 | return (False, None) |
| 945 | else: |
| 946 | return (True, int(save_response)) |
| 947 | |
| 948 | def add_QRestoreRegisterState_packets(self, save_id, thread_id=None): |
| 949 | if thread_id: |
| 950 | # Use the thread suffix form. |
| 951 | request = "read packet: $QRestoreRegisterState:{};thread:{:x}#00".format(save_id, thread_id) |
| 952 | else: |
| 953 | request = "read packet: $QRestoreRegisterState:{}#00".format(save_id) |
| 954 | |
| 955 | self.test_sequence.add_log_lines([ |
| 956 | request, |
| 957 | "send packet: $OK#00" |
| 958 | ], True) |
| 959 | |
| 960 | def flip_all_bits_in_each_register_value(self, reg_infos, endian, thread_id=None): |
| 961 | self.assertIsNotNone(reg_infos) |
| 962 | |
| 963 | successful_writes = 0 |
| 964 | failed_writes = 0 |
| 965 | |
| 966 | for reg_info in reg_infos: |
| 967 | # Use the lldb register index added to the reg info. We're not necessarily |
| 968 | # working off a full set of register infos, so an inferred register index could be wrong. |
| 969 | reg_index = reg_info["lldb_register_index"] |
| 970 | self.assertIsNotNone(reg_index) |
| 971 | |
| 972 | reg_byte_size = int(reg_info["bitsize"])/8 |
| 973 | self.assertTrue(reg_byte_size > 0) |
| 974 | |
| 975 | # Handle thread suffix. |
| 976 | if thread_id: |
| 977 | p_request = "read packet: $p{:x};thread:{:x}#00".format(reg_index, thread_id) |
| 978 | else: |
| 979 | p_request = "read packet: $p{:x}#00".format(reg_index) |
| 980 | |
| 981 | # Read the existing value. |
| 982 | self.reset_test_sequence() |
| 983 | self.test_sequence.add_log_lines([ |
| 984 | p_request, |
| 985 | { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, |
| 986 | ], True) |
| 987 | context = self.expect_gdbremote_sequence() |
| 988 | self.assertIsNotNone(context) |
| 989 | |
| 990 | # Verify the response length. |
| 991 | p_response = context.get("p_response") |
| 992 | self.assertIsNotNone(p_response) |
| 993 | initial_reg_value = unpack_register_hex_unsigned(endian, p_response) |
| 994 | |
| 995 | # Flip the value by xoring with all 1s |
| 996 | all_one_bits_raw = "ff" * (int(reg_info["bitsize"]) / 8) |
| 997 | flipped_bits_int = initial_reg_value ^ int(all_one_bits_raw, 16) |
| 998 | # print "reg (index={}, name={}): val={}, flipped bits (int={}, hex={:x})".format(reg_index, reg_info["name"], initial_reg_value, flipped_bits_int, flipped_bits_int) |
| 999 | |
| 1000 | # Handle thread suffix for P. |
| 1001 | if thread_id: |
| 1002 | P_request = "read packet: $P{:x}={};thread:{:x}#00".format(reg_index, pack_register_hex(endian, flipped_bits_int, byte_size=reg_byte_size), thread_id) |
| 1003 | else: |
| 1004 | P_request = "read packet: $P{:x}={}#00".format(reg_index, pack_register_hex(endian, flipped_bits_int, byte_size=reg_byte_size)) |
| 1005 | |
| 1006 | # Write the flipped value to the register. |
| 1007 | self.reset_test_sequence() |
| 1008 | self.test_sequence.add_log_lines([ |
| 1009 | P_request, |
| 1010 | { "direction":"send", "regex":r"^\$(OK|E[0-9a-fA-F]+)#[0-9a-fA-F]{2}", "capture":{1:"P_response"} }, |
| 1011 | ], True) |
| 1012 | context = self.expect_gdbremote_sequence() |
| 1013 | self.assertIsNotNone(context) |
| 1014 | |
| 1015 | # Determine if the write succeeded. There are a handful of registers that can fail, or partially fail |
| 1016 | # (e.g. flags, segment selectors, etc.) due to register value restrictions. Don't worry about them |
| 1017 | # all flipping perfectly. |
| 1018 | P_response = context.get("P_response") |
| 1019 | self.assertIsNotNone(P_response) |
| 1020 | if P_response == "OK": |
| 1021 | successful_writes += 1 |
| 1022 | else: |
| 1023 | failed_writes += 1 |
| 1024 | # print "reg (index={}, name={}) write FAILED (error: {})".format(reg_index, reg_info["name"], P_response) |
| 1025 | |
| 1026 | # Read back the register value, ensure it matches the flipped value. |
| 1027 | if P_response == "OK": |
| 1028 | self.reset_test_sequence() |
| 1029 | self.test_sequence.add_log_lines([ |
| 1030 | p_request, |
| 1031 | { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, |
| 1032 | ], True) |
| 1033 | context = self.expect_gdbremote_sequence() |
| 1034 | self.assertIsNotNone(context) |
| 1035 | |
| 1036 | verify_p_response_raw = context.get("p_response") |
| 1037 | self.assertIsNotNone(verify_p_response_raw) |
| 1038 | verify_bits = unpack_register_hex_unsigned(endian, verify_p_response_raw) |
| 1039 | |
| 1040 | if verify_bits != flipped_bits_int: |
| 1041 | # Some registers, like mxcsrmask and others, will permute what's written. Adjust succeed/fail counts. |
| 1042 | # print "reg (index={}, name={}): read verify FAILED: wrote {:x}, verify read back {:x}".format(reg_index, reg_info["name"], flipped_bits_int, verify_bits) |
| 1043 | successful_writes -= 1 |
| 1044 | failed_writes +=1 |
| 1045 | |
| 1046 | return (successful_writes, failed_writes) |
| 1047 | |
| 1048 | def is_bit_flippable_register(self, reg_info): |
| 1049 | if not reg_info: |
| 1050 | return False |
| 1051 | if not "set" in reg_info: |
| 1052 | return False |
| 1053 | if reg_info["set"] != "General Purpose Registers": |
| 1054 | return False |
| 1055 | if ("container-regs" in reg_info) and (len(reg_info["container-regs"]) > 0): |
| 1056 | # Don't try to bit flip registers contained in another register. |
| 1057 | return False |
| 1058 | if re.match("^.s$", reg_info["name"]): |
| 1059 | # This is a 2-letter register name that ends in "s", like a segment register. |
| 1060 | # Don't try to bit flip these. |
| 1061 | return False |
| 1062 | # Okay, this looks fine-enough. |
| 1063 | return True |
| 1064 | |
| 1065 | def read_register_values(self, reg_infos, endian, thread_id=None): |
| 1066 | self.assertIsNotNone(reg_infos) |
| 1067 | values = {} |
| 1068 | |
| 1069 | for reg_info in reg_infos: |
| 1070 | # We append a register index when load reg infos so we can work with subsets. |
| 1071 | reg_index = reg_info.get("lldb_register_index") |
| 1072 | self.assertIsNotNone(reg_index) |
| 1073 | |
| 1074 | # Handle thread suffix. |
| 1075 | if thread_id: |
| 1076 | p_request = "read packet: $p{:x};thread:{:x}#00".format(reg_index, thread_id) |
| 1077 | else: |
| 1078 | p_request = "read packet: $p{:x}#00".format(reg_index) |
| 1079 | |
| 1080 | # Read it with p. |
| 1081 | self.reset_test_sequence() |
| 1082 | self.test_sequence.add_log_lines([ |
| 1083 | p_request, |
| 1084 | { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, |
| 1085 | ], True) |
| 1086 | context = self.expect_gdbremote_sequence() |
| 1087 | self.assertIsNotNone(context) |
| 1088 | |
| 1089 | # Convert value from target endian to integral. |
| 1090 | p_response = context.get("p_response") |
| 1091 | self.assertIsNotNone(p_response) |
| 1092 | self.assertTrue(len(p_response) > 0) |
| 1093 | self.assertFalse(p_response[0] == "E") |
| 1094 | |
| 1095 | values[reg_index] = unpack_register_hex_unsigned(endian, p_response) |
| 1096 | |
Todd Fiala | e220200 | 2014-06-27 22:11:56 +0000 | [diff] [blame] | 1097 | return values |
| 1098 | |
| 1099 | def add_vCont_query_packets(self): |
| 1100 | self.test_sequence.add_log_lines([ |
Stephane Sezer | 22ed42e | 2014-11-13 21:39:24 +0000 | [diff] [blame] | 1101 | "read packet: $vCont?#49", |
Todd Fiala | e220200 | 2014-06-27 22:11:56 +0000 | [diff] [blame] | 1102 | {"direction":"send", "regex":r"^\$(vCont)?(.*)#[0-9a-fA-F]{2}$", "capture":{2:"vCont_query_response" } }, |
| 1103 | ], True) |
| 1104 | |
| 1105 | def parse_vCont_query_response(self, context): |
| 1106 | self.assertIsNotNone(context) |
| 1107 | vCont_query_response = context.get("vCont_query_response") |
| 1108 | |
| 1109 | # Handle case of no vCont support at all - in which case the capture group will be none or zero length. |
| 1110 | if not vCont_query_response or len(vCont_query_response) == 0: |
| 1111 | return {} |
| 1112 | |
| 1113 | return {key:1 for key in vCont_query_response.split(";") if key and len(key) > 0} |
| 1114 | |
| 1115 | def count_single_steps_until_true(self, thread_id, predicate, args, max_step_count=100, use_Hc_packet=True, step_instruction="s"): |
| 1116 | """Used by single step test that appears in a few different contexts.""" |
| 1117 | single_step_count = 0 |
| 1118 | |
| 1119 | while single_step_count < max_step_count: |
| 1120 | self.assertIsNotNone(thread_id) |
| 1121 | |
| 1122 | # Build the packet for the single step instruction. We replace {thread}, if present, with the thread_id. |
| 1123 | step_packet = "read packet: ${}#00".format(re.sub(r"{thread}", "{:x}".format(thread_id), step_instruction)) |
| 1124 | # print "\nstep_packet created: {}\n".format(step_packet) |
| 1125 | |
| 1126 | # Single step. |
| 1127 | self.reset_test_sequence() |
| 1128 | if use_Hc_packet: |
| 1129 | self.test_sequence.add_log_lines( |
| 1130 | [# Set the continue thread. |
| 1131 | "read packet: $Hc{0:x}#00".format(thread_id), |
| 1132 | "send packet: $OK#00", |
| 1133 | ], True) |
| 1134 | self.test_sequence.add_log_lines([ |
| 1135 | # Single step. |
| 1136 | step_packet, |
| 1137 | # "read packet: $vCont;s:{0:x}#00".format(thread_id), |
| 1138 | # Expect a breakpoint stop report. |
| 1139 | {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, |
| 1140 | ], True) |
| 1141 | context = self.expect_gdbremote_sequence() |
| 1142 | self.assertIsNotNone(context) |
| 1143 | self.assertIsNotNone(context.get("stop_signo")) |
| 1144 | self.assertEquals(int(context.get("stop_signo"), 16), signal.SIGTRAP) |
| 1145 | |
| 1146 | single_step_count += 1 |
| 1147 | |
| 1148 | # See if the predicate is true. If so, we're done. |
| 1149 | if predicate(args): |
| 1150 | return (True, single_step_count) |
| 1151 | |
| 1152 | # The predicate didn't return true within the runaway step count. |
| 1153 | return (False, single_step_count) |
| 1154 | |
| 1155 | def g_c1_c2_contents_are(self, args): |
| 1156 | """Used by single step test that appears in a few different contexts.""" |
| 1157 | g_c1_address = args["g_c1_address"] |
| 1158 | g_c2_address = args["g_c2_address"] |
| 1159 | expected_g_c1 = args["expected_g_c1"] |
| 1160 | expected_g_c2 = args["expected_g_c2"] |
| 1161 | |
| 1162 | # Read g_c1 and g_c2 contents. |
| 1163 | self.reset_test_sequence() |
| 1164 | self.test_sequence.add_log_lines( |
| 1165 | ["read packet: $m{0:x},{1:x}#00".format(g_c1_address, 1), |
| 1166 | {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"g_c1_contents"} }, |
| 1167 | "read packet: $m{0:x},{1:x}#00".format(g_c2_address, 1), |
| 1168 | {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"g_c2_contents"} }], |
| 1169 | True) |
| 1170 | |
| 1171 | # Run the packet stream. |
| 1172 | context = self.expect_gdbremote_sequence() |
| 1173 | self.assertIsNotNone(context) |
| 1174 | |
| 1175 | # Check if what we read from inferior memory is what we are expecting. |
| 1176 | self.assertIsNotNone(context.get("g_c1_contents")) |
| 1177 | self.assertIsNotNone(context.get("g_c2_contents")) |
| 1178 | |
| 1179 | return (context.get("g_c1_contents").decode("hex") == expected_g_c1) and (context.get("g_c2_contents").decode("hex") == expected_g_c2) |
| 1180 | |
| 1181 | def single_step_only_steps_one_instruction(self, use_Hc_packet=True, step_instruction="s"): |
| 1182 | """Used by single step test that appears in a few different contexts.""" |
| 1183 | # Start up the inferior. |
| 1184 | procs = self.prep_debug_monitor_and_inferior( |
| 1185 | inferior_args=["get-code-address-hex:swap_chars", "get-data-address-hex:g_c1", "get-data-address-hex:g_c2", "sleep:1", "call-function:swap_chars", "sleep:5"]) |
| 1186 | |
| 1187 | # Run the process |
| 1188 | self.test_sequence.add_log_lines( |
| 1189 | [# Start running after initial stop. |
Stephane Sezer | 22ed42e | 2014-11-13 21:39:24 +0000 | [diff] [blame] | 1190 | "read packet: $c#63", |
Todd Fiala | e220200 | 2014-06-27 22:11:56 +0000 | [diff] [blame] | 1191 | # Match output line that prints the memory address of the function call entry point. |
| 1192 | # Note we require launch-only testing so we can get inferior otuput. |
| 1193 | { "type":"output_match", "regex":r"^code address: 0x([0-9a-fA-F]+)\r\ndata address: 0x([0-9a-fA-F]+)\r\ndata address: 0x([0-9a-fA-F]+)\r\n$", |
| 1194 | "capture":{ 1:"function_address", 2:"g_c1_address", 3:"g_c2_address"} }, |
| 1195 | # Now stop the inferior. |
| 1196 | "read packet: {}".format(chr(03)), |
| 1197 | # And wait for the stop notification. |
| 1198 | {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], |
| 1199 | True) |
| 1200 | |
| 1201 | # Run the packet stream. |
| 1202 | context = self.expect_gdbremote_sequence() |
| 1203 | self.assertIsNotNone(context) |
| 1204 | |
| 1205 | # Grab the main thread id. |
| 1206 | self.assertIsNotNone(context.get("stop_thread_id")) |
| 1207 | main_thread_id = int(context.get("stop_thread_id"), 16) |
| 1208 | |
| 1209 | # Grab the function address. |
| 1210 | self.assertIsNotNone(context.get("function_address")) |
| 1211 | function_address = int(context.get("function_address"), 16) |
| 1212 | |
| 1213 | # Grab the data addresses. |
| 1214 | self.assertIsNotNone(context.get("g_c1_address")) |
| 1215 | g_c1_address = int(context.get("g_c1_address"), 16) |
| 1216 | |
| 1217 | self.assertIsNotNone(context.get("g_c2_address")) |
| 1218 | g_c2_address = int(context.get("g_c2_address"), 16) |
| 1219 | |
| 1220 | # Set a breakpoint at the given address. |
Tamas Berghammer | 157e84f | 2015-05-28 10:55:01 +0000 | [diff] [blame^] | 1221 | if self.getArchitecture() == "arm": |
| 1222 | # TODO: Handle case when setting breakpoint in thumb code |
| 1223 | BREAKPOINT_KIND = 4 |
| 1224 | else: |
| 1225 | BREAKPOINT_KIND = 1 |
Todd Fiala | e220200 | 2014-06-27 22:11:56 +0000 | [diff] [blame] | 1226 | self.reset_test_sequence() |
| 1227 | self.add_set_breakpoint_packets(function_address, do_continue=True, breakpoint_kind=BREAKPOINT_KIND) |
| 1228 | context = self.expect_gdbremote_sequence() |
| 1229 | self.assertIsNotNone(context) |
| 1230 | |
| 1231 | # Remove the breakpoint. |
| 1232 | self.reset_test_sequence() |
| 1233 | self.add_remove_breakpoint_packets(function_address, breakpoint_kind=BREAKPOINT_KIND) |
| 1234 | context = self.expect_gdbremote_sequence() |
| 1235 | self.assertIsNotNone(context) |
| 1236 | |
| 1237 | # Verify g_c1 and g_c2 match expected initial state. |
| 1238 | args = {} |
| 1239 | args["g_c1_address"] = g_c1_address |
| 1240 | args["g_c2_address"] = g_c2_address |
| 1241 | args["expected_g_c1"] = "0" |
| 1242 | args["expected_g_c2"] = "1" |
| 1243 | |
| 1244 | self.assertTrue(self.g_c1_c2_contents_are(args)) |
| 1245 | |
| 1246 | # Verify we take only a small number of steps to hit the first state. Might need to work through function entry prologue code. |
| 1247 | args["expected_g_c1"] = "1" |
| 1248 | args["expected_g_c2"] = "1" |
| 1249 | (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=25, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) |
| 1250 | self.assertTrue(state_reached) |
| 1251 | |
| 1252 | # Verify we hit the next state. |
| 1253 | args["expected_g_c1"] = "1" |
| 1254 | args["expected_g_c2"] = "0" |
| 1255 | (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=5, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) |
| 1256 | self.assertTrue(state_reached) |
| 1257 | self.assertEquals(step_count, 1) |
| 1258 | |
| 1259 | # Verify we hit the next state. |
| 1260 | args["expected_g_c1"] = "0" |
| 1261 | args["expected_g_c2"] = "0" |
| 1262 | (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=5, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) |
| 1263 | self.assertTrue(state_reached) |
| 1264 | self.assertEquals(step_count, 1) |
| 1265 | |
| 1266 | # Verify we hit the next state. |
| 1267 | args["expected_g_c1"] = "0" |
| 1268 | args["expected_g_c2"] = "1" |
| 1269 | (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=5, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) |
| 1270 | self.assertTrue(state_reached) |
| 1271 | self.assertEquals(step_count, 1) |
Todd Fiala | af245d1 | 2014-06-30 21:05:18 +0000 | [diff] [blame] | 1272 | |