diff --git a/doc/man/ade-debug.1 b/doc/man/ade-debug.1 new file mode 100644 index 0000000000000000000000000000000000000000..0a425b2d8c1d6d445d970e213bfbd68a014532d1 --- /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 95450c3ec518677dfa89d412881edb2a6cb2d217..ddf297552dbb8902dc242be591e18c1911ad560e 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 1fd24917821a653fcb0ab3ec49289332cf1e9250..8afb9963a89cc4c720291cc3848546bf13385e4d 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()