From 1cd591de68bfae4abdcdc4e7c47465fd9bd54ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis-Francis=20Ratt=C3=A9-Boulianne?= <lfrb@collabora.com> Date: Thu, 15 Dec 2016 02:55:35 -0500 Subject: [PATCH] Add support start a remote application under a debugger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start the gdbserver on device and start gdb client locally in interactive mode. Default port is not configurable as of now (1234) and other targets (e.g. simulator, SDK) are not supported. Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com> Reviewed-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk> Differential Revision: https://phabricator.apertis.org/D5299 --- doc/man/ade-debug.1 | 45 ++++++++++++++++++ doc/man/ade.1 | 8 ++++ tools/ade | 112 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 doc/man/ade-debug.1 diff --git a/doc/man/ade-debug.1 b/doc/man/ade-debug.1 new file mode 100644 index 0000000..0a425b2 --- /dev/null +++ b/doc/man/ade-debug.1 @@ -0,0 +1,45 @@ +.TH ADE\-DEBUG 1 14/12/2016 0.1612.0 Apertis\ Development\ Tools\ Manual + +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l + +.SH NAME + +ade-debug \- Start an application in debug mode + +.SH SYNOPSIS +.sp +.nf +\fIade debug\fR --app=<app> --device [<user>[:<pass>]@]<hostname>[:<port>] \fBARGS\fR +.fi +.sp +.SH DESCRIPTION +.sp +Start an application on given device in debug mode and connect GDB to it\&. +The tool should be run with the target project directory as the current one\&. +Ideally, the project should have been configured with the \-\-debug option so +the debug symbols are available\&. +.sp +.SH OPTIONS +.sp +.PP +\fI\-\-app\fR +.RS 4 +Absolute path to application to start\&. +.sp +.RE +.PP +\fI\-\-device\fR +.RS 4 +Use device as target (e.g. user@apertis)\&. +.sp +.RE +.SH SEE ALSO + +ade(1) + +.SH COPYRIGHT + +Copyright (c) 2016 Collabora Ltd. diff --git a/doc/man/ade.1 b/doc/man/ade.1 index 95450c3..ddf2975 100644 --- a/doc/man/ade.1 +++ b/doc/man/ade.1 @@ -101,6 +101,14 @@ Start application in simulator or on device\&. See \fBade-run(1)\fR .sp .RE +.PP +\fIdebug\fR +.RS 4 +Start application in debug mode\&. +.sp +See \fBade-debug(1)\fR +.sp +.RE .SH SEE ALSO ade-sysroot(1) diff --git a/tools/ade b/tools/ade index 1fd2491..8afb996 100755 --- a/tools/ade +++ b/tools/ade @@ -27,6 +27,8 @@ import subprocess import sys import tarfile import tempfile +import time +import threading import urllib import xdg.BaseDirectory @@ -37,6 +39,7 @@ from urllib.parse import urlparse from urllib.request import urlopen, urlretrieve HARD_FLOAT_FLAG = 0x00000400 +DEFAULT_GDBSERVER_PORT = 1234 def print_progress(count, blockSize, total): @@ -171,6 +174,72 @@ class TargetTriplet: raise NotSupportedError +class DebuggerServerThread(threading.Thread): + + def __init__(self, target, port, app, *args): + super().__init__() + self.target = target + self.port = port + self.app = app + self.args = args + + def run(self): + self._data = self.target.start_gdbserver(self.port, self.app, *self.args) + + def stop(self): + self.target.stop_gdbserver(self._data) + + +class DebuggerServer: + + def __init__(self, target, app, *args): + self.target = target + self.app = app + self.args = args + self.port = DEFAULT_GDBSERVER_PORT + self._thread = None + + def __enter__(self): + self._thread = DebuggerServerThread(self.target, self.port, self.app, *self.args) + self._thread.start() + return self + + def __exit__(self, et, ev, tb): + self._thread.stop() + self._thread.join() + + def get_info(self): + return "tcp:{}:{}".format(self.target.host, self.port) + + +class Debugger: + + def __init__(self, target, project): + self.target = target + self.project = project + + def _get_commands(self, server, debugdir, libdir): + cmds = [] + if isinstance(self.target, Sysroot): + cmds.append("set sysroot {}".format(self.target.path)) + cmds.append("set solib-search-path {}".format(libdir)) + cmds.append("file {}{}".format(debugdir, server.app)) + cmds.append("target remote {}".format(server.get_info())) + return cmds + + def connect(self, server): + debugdir = os.path.join(self.project.root, 'debug') + libdir = os.path.join(self.project.root, 'debug', 'Applications', self.project.bundle_id, 'lib') + self.project.install(debugdir) + + cmds = self._get_commands(server, debugdir, libdir) + args = ['gdb'] + for cmd in cmds: + args.append('-ex') + args.append(cmd) + os.execv('/usr/bin/gdb', args) + + class Simulator: def __init__(self): @@ -249,6 +318,11 @@ class Device: return out + def _run(self, *args): + ssh = self._connect() + stdin, stdout, stderr = ssh.exec_command(' '.join(args)) + return ssh, stdout.channel + def load_sysroot_version(self): try: v = self._exec('cat', '/etc/image_version') @@ -280,6 +354,29 @@ class Device: def run(self, app, *args): self._exec('canterbury-exec', app, *args) + def _wait_gdbserver(self, channel): + data = b'' + while True: + if channel.exit_status_ready(): + status = channel.recv_exit_status() + if status != 0: + raise CommandFailedError('', data.decode().strip(), status) + while channel.recv_stderr_ready(): + data += channel.recv_stderr(1024) + if 'Listening on port' in data.decode(): + return + time.sleep(1) + + def start_gdbserver(self, port, app, *args): + host = self.host + conn = "{}:{}".format(host, port) + ssh, chan = self._run('gdbserver', '--wrapper', 'canterbury-exec', '--', conn, app, *args) + self._wait_gdbserver(chan) + return (ssh, chan) + + def stop_gdbserver(self, data): + pass + class SysrootVersion: @@ -1263,6 +1360,14 @@ class Ade: target = self.get_target() target.run(self.app, *self.args) + def do_debug(self): + target = self.get_target() + project = Project() + + with DebuggerServer(target, self.app, *self.args) as server: + gdb = Debugger(self.unpack_sysroot(target), project) + gdb.connect(server) + def info(self, message): if self.format == 'friendly': print(message) @@ -1379,6 +1484,13 @@ if __name__ == '__main__': group.add_argument('--device', help="Use device as target (e.g. user@apertis)") run_parser.add_argument('args', help="Arguments to pass to application", nargs=argparse.REMAINDER) + # Debug parser + debug_parser = subparsers.add_parser('debug', help="Debug application") + debug_parser.add_argument('--app', help="Remote path to application to debug") + group = debug_parser.add_mutually_exclusive_group() + group.add_argument('--device', help="Use device as target (e.g. user@apertis)") + debug_parser.add_argument('args', help="Arguments to pass to application", nargs=argparse.REMAINDER) + argcomplete.autocomplete(root_parser) obj = Ade() -- GitLab