From cfff4e343b6678a9a8ada6304b3856258bc1190a Mon Sep 17 00:00:00 2001 From: Nikita Putikhin Date: Mon, 22 Apr 2024 13:04:45 +0200 Subject: Add --cwd parameter to set debuggee working directory in lldbclient Setting the working direcotry is useful for binaries that expect to be run from a specific folder. In particular, this allows to debug target integration tests with data files, since they expect these files to be available by relative paths. Test: lldbclient.py /data/ -r pwd # prints / lldbclient.py --cwd /data/ -r pwd # prints /data lldbclient.py --cwd /data/ --chroot / -r pwd # exits with error lldbclient.py --cwd /invalid_path -r pwd # exits with error Change-Id: I9d1002c28219028d0120df3b9620e09030d1361e --- python-packages/gdbrunner/gdbrunner/__init__.py | 13 ++++++++++++- scripts/gdbclient.py | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/python-packages/gdbrunner/gdbrunner/__init__.py b/python-packages/gdbrunner/gdbrunner/__init__.py index ebbe9e0b5..72e59800f 100644 --- a/python-packages/gdbrunner/gdbrunner/__init__.py +++ b/python-packages/gdbrunner/gdbrunner/__init__.py @@ -144,7 +144,7 @@ def get_pids(device, process_name): def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, target_pid, run_cmd, debug_socket, port, run_as_cmd=[], - lldb=False, chroot=""): + lldb=False, chroot="", cwd=""): """Start gdbserver in the background and forward necessary ports. Args: @@ -156,6 +156,8 @@ def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, debug_socket: Device path to place gdbserver unix domain socket. port: Host port to forward the debug_socket to. run_as_cmd: run-as or su command to prepend to commands. + chroot: The directory to pass to chroot when running the command. Cannot be set with cwd. + cwd: The working directory for the command. Cannot be set with chroot. Returns: Popen handle to the `adb shell` process gdbserver was started with. @@ -164,6 +166,12 @@ def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, assert target_pid is None or run_cmd is None if chroot: + # Chroot changes the working directory, so cd won't work if ran before chroot. + # On the other hand naively passing cd to chroot also won't work: chroot + # executes a binary, while cd is a bash command. + # They are unlikely to be used together anyway, so we simply disallow it. + if cwd: + raise ValueError('chroot and cwd cannot be set together') run_as_cmd = ["chroot", chroot] + run_as_cmd # Remove the old socket file. @@ -209,6 +217,9 @@ def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, "gdbclient.log") print("Redirecting gdbserver output to {}".format(gdbserver_output_path)) gdbserver_output = open(gdbserver_output_path, 'w') + + if cwd: + gdbserver_cmd = ["cd", cwd, "&&"] + gdbserver_cmd return device.shell_popen(gdbserver_cmd, stdout=gdbserver_output, stderr=gdbserver_output) diff --git a/scripts/gdbclient.py b/scripts/gdbclient.py index f270c19ed..81d9992f5 100755 --- a/scripts/gdbclient.py +++ b/scripts/gdbclient.py @@ -123,7 +123,10 @@ def parse_args() -> argparse.Namespace: help="set environment variable when running a binary") parser.add_argument( "--chroot", nargs='?', default="", metavar="PATH", - help="run command in a chroot in the given directory") + help="run command in a chroot in the given directory. Cannot be used with --cwd.") + parser.add_argument( + "--cwd", nargs='?', default="", metavar="PATH", + help="working directory for the command. Cannot be used with --chroot.") return parser.parse_args() @@ -136,6 +139,11 @@ def verify_device(device: adb.AndroidDevice) -> None: sys.exit(msg.format(target_device, ", ".join(n if n else "None" for n in names))) +def get_device_dir_exists(device: adb.AndroidDevice, dir: str) -> bool: + exit_code, _, _ = device.shell_nocheck(['[', '-d', dir, ']']) + return exit_code == 0 + + def get_remote_pid(device: adb.AndroidDevice, process_name: str) -> int: processes = gdbrunner.get_processes(device) if process_name not in processes: @@ -458,6 +466,13 @@ def do_main() -> None: root = os.environ["ANDROID_BUILD_TOP"] sysroot = os.path.join(os.environ["ANDROID_PRODUCT_OUT"], "symbols") + if args.cwd: + if not get_device_dir_exists(device, args.cwd): + raise ValueError('Working directory does not exist on device: {}'.format(args.cwd)) + if args.chroot: + # See the comment in start_gdbserver about why this is not implemented. + raise ValueError('--cwd and --chroot cannot be used together') + # Make sure the environment matches the attached device. # Skip when running in a chroot because the chroot lunch target may not # match the device's lunch target. @@ -517,7 +532,7 @@ def do_main() -> None: gdbrunner.start_gdbserver( device, server_local_path, server_remote_path, target_pid=pid, run_cmd=run_cmd, debug_socket=debug_socket, - port=args.port, run_as_cmd=cmd_prefix, lldb=True, chroot=args.chroot) + port=args.port, run_as_cmd=cmd_prefix, lldb=True, chroot=args.chroot, cwd=args.cwd) else: print( "Connecting to tracing pid {} using local port {}".format( -- cgit v1.2.3