xref: /aosp_15_r20/development/scripts/gdbclient.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*90c8c64dSAndroid Build Coastguard Worker#
3*90c8c64dSAndroid Build Coastguard Worker# Copyright (C) 2015 The Android Open Source Project
4*90c8c64dSAndroid Build Coastguard Worker#
5*90c8c64dSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*90c8c64dSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*90c8c64dSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*90c8c64dSAndroid Build Coastguard Worker#
9*90c8c64dSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*90c8c64dSAndroid Build Coastguard Worker#
11*90c8c64dSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*90c8c64dSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*90c8c64dSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*90c8c64dSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*90c8c64dSAndroid Build Coastguard Worker# limitations under the License.
16*90c8c64dSAndroid Build Coastguard Worker#
17*90c8c64dSAndroid Build Coastguard Worker
18*90c8c64dSAndroid Build Coastguard Workerimport argparse
19*90c8c64dSAndroid Build Coastguard Workerimport json
20*90c8c64dSAndroid Build Coastguard Workerimport logging
21*90c8c64dSAndroid Build Coastguard Workerimport os
22*90c8c64dSAndroid Build Coastguard Workerimport pathlib
23*90c8c64dSAndroid Build Coastguard Workerimport posixpath
24*90c8c64dSAndroid Build Coastguard Workerimport re
25*90c8c64dSAndroid Build Coastguard Workerimport shutil
26*90c8c64dSAndroid Build Coastguard Workerimport subprocess
27*90c8c64dSAndroid Build Coastguard Workerimport sys
28*90c8c64dSAndroid Build Coastguard Workerimport tempfile
29*90c8c64dSAndroid Build Coastguard Workerimport textwrap
30*90c8c64dSAndroid Build Coastguard Workerfrom typing import Any, BinaryIO
31*90c8c64dSAndroid Build Coastguard Worker
32*90c8c64dSAndroid Build Coastguard Workerimport adb
33*90c8c64dSAndroid Build Coastguard Worker# Shared functions across gdbclient.py and ndk-gdb.py.
34*90c8c64dSAndroid Build Coastguard Workerimport gdbrunner
35*90c8c64dSAndroid Build Coastguard Worker
36*90c8c64dSAndroid Build Coastguard Workerg_temp_dirs = []
37*90c8c64dSAndroid Build Coastguard Worker
38*90c8c64dSAndroid Build Coastguard Workerg_vscode_config_marker_begin = '// #lldbclient-generated-begin'
39*90c8c64dSAndroid Build Coastguard Workerg_vscode_config_marker_end = '// #lldbclient-generated-end'
40*90c8c64dSAndroid Build Coastguard Worker
41*90c8c64dSAndroid Build Coastguard Worker
42*90c8c64dSAndroid Build Coastguard Workerdef read_toolchain_config(root: str) -> str:
43*90c8c64dSAndroid Build Coastguard Worker    """Finds out current toolchain version."""
44*90c8c64dSAndroid Build Coastguard Worker    version_output = subprocess.check_output(
45*90c8c64dSAndroid Build Coastguard Worker        f'{root}/build/soong/scripts/get_clang_version.py',
46*90c8c64dSAndroid Build Coastguard Worker        text=True)
47*90c8c64dSAndroid Build Coastguard Worker    return version_output.strip()
48*90c8c64dSAndroid Build Coastguard Worker
49*90c8c64dSAndroid Build Coastguard Worker
50*90c8c64dSAndroid Build Coastguard Workerdef get_lldb_path(toolchain_path: str) -> str | None:
51*90c8c64dSAndroid Build Coastguard Worker    for lldb_name in ['lldb.sh', 'lldb.cmd', 'lldb', 'lldb.exe']:
52*90c8c64dSAndroid Build Coastguard Worker        debugger_path = os.path.join(toolchain_path, "bin", lldb_name)
53*90c8c64dSAndroid Build Coastguard Worker        if os.path.isfile(debugger_path):
54*90c8c64dSAndroid Build Coastguard Worker            return debugger_path
55*90c8c64dSAndroid Build Coastguard Worker    return None
56*90c8c64dSAndroid Build Coastguard Worker
57*90c8c64dSAndroid Build Coastguard Worker
58*90c8c64dSAndroid Build Coastguard Workerdef get_lldb_server_path(root: str, clang_base: str, clang_version: str, arch: str) -> str:
59*90c8c64dSAndroid Build Coastguard Worker    arch = {
60*90c8c64dSAndroid Build Coastguard Worker        'arm': 'arm',
61*90c8c64dSAndroid Build Coastguard Worker        'arm64': 'aarch64',
62*90c8c64dSAndroid Build Coastguard Worker        'riscv64': 'riscv64',
63*90c8c64dSAndroid Build Coastguard Worker        'x86': 'i386',
64*90c8c64dSAndroid Build Coastguard Worker        'x86_64': 'x86_64',
65*90c8c64dSAndroid Build Coastguard Worker    }[arch]
66*90c8c64dSAndroid Build Coastguard Worker    return os.path.join(root, clang_base, "linux-x86",
67*90c8c64dSAndroid Build Coastguard Worker                        clang_version, "runtimes_ndk_cxx", arch, "lldb-server")
68*90c8c64dSAndroid Build Coastguard Worker
69*90c8c64dSAndroid Build Coastguard Worker
70*90c8c64dSAndroid Build Coastguard Workerdef get_tracer_pid(device: adb.AndroidDevice, pid: int | str | None) -> int:
71*90c8c64dSAndroid Build Coastguard Worker    if pid is None:
72*90c8c64dSAndroid Build Coastguard Worker        return 0
73*90c8c64dSAndroid Build Coastguard Worker
74*90c8c64dSAndroid Build Coastguard Worker    line, _ = device.shell(["grep", "-e", "^TracerPid:", "/proc/{}/status".format(pid)])
75*90c8c64dSAndroid Build Coastguard Worker    tracer_pid = re.sub('TracerPid:\t(.*)\n', r'\1', line)
76*90c8c64dSAndroid Build Coastguard Worker    return int(tracer_pid)
77*90c8c64dSAndroid Build Coastguard Worker
78*90c8c64dSAndroid Build Coastguard Worker
79*90c8c64dSAndroid Build Coastguard Workerdef parse_args() -> argparse.Namespace:
80*90c8c64dSAndroid Build Coastguard Worker    parser = gdbrunner.ArgumentParser()
81*90c8c64dSAndroid Build Coastguard Worker
82*90c8c64dSAndroid Build Coastguard Worker    group = parser.add_argument_group(title="attach target")
83*90c8c64dSAndroid Build Coastguard Worker    group = group.add_mutually_exclusive_group(required=True)
84*90c8c64dSAndroid Build Coastguard Worker    group.add_argument(
85*90c8c64dSAndroid Build Coastguard Worker        "-p", dest="target_pid", metavar="PID", type=int,
86*90c8c64dSAndroid Build Coastguard Worker        help="attach to a process with specified PID")
87*90c8c64dSAndroid Build Coastguard Worker    group.add_argument(
88*90c8c64dSAndroid Build Coastguard Worker        "-n", dest="target_name", metavar="NAME",
89*90c8c64dSAndroid Build Coastguard Worker        help="attach to a process with specified name")
90*90c8c64dSAndroid Build Coastguard Worker    group.add_argument(
91*90c8c64dSAndroid Build Coastguard Worker        "-r", dest="run_cmd", metavar="CMD", nargs=argparse.REMAINDER,
92*90c8c64dSAndroid Build Coastguard Worker        help="run a binary on the device, with args")
93*90c8c64dSAndroid Build Coastguard Worker
94*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
95*90c8c64dSAndroid Build Coastguard Worker        "--port", nargs="?", default="5039",
96*90c8c64dSAndroid Build Coastguard Worker        help="Unused **host** port to forward the debug_socket to.[default: 5039]")
97*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
98*90c8c64dSAndroid Build Coastguard Worker        "--user", nargs="?", default="root",
99*90c8c64dSAndroid Build Coastguard Worker        help="user to run commands as on the device [default: root]")
100*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
101*90c8c64dSAndroid Build Coastguard Worker        "--setup-forwarding", default=None,
102*90c8c64dSAndroid Build Coastguard Worker        choices=["lldb", "vscode-lldb"],
103*90c8c64dSAndroid Build Coastguard Worker        help=("Set up lldb-server and port forwarding. Prints commands or " +
104*90c8c64dSAndroid Build Coastguard Worker              ".vscode/launch.json configuration needed to connect the debugging " +
105*90c8c64dSAndroid Build Coastguard Worker              "client to the server. 'vscode' with lldb and 'vscode-lldb' both " +
106*90c8c64dSAndroid Build Coastguard Worker              "require the 'vadimcn.vscode-lldb' extension."))
107*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
108*90c8c64dSAndroid Build Coastguard Worker        "--vscode-launch-props", default=None,
109*90c8c64dSAndroid Build Coastguard Worker        dest="vscode_launch_props",
110*90c8c64dSAndroid Build Coastguard Worker        help=("JSON with extra properties to add to launch parameters when using " +
111*90c8c64dSAndroid Build Coastguard Worker              "vscode-lldb forwarding."))
112*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
113*90c8c64dSAndroid Build Coastguard Worker        "--vscode-launch-file", default=None,
114*90c8c64dSAndroid Build Coastguard Worker        dest="vscode_launch_file",
115*90c8c64dSAndroid Build Coastguard Worker        help=textwrap.dedent(f"""Path to .vscode/launch.json file for the generated launch
116*90c8c64dSAndroid Build Coastguard Worker                     config when using vscode-lldb forwarding. The file needs to
117*90c8c64dSAndroid Build Coastguard Worker                     contain two marker lines: '{g_vscode_config_marker_begin}'
118*90c8c64dSAndroid Build Coastguard Worker                     and '{g_vscode_config_marker_end}'. The config will be written inline
119*90c8c64dSAndroid Build Coastguard Worker                     between these lines, replacing any text that is already there."""))
120*90c8c64dSAndroid Build Coastguard Worker
121*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
122*90c8c64dSAndroid Build Coastguard Worker        "--env", nargs=1, action="append", metavar="VAR=VALUE",
123*90c8c64dSAndroid Build Coastguard Worker        help="set environment variable when running a binary")
124*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
125*90c8c64dSAndroid Build Coastguard Worker        "--chroot", nargs='?', default="", metavar="PATH",
126*90c8c64dSAndroid Build Coastguard Worker        help="run command in a chroot in the given directory. Cannot be used with --cwd.")
127*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
128*90c8c64dSAndroid Build Coastguard Worker        "--cwd", nargs='?', default="", metavar="PATH",
129*90c8c64dSAndroid Build Coastguard Worker        help="working directory for the command. Cannot be used with --chroot.")
130*90c8c64dSAndroid Build Coastguard Worker
131*90c8c64dSAndroid Build Coastguard Worker    return parser.parse_args()
132*90c8c64dSAndroid Build Coastguard Worker
133*90c8c64dSAndroid Build Coastguard Worker
134*90c8c64dSAndroid Build Coastguard Workerdef verify_device(device: adb.AndroidDevice) -> None:
135*90c8c64dSAndroid Build Coastguard Worker    names = set([device.get_prop("ro.build.product"), device.get_prop("ro.product.name")])
136*90c8c64dSAndroid Build Coastguard Worker    target_device = os.environ["TARGET_PRODUCT"]
137*90c8c64dSAndroid Build Coastguard Worker    if target_device not in names:
138*90c8c64dSAndroid Build Coastguard Worker        msg = "You used the wrong lunch: TARGET_PRODUCT ({}) does not match attached device ({})"
139*90c8c64dSAndroid Build Coastguard Worker        sys.exit(msg.format(target_device, ", ".join(n if n else "None" for n in names)))
140*90c8c64dSAndroid Build Coastguard Worker
141*90c8c64dSAndroid Build Coastguard Worker
142*90c8c64dSAndroid Build Coastguard Workerdef get_device_dir_exists(device: adb.AndroidDevice, dir: str) -> bool:
143*90c8c64dSAndroid Build Coastguard Worker    exit_code, _, _ = device.shell_nocheck(['[', '-d', dir, ']'])
144*90c8c64dSAndroid Build Coastguard Worker    return exit_code == 0
145*90c8c64dSAndroid Build Coastguard Worker
146*90c8c64dSAndroid Build Coastguard Worker
147*90c8c64dSAndroid Build Coastguard Workerdef get_remote_pid(device: adb.AndroidDevice, process_name: str) -> int:
148*90c8c64dSAndroid Build Coastguard Worker    processes = gdbrunner.get_processes(device)
149*90c8c64dSAndroid Build Coastguard Worker    if process_name not in processes:
150*90c8c64dSAndroid Build Coastguard Worker        msg = "failed to find running process {}".format(process_name)
151*90c8c64dSAndroid Build Coastguard Worker        sys.exit(msg)
152*90c8c64dSAndroid Build Coastguard Worker    pids = processes[process_name]
153*90c8c64dSAndroid Build Coastguard Worker    if len(pids) > 1:
154*90c8c64dSAndroid Build Coastguard Worker        msg = "multiple processes match '{}': {}".format(process_name, pids)
155*90c8c64dSAndroid Build Coastguard Worker        sys.exit(msg)
156*90c8c64dSAndroid Build Coastguard Worker
157*90c8c64dSAndroid Build Coastguard Worker    # Fetch the binary using the PID later.
158*90c8c64dSAndroid Build Coastguard Worker    return pids[0]
159*90c8c64dSAndroid Build Coastguard Worker
160*90c8c64dSAndroid Build Coastguard Worker
161*90c8c64dSAndroid Build Coastguard Workerdef make_temp_dir(prefix: str) -> str:
162*90c8c64dSAndroid Build Coastguard Worker    global g_temp_dirs
163*90c8c64dSAndroid Build Coastguard Worker    result = tempfile.mkdtemp(prefix='lldbclient-linker-')
164*90c8c64dSAndroid Build Coastguard Worker    g_temp_dirs.append(result)
165*90c8c64dSAndroid Build Coastguard Worker    return result
166*90c8c64dSAndroid Build Coastguard Worker
167*90c8c64dSAndroid Build Coastguard Worker
168*90c8c64dSAndroid Build Coastguard Workerdef ensure_linker(device: adb.AndroidDevice, sysroot: str, interp: str | None) -> str | None:
169*90c8c64dSAndroid Build Coastguard Worker    """Ensure that the device's linker exists on the host.
170*90c8c64dSAndroid Build Coastguard Worker
171*90c8c64dSAndroid Build Coastguard Worker    PT_INTERP is usually /system/bin/linker[64], but on the device, that file is
172*90c8c64dSAndroid Build Coastguard Worker    a symlink to /apex/com.android.runtime/bin/linker[64]. The symbolized linker
173*90c8c64dSAndroid Build Coastguard Worker    binary on the host is located in ${sysroot}/apex, not in ${sysroot}/system,
174*90c8c64dSAndroid Build Coastguard Worker    so add the ${sysroot}/apex path to the solib search path.
175*90c8c64dSAndroid Build Coastguard Worker
176*90c8c64dSAndroid Build Coastguard Worker    PT_INTERP will be /system/bin/bootstrap/linker[64] for executables using the
177*90c8c64dSAndroid Build Coastguard Worker    non-APEX/bootstrap linker. No search path modification is needed.
178*90c8c64dSAndroid Build Coastguard Worker
179*90c8c64dSAndroid Build Coastguard Worker    For a tapas build, only an unbundled app is built, and there is no linker in
180*90c8c64dSAndroid Build Coastguard Worker    ${sysroot} at all, so copy the linker from the device.
181*90c8c64dSAndroid Build Coastguard Worker
182*90c8c64dSAndroid Build Coastguard Worker    Returns:
183*90c8c64dSAndroid Build Coastguard Worker        A directory to add to the soinfo search path or None if no directory
184*90c8c64dSAndroid Build Coastguard Worker        needs to be added.
185*90c8c64dSAndroid Build Coastguard Worker    """
186*90c8c64dSAndroid Build Coastguard Worker
187*90c8c64dSAndroid Build Coastguard Worker    # Static executables have no interpreter.
188*90c8c64dSAndroid Build Coastguard Worker    if interp is None:
189*90c8c64dSAndroid Build Coastguard Worker        return None
190*90c8c64dSAndroid Build Coastguard Worker
191*90c8c64dSAndroid Build Coastguard Worker    # lldb will search for the linker using the PT_INTERP path. First try to find
192*90c8c64dSAndroid Build Coastguard Worker    # it in the sysroot.
193*90c8c64dSAndroid Build Coastguard Worker    local_path = os.path.join(sysroot, interp.lstrip("/"))
194*90c8c64dSAndroid Build Coastguard Worker    if os.path.exists(local_path):
195*90c8c64dSAndroid Build Coastguard Worker        return None
196*90c8c64dSAndroid Build Coastguard Worker
197*90c8c64dSAndroid Build Coastguard Worker    # If the linker on the device is a symlink, search for the symlink's target
198*90c8c64dSAndroid Build Coastguard Worker    # in the sysroot directory.
199*90c8c64dSAndroid Build Coastguard Worker    interp_real, _ = device.shell(["realpath", interp])
200*90c8c64dSAndroid Build Coastguard Worker    interp_real = interp_real.strip()
201*90c8c64dSAndroid Build Coastguard Worker    local_path = os.path.join(sysroot, interp_real.lstrip("/"))
202*90c8c64dSAndroid Build Coastguard Worker    if os.path.exists(local_path):
203*90c8c64dSAndroid Build Coastguard Worker        if posixpath.basename(interp) == posixpath.basename(interp_real):
204*90c8c64dSAndroid Build Coastguard Worker            # Add the interpreter's directory to the search path.
205*90c8c64dSAndroid Build Coastguard Worker            return os.path.dirname(local_path)
206*90c8c64dSAndroid Build Coastguard Worker        else:
207*90c8c64dSAndroid Build Coastguard Worker            # If PT_INTERP is linker_asan[64], but the sysroot file is
208*90c8c64dSAndroid Build Coastguard Worker            # linker[64], then copy the local file to the name lldb expects.
209*90c8c64dSAndroid Build Coastguard Worker            result = make_temp_dir('lldbclient-linker-')
210*90c8c64dSAndroid Build Coastguard Worker            shutil.copy(local_path, os.path.join(result, posixpath.basename(interp)))
211*90c8c64dSAndroid Build Coastguard Worker            return result
212*90c8c64dSAndroid Build Coastguard Worker
213*90c8c64dSAndroid Build Coastguard Worker    # Pull the system linker.
214*90c8c64dSAndroid Build Coastguard Worker    result = make_temp_dir('lldbclient-linker-')
215*90c8c64dSAndroid Build Coastguard Worker    device.pull(interp, os.path.join(result, posixpath.basename(interp)))
216*90c8c64dSAndroid Build Coastguard Worker    return result
217*90c8c64dSAndroid Build Coastguard Worker
218*90c8c64dSAndroid Build Coastguard Worker
219*90c8c64dSAndroid Build Coastguard Workerdef handle_switches(args, sysroot: str) -> tuple[BinaryIO, int | None, str | None]:
220*90c8c64dSAndroid Build Coastguard Worker    """Fetch the targeted binary and determine how to attach lldb.
221*90c8c64dSAndroid Build Coastguard Worker
222*90c8c64dSAndroid Build Coastguard Worker    Args:
223*90c8c64dSAndroid Build Coastguard Worker        args: Parsed arguments.
224*90c8c64dSAndroid Build Coastguard Worker        sysroot: Local sysroot path.
225*90c8c64dSAndroid Build Coastguard Worker
226*90c8c64dSAndroid Build Coastguard Worker    Returns:
227*90c8c64dSAndroid Build Coastguard Worker        (binary_file, attach_pid, run_cmd).
228*90c8c64dSAndroid Build Coastguard Worker        Precisely one of attach_pid or run_cmd will be None.
229*90c8c64dSAndroid Build Coastguard Worker    """
230*90c8c64dSAndroid Build Coastguard Worker
231*90c8c64dSAndroid Build Coastguard Worker    device = args.device
232*90c8c64dSAndroid Build Coastguard Worker    binary_file = None
233*90c8c64dSAndroid Build Coastguard Worker    pid = None
234*90c8c64dSAndroid Build Coastguard Worker    run_cmd = None
235*90c8c64dSAndroid Build Coastguard Worker
236*90c8c64dSAndroid Build Coastguard Worker    args.su_cmd = ["su", args.user] if args.user else []
237*90c8c64dSAndroid Build Coastguard Worker
238*90c8c64dSAndroid Build Coastguard Worker    if args.target_pid:
239*90c8c64dSAndroid Build Coastguard Worker        # Fetch the binary using the PID later.
240*90c8c64dSAndroid Build Coastguard Worker        pid = args.target_pid
241*90c8c64dSAndroid Build Coastguard Worker    elif args.target_name:
242*90c8c64dSAndroid Build Coastguard Worker        # Fetch the binary using the PID later.
243*90c8c64dSAndroid Build Coastguard Worker        pid = get_remote_pid(device, args.target_name)
244*90c8c64dSAndroid Build Coastguard Worker    elif args.run_cmd:
245*90c8c64dSAndroid Build Coastguard Worker        if not args.run_cmd[0]:
246*90c8c64dSAndroid Build Coastguard Worker            sys.exit("empty command passed to -r")
247*90c8c64dSAndroid Build Coastguard Worker        run_cmd = args.run_cmd
248*90c8c64dSAndroid Build Coastguard Worker        if not run_cmd[0].startswith("/"):
249*90c8c64dSAndroid Build Coastguard Worker            try:
250*90c8c64dSAndroid Build Coastguard Worker                run_cmd[0] = gdbrunner.find_executable_path(device, args.run_cmd[0],
251*90c8c64dSAndroid Build Coastguard Worker                                                            run_as_cmd=args.su_cmd)
252*90c8c64dSAndroid Build Coastguard Worker            except RuntimeError:
253*90c8c64dSAndroid Build Coastguard Worker              sys.exit("Could not find executable '{}' passed to -r, "
254*90c8c64dSAndroid Build Coastguard Worker                       "please provide an absolute path.".format(args.run_cmd[0]))
255*90c8c64dSAndroid Build Coastguard Worker
256*90c8c64dSAndroid Build Coastguard Worker        binary_file, local = gdbrunner.find_file(device, run_cmd[0], sysroot,
257*90c8c64dSAndroid Build Coastguard Worker                                                 run_as_cmd=args.su_cmd)
258*90c8c64dSAndroid Build Coastguard Worker    if binary_file is None:
259*90c8c64dSAndroid Build Coastguard Worker        assert pid is not None
260*90c8c64dSAndroid Build Coastguard Worker        try:
261*90c8c64dSAndroid Build Coastguard Worker            binary_file, local = gdbrunner.find_binary(device, pid, sysroot,
262*90c8c64dSAndroid Build Coastguard Worker                                                       run_as_cmd=args.su_cmd)
263*90c8c64dSAndroid Build Coastguard Worker        except adb.ShellError:
264*90c8c64dSAndroid Build Coastguard Worker            sys.exit("failed to pull binary for PID {}".format(pid))
265*90c8c64dSAndroid Build Coastguard Worker
266*90c8c64dSAndroid Build Coastguard Worker    if not local:
267*90c8c64dSAndroid Build Coastguard Worker        logging.warning("Couldn't find local unstripped executable in {},"
268*90c8c64dSAndroid Build Coastguard Worker                        " symbols may not be available.".format(sysroot))
269*90c8c64dSAndroid Build Coastguard Worker
270*90c8c64dSAndroid Build Coastguard Worker    return (binary_file, pid, run_cmd)
271*90c8c64dSAndroid Build Coastguard Worker
272*90c8c64dSAndroid Build Coastguard Workerdef merge_launch_dict(base: dict[str, Any], to_add:  dict[str, Any] | None) -> None:
273*90c8c64dSAndroid Build Coastguard Worker    """Merges two dicts describing VSCode launch.json properties: base and
274*90c8c64dSAndroid Build Coastguard Worker    to_add. Base is modified in-place with items from to_add.
275*90c8c64dSAndroid Build Coastguard Worker    Items from to_add that are not present in base are inserted. Items that are
276*90c8c64dSAndroid Build Coastguard Worker    present are merged following these rules:
277*90c8c64dSAndroid Build Coastguard Worker        - Lists are merged with to_add elements appended to the end of base
278*90c8c64dSAndroid Build Coastguard Worker          list. Only a list can be merged with a list.
279*90c8c64dSAndroid Build Coastguard Worker        - dicts are merged recursively. Only a dict can be merged with a dict.
280*90c8c64dSAndroid Build Coastguard Worker        - Other present values in base get overwritten with values from to_add.
281*90c8c64dSAndroid Build Coastguard Worker
282*90c8c64dSAndroid Build Coastguard Worker    The reason for these rules is that merging in new values should prefer to
283*90c8c64dSAndroid Build Coastguard Worker    expand the existing set instead of overwriting where possible.
284*90c8c64dSAndroid Build Coastguard Worker    """
285*90c8c64dSAndroid Build Coastguard Worker    if to_add is None:
286*90c8c64dSAndroid Build Coastguard Worker        return
287*90c8c64dSAndroid Build Coastguard Worker
288*90c8c64dSAndroid Build Coastguard Worker    for key, val in to_add.items():
289*90c8c64dSAndroid Build Coastguard Worker        if key not in base:
290*90c8c64dSAndroid Build Coastguard Worker            base[key] = val
291*90c8c64dSAndroid Build Coastguard Worker        else:
292*90c8c64dSAndroid Build Coastguard Worker            if isinstance(base[key], list) and not isinstance(val, list):
293*90c8c64dSAndroid Build Coastguard Worker                raise ValueError(f'Cannot merge non-list into list at key={key}. ' +
294*90c8c64dSAndroid Build Coastguard Worker                'You probably need to wrap your value into a list.')
295*90c8c64dSAndroid Build Coastguard Worker            if not isinstance(base[key], list) and isinstance(val, list):
296*90c8c64dSAndroid Build Coastguard Worker                raise ValueError(f'Cannot merge list into non-list at key={key}.')
297*90c8c64dSAndroid Build Coastguard Worker            if isinstance(base[key], dict) != isinstance(val, dict):
298*90c8c64dSAndroid Build Coastguard Worker                raise ValueError(f'Cannot merge dict and non-dict at key={key}')
299*90c8c64dSAndroid Build Coastguard Worker
300*90c8c64dSAndroid Build Coastguard Worker            # We don't allow the user to overwrite or interleave lists and don't allow
301*90c8c64dSAndroid Build Coastguard Worker            # to delete dict entries.
302*90c8c64dSAndroid Build Coastguard Worker            # It can be done but would make the implementation a bit more complicated
303*90c8c64dSAndroid Build Coastguard Worker            # and provides less value than adding elements.
304*90c8c64dSAndroid Build Coastguard Worker            # We expect that the config generated by gdbclient doesn't contain anything
305*90c8c64dSAndroid Build Coastguard Worker            # the user would want to remove.
306*90c8c64dSAndroid Build Coastguard Worker            if isinstance(base[key], list):
307*90c8c64dSAndroid Build Coastguard Worker                base[key] += val
308*90c8c64dSAndroid Build Coastguard Worker            elif isinstance(base[key], dict):
309*90c8c64dSAndroid Build Coastguard Worker                merge_launch_dict(base[key], val)
310*90c8c64dSAndroid Build Coastguard Worker            else:
311*90c8c64dSAndroid Build Coastguard Worker                base[key] = val
312*90c8c64dSAndroid Build Coastguard Worker
313*90c8c64dSAndroid Build Coastguard Worker
314*90c8c64dSAndroid Build Coastguard Workerdef generate_vscode_lldb_script(root: str, sysroot: str, binary_name: str, port: str | int, solib_search_path: list[str], extra_props: dict[str, Any] | None) -> str:
315*90c8c64dSAndroid Build Coastguard Worker    # TODO It would be nice if we didn't need to copy this or run the
316*90c8c64dSAndroid Build Coastguard Worker    #      lldbclient.py program manually. Doing this would probably require
317*90c8c64dSAndroid Build Coastguard Worker    #      writing a vscode extension or modifying an existing one.
318*90c8c64dSAndroid Build Coastguard Worker    # TODO: https://code.visualstudio.com/api/references/vscode-api#debug and
319*90c8c64dSAndroid Build Coastguard Worker    #       https://code.visualstudio.com/api/extension-guides/debugger-extension and
320*90c8c64dSAndroid Build Coastguard Worker    #       https://github.com/vadimcn/vscode-lldb/blob/6b775c439992b6615e92f4938ee4e211f1b060cf/extension/pickProcess.ts#L6
321*90c8c64dSAndroid Build Coastguard Worker    res = {
322*90c8c64dSAndroid Build Coastguard Worker        "name": "(lldbclient.py) Attach {} (port: {})".format(binary_name.split("/")[-1], port),
323*90c8c64dSAndroid Build Coastguard Worker        "type": "lldb",
324*90c8c64dSAndroid Build Coastguard Worker        "request": "custom",
325*90c8c64dSAndroid Build Coastguard Worker        "relativePathBase": root,
326*90c8c64dSAndroid Build Coastguard Worker        "sourceMap": { "/b/f/w" : root, '': root, '.': root },
327*90c8c64dSAndroid Build Coastguard Worker        "initCommands": ['settings append target.exec-search-paths {}'.format(' '.join(solib_search_path))],
328*90c8c64dSAndroid Build Coastguard Worker        "targetCreateCommands": ["target create {}".format(binary_name),
329*90c8c64dSAndroid Build Coastguard Worker                                 "target modules search-paths add / {}/".format(sysroot)],
330*90c8c64dSAndroid Build Coastguard Worker        "processCreateCommands": ["gdb-remote {}".format(str(port))]
331*90c8c64dSAndroid Build Coastguard Worker    }
332*90c8c64dSAndroid Build Coastguard Worker    merge_launch_dict(res, extra_props)
333*90c8c64dSAndroid Build Coastguard Worker    return json.dumps(res, indent=4)
334*90c8c64dSAndroid Build Coastguard Worker
335*90c8c64dSAndroid Build Coastguard Workerdef generate_lldb_script(root: str, sysroot: str, binary_name: str, port: str | int, solib_search_path: list[str]) -> str:
336*90c8c64dSAndroid Build Coastguard Worker    commands = []
337*90c8c64dSAndroid Build Coastguard Worker    commands.append(
338*90c8c64dSAndroid Build Coastguard Worker        'settings append target.exec-search-paths {}'.format(' '.join(solib_search_path)))
339*90c8c64dSAndroid Build Coastguard Worker
340*90c8c64dSAndroid Build Coastguard Worker    commands.append('target create {}'.format(binary_name))
341*90c8c64dSAndroid Build Coastguard Worker    # For RBE support.
342*90c8c64dSAndroid Build Coastguard Worker    commands.append("settings append target.source-map '/b/f/w' '{}'".format(root))
343*90c8c64dSAndroid Build Coastguard Worker    commands.append("settings append target.source-map '' '{}'".format(root))
344*90c8c64dSAndroid Build Coastguard Worker    commands.append('target modules search-paths add / {}/'.format(sysroot))
345*90c8c64dSAndroid Build Coastguard Worker    commands.append('# If the below `gdb-remote` fails, run the command manually, '
346*90c8c64dSAndroid Build Coastguard Worker                    + 'as it may have raced with lldbserver startup.')
347*90c8c64dSAndroid Build Coastguard Worker    commands.append('gdb-remote {}'.format(str(port)))
348*90c8c64dSAndroid Build Coastguard Worker    return '\n'.join(commands)
349*90c8c64dSAndroid Build Coastguard Worker
350*90c8c64dSAndroid Build Coastguard Worker
351*90c8c64dSAndroid Build Coastguard Workerdef generate_setup_script(sysroot: str, linker_search_dir: str | None, binary_name: str, is64bit: bool, port: str | int, debugger: str, vscode_launch_props: dict[str, Any] | None) -> str:
352*90c8c64dSAndroid Build Coastguard Worker    # Generate a setup script.
353*90c8c64dSAndroid Build Coastguard Worker    root = os.environ["ANDROID_BUILD_TOP"]
354*90c8c64dSAndroid Build Coastguard Worker    symbols_dir = os.path.join(sysroot, "system", "lib64" if is64bit else "lib")
355*90c8c64dSAndroid Build Coastguard Worker    vendor_dir = os.path.join(sysroot, "vendor", "lib64" if is64bit else "lib")
356*90c8c64dSAndroid Build Coastguard Worker
357*90c8c64dSAndroid Build Coastguard Worker    solib_search_path = []
358*90c8c64dSAndroid Build Coastguard Worker    symbols_paths = ["", "hw", "ssl/engines", "drm", "egl", "soundfx"]
359*90c8c64dSAndroid Build Coastguard Worker    vendor_paths = ["", "hw", "egl"]
360*90c8c64dSAndroid Build Coastguard Worker    solib_search_path += [os.path.join(symbols_dir, x) for x in symbols_paths]
361*90c8c64dSAndroid Build Coastguard Worker    solib_search_path += [os.path.join(vendor_dir, x) for x in vendor_paths]
362*90c8c64dSAndroid Build Coastguard Worker    if linker_search_dir is not None:
363*90c8c64dSAndroid Build Coastguard Worker        solib_search_path += [linker_search_dir]
364*90c8c64dSAndroid Build Coastguard Worker
365*90c8c64dSAndroid Build Coastguard Worker    if debugger == "vscode-lldb":
366*90c8c64dSAndroid Build Coastguard Worker        return generate_vscode_lldb_script(
367*90c8c64dSAndroid Build Coastguard Worker            root, sysroot, binary_name, port, solib_search_path, vscode_launch_props)
368*90c8c64dSAndroid Build Coastguard Worker    elif debugger == 'lldb':
369*90c8c64dSAndroid Build Coastguard Worker        return generate_lldb_script(
370*90c8c64dSAndroid Build Coastguard Worker            root, sysroot, binary_name, port, solib_search_path)
371*90c8c64dSAndroid Build Coastguard Worker    else:
372*90c8c64dSAndroid Build Coastguard Worker        raise Exception("Unknown debugger type " + debugger)
373*90c8c64dSAndroid Build Coastguard Worker
374*90c8c64dSAndroid Build Coastguard Worker
375*90c8c64dSAndroid Build Coastguard Workerdef insert_commands_into_vscode_config(dst_launch_config: str, setup_commands: str) -> str:
376*90c8c64dSAndroid Build Coastguard Worker    """Inserts setup commands into launch config between two marker lines.
377*90c8c64dSAndroid Build Coastguard Worker    Marker lines are set in global variables g_vscode_config_marker_end and g_vscode_config_marker_end.
378*90c8c64dSAndroid Build Coastguard Worker    The commands are inserted with the same indentation as the first marker line.
379*90c8c64dSAndroid Build Coastguard Worker
380*90c8c64dSAndroid Build Coastguard Worker    Args:
381*90c8c64dSAndroid Build Coastguard Worker        dst_launch_config: Config to insert commands into.
382*90c8c64dSAndroid Build Coastguard Worker        setup_commands: Commands to insert.
383*90c8c64dSAndroid Build Coastguard Worker    Returns:
384*90c8c64dSAndroid Build Coastguard Worker        Config with inserted commands.
385*90c8c64dSAndroid Build Coastguard Worker    Raises:
386*90c8c64dSAndroid Build Coastguard Worker        ValueError if the begin marker is not found or not terminated with an end marker.
387*90c8c64dSAndroid Build Coastguard Worker    """
388*90c8c64dSAndroid Build Coastguard Worker
389*90c8c64dSAndroid Build Coastguard Worker    # We expect the files to be small (~10s KB), so we use simple string concatenation
390*90c8c64dSAndroid Build Coastguard Worker    # for simplicity and readability even if it is slower.
391*90c8c64dSAndroid Build Coastguard Worker    output = ""
392*90c8c64dSAndroid Build Coastguard Worker    found_at_least_one_begin = False
393*90c8c64dSAndroid Build Coastguard Worker    unterminated_begin_line = None
394*90c8c64dSAndroid Build Coastguard Worker
395*90c8c64dSAndroid Build Coastguard Worker    # It might be tempting to rewrite this using find() or even regexes,
396*90c8c64dSAndroid Build Coastguard Worker    # but keeping track of line numbers, preserving whitespace, and detecting indent
397*90c8c64dSAndroid Build Coastguard Worker    # becomes tricky enough that this simple loop is more clear.
398*90c8c64dSAndroid Build Coastguard Worker    for linenum, line in enumerate(dst_launch_config.splitlines(keepends=True), start=1):
399*90c8c64dSAndroid Build Coastguard Worker       if unterminated_begin_line != None:
400*90c8c64dSAndroid Build Coastguard Worker           if line.strip() == g_vscode_config_marker_end:
401*90c8c64dSAndroid Build Coastguard Worker               unterminated_begin_line = None
402*90c8c64dSAndroid Build Coastguard Worker           else:
403*90c8c64dSAndroid Build Coastguard Worker               continue
404*90c8c64dSAndroid Build Coastguard Worker       output += line
405*90c8c64dSAndroid Build Coastguard Worker       if line.strip() == g_vscode_config_marker_begin:
406*90c8c64dSAndroid Build Coastguard Worker           found_at_least_one_begin = True
407*90c8c64dSAndroid Build Coastguard Worker           unterminated_begin_line = linenum
408*90c8c64dSAndroid Build Coastguard Worker           marker_indent = line[:line.find(g_vscode_config_marker_begin)]
409*90c8c64dSAndroid Build Coastguard Worker           output += textwrap.indent(setup_commands, marker_indent) + '\n'
410*90c8c64dSAndroid Build Coastguard Worker
411*90c8c64dSAndroid Build Coastguard Worker    if not found_at_least_one_begin:
412*90c8c64dSAndroid Build Coastguard Worker       raise ValueError(f"Did not find begin marker line '{g_vscode_config_marker_begin}' " +
413*90c8c64dSAndroid Build Coastguard Worker                        "in the VSCode launch file")
414*90c8c64dSAndroid Build Coastguard Worker
415*90c8c64dSAndroid Build Coastguard Worker    if unterminated_begin_line is not None:
416*90c8c64dSAndroid Build Coastguard Worker       raise ValueError(f"Unterminated begin marker at line {unterminated_begin_line} " +
417*90c8c64dSAndroid Build Coastguard Worker                        f"in the VSCode launch file. Add end marker line to file: '{g_vscode_config_marker_end}'")
418*90c8c64dSAndroid Build Coastguard Worker
419*90c8c64dSAndroid Build Coastguard Worker    return output
420*90c8c64dSAndroid Build Coastguard Worker
421*90c8c64dSAndroid Build Coastguard Worker
422*90c8c64dSAndroid Build Coastguard Workerdef replace_file_contents(dst_path: os.PathLike, contents: str) -> None:
423*90c8c64dSAndroid Build Coastguard Worker    """Replaces the contents of the file pointed to by dst_path.
424*90c8c64dSAndroid Build Coastguard Worker
425*90c8c64dSAndroid Build Coastguard Worker    This function writes the new contents into a temporary file, then atomically swaps it with
426*90c8c64dSAndroid Build Coastguard Worker    the target file. This way if a write fails, the original file is not overwritten.
427*90c8c64dSAndroid Build Coastguard Worker
428*90c8c64dSAndroid Build Coastguard Worker    Args:
429*90c8c64dSAndroid Build Coastguard Worker        dst_path: The path to the file to be replaced.
430*90c8c64dSAndroid Build Coastguard Worker        contents: The new contents of the file.
431*90c8c64dSAndroid Build Coastguard Worker    Raises:
432*90c8c64dSAndroid Build Coastguard Worker        Forwards exceptions from underlying filesystem methods.
433*90c8c64dSAndroid Build Coastguard Worker    """
434*90c8c64dSAndroid Build Coastguard Worker    tempf = tempfile.NamedTemporaryFile('w', delete=False)
435*90c8c64dSAndroid Build Coastguard Worker    try:
436*90c8c64dSAndroid Build Coastguard Worker        tempf.write(contents)
437*90c8c64dSAndroid Build Coastguard Worker        os.replace(tempf.name, dst_path)
438*90c8c64dSAndroid Build Coastguard Worker    except:
439*90c8c64dSAndroid Build Coastguard Worker        os.remove(tempf.name)
440*90c8c64dSAndroid Build Coastguard Worker        raise
441*90c8c64dSAndroid Build Coastguard Worker
442*90c8c64dSAndroid Build Coastguard Worker
443*90c8c64dSAndroid Build Coastguard Workerdef write_vscode_config(vscode_launch_file: pathlib.Path, setup_commands: str) -> None:
444*90c8c64dSAndroid Build Coastguard Worker    """Writes setup_commands into the file pointed by vscode_launch_file.
445*90c8c64dSAndroid Build Coastguard Worker
446*90c8c64dSAndroid Build Coastguard Worker    See insert_commands_into_vscode_config for the description of how the setup commands are written.
447*90c8c64dSAndroid Build Coastguard Worker    """
448*90c8c64dSAndroid Build Coastguard Worker    contents = insert_commands_into_vscode_config(vscode_launch_file.read_text(), setup_commands)
449*90c8c64dSAndroid Build Coastguard Worker    replace_file_contents(vscode_launch_file, contents)
450*90c8c64dSAndroid Build Coastguard Worker
451*90c8c64dSAndroid Build Coastguard Worker
452*90c8c64dSAndroid Build Coastguard Workerdef do_main() -> None:
453*90c8c64dSAndroid Build Coastguard Worker    required_env = ["ANDROID_BUILD_TOP",
454*90c8c64dSAndroid Build Coastguard Worker                    "ANDROID_PRODUCT_OUT", "TARGET_PRODUCT"]
455*90c8c64dSAndroid Build Coastguard Worker    for env in required_env:
456*90c8c64dSAndroid Build Coastguard Worker        if env not in os.environ:
457*90c8c64dSAndroid Build Coastguard Worker            sys.exit(
458*90c8c64dSAndroid Build Coastguard Worker                "Environment variable '{}' not defined, have you run lunch?".format(env))
459*90c8c64dSAndroid Build Coastguard Worker
460*90c8c64dSAndroid Build Coastguard Worker    args = parse_args()
461*90c8c64dSAndroid Build Coastguard Worker    device = args.device
462*90c8c64dSAndroid Build Coastguard Worker
463*90c8c64dSAndroid Build Coastguard Worker    if device is None:
464*90c8c64dSAndroid Build Coastguard Worker        sys.exit("ERROR: Failed to find device.")
465*90c8c64dSAndroid Build Coastguard Worker
466*90c8c64dSAndroid Build Coastguard Worker    root = os.environ["ANDROID_BUILD_TOP"]
467*90c8c64dSAndroid Build Coastguard Worker    sysroot = os.path.join(os.environ["ANDROID_PRODUCT_OUT"], "symbols")
468*90c8c64dSAndroid Build Coastguard Worker
469*90c8c64dSAndroid Build Coastguard Worker    if args.cwd:
470*90c8c64dSAndroid Build Coastguard Worker        if not get_device_dir_exists(device, args.cwd):
471*90c8c64dSAndroid Build Coastguard Worker            raise ValueError('Working directory does not exist on device: {}'.format(args.cwd))
472*90c8c64dSAndroid Build Coastguard Worker        if args.chroot:
473*90c8c64dSAndroid Build Coastguard Worker            # See the comment in start_gdbserver about why this is not implemented.
474*90c8c64dSAndroid Build Coastguard Worker            raise ValueError('--cwd and --chroot cannot be used together')
475*90c8c64dSAndroid Build Coastguard Worker
476*90c8c64dSAndroid Build Coastguard Worker    # Make sure the environment matches the attached device.
477*90c8c64dSAndroid Build Coastguard Worker    # Skip when running in a chroot because the chroot lunch target may not
478*90c8c64dSAndroid Build Coastguard Worker    # match the device's lunch target.
479*90c8c64dSAndroid Build Coastguard Worker    if not args.chroot:
480*90c8c64dSAndroid Build Coastguard Worker        verify_device(device)
481*90c8c64dSAndroid Build Coastguard Worker
482*90c8c64dSAndroid Build Coastguard Worker    debug_socket = "/data/local/tmp/debug_socket"
483*90c8c64dSAndroid Build Coastguard Worker    pid = None
484*90c8c64dSAndroid Build Coastguard Worker    run_cmd = None
485*90c8c64dSAndroid Build Coastguard Worker
486*90c8c64dSAndroid Build Coastguard Worker    # Fetch binary for -p, -n.
487*90c8c64dSAndroid Build Coastguard Worker    binary_file, pid, run_cmd = handle_switches(args, sysroot)
488*90c8c64dSAndroid Build Coastguard Worker
489*90c8c64dSAndroid Build Coastguard Worker    vscode_launch_props = None
490*90c8c64dSAndroid Build Coastguard Worker    if args.vscode_launch_props:
491*90c8c64dSAndroid Build Coastguard Worker        if args.setup_forwarding != "vscode-lldb":
492*90c8c64dSAndroid Build Coastguard Worker            raise ValueError(
493*90c8c64dSAndroid Build Coastguard Worker                'vscode-launch-props requires --setup-forwarding=vscode-lldb')
494*90c8c64dSAndroid Build Coastguard Worker        vscode_launch_props = json.loads(args.vscode_launch_props)
495*90c8c64dSAndroid Build Coastguard Worker
496*90c8c64dSAndroid Build Coastguard Worker    vscode_launch_file = None
497*90c8c64dSAndroid Build Coastguard Worker    if args.vscode_launch_file:
498*90c8c64dSAndroid Build Coastguard Worker        if args.setup_forwarding != "vscode-lldb":
499*90c8c64dSAndroid Build Coastguard Worker            raise ValueError(
500*90c8c64dSAndroid Build Coastguard Worker                'vscode-launch-file requires --setup-forwarding=vscode-lldb')
501*90c8c64dSAndroid Build Coastguard Worker        vscode_launch_file = args.vscode_launch_file
502*90c8c64dSAndroid Build Coastguard Worker
503*90c8c64dSAndroid Build Coastguard Worker    with binary_file:
504*90c8c64dSAndroid Build Coastguard Worker        if sys.platform.startswith("linux"):
505*90c8c64dSAndroid Build Coastguard Worker            platform_name = "linux-x86"
506*90c8c64dSAndroid Build Coastguard Worker        elif sys.platform.startswith("darwin"):
507*90c8c64dSAndroid Build Coastguard Worker            platform_name = "darwin-x86"
508*90c8c64dSAndroid Build Coastguard Worker        else:
509*90c8c64dSAndroid Build Coastguard Worker            sys.exit("Unknown platform: {}".format(sys.platform))
510*90c8c64dSAndroid Build Coastguard Worker
511*90c8c64dSAndroid Build Coastguard Worker        arch = gdbrunner.get_binary_arch(binary_file)
512*90c8c64dSAndroid Build Coastguard Worker        is64bit = arch.endswith("64")
513*90c8c64dSAndroid Build Coastguard Worker
514*90c8c64dSAndroid Build Coastguard Worker        # Make sure we have the linker
515*90c8c64dSAndroid Build Coastguard Worker        clang_base = 'prebuilts/clang/host'
516*90c8c64dSAndroid Build Coastguard Worker        clang_version = read_toolchain_config(root)
517*90c8c64dSAndroid Build Coastguard Worker        toolchain_path = os.path.join(root, clang_base, platform_name,
518*90c8c64dSAndroid Build Coastguard Worker                                      clang_version)
519*90c8c64dSAndroid Build Coastguard Worker        llvm_readobj_path = os.path.join(toolchain_path, "bin", "llvm-readobj")
520*90c8c64dSAndroid Build Coastguard Worker        interp = gdbrunner.get_binary_interp(binary_file.name, llvm_readobj_path)
521*90c8c64dSAndroid Build Coastguard Worker        linker_search_dir = ensure_linker(device, sysroot, interp)
522*90c8c64dSAndroid Build Coastguard Worker
523*90c8c64dSAndroid Build Coastguard Worker        tracer_pid = get_tracer_pid(device, pid)
524*90c8c64dSAndroid Build Coastguard Worker        if tracer_pid == 0:
525*90c8c64dSAndroid Build Coastguard Worker            cmd_prefix = args.su_cmd
526*90c8c64dSAndroid Build Coastguard Worker            if args.env:
527*90c8c64dSAndroid Build Coastguard Worker                cmd_prefix += ['env'] + [v[0] for v in args.env]
528*90c8c64dSAndroid Build Coastguard Worker
529*90c8c64dSAndroid Build Coastguard Worker            # Start lldb-server.
530*90c8c64dSAndroid Build Coastguard Worker            server_local_path = get_lldb_server_path(root, clang_base, clang_version, arch)
531*90c8c64dSAndroid Build Coastguard Worker            server_remote_path = "/data/local/tmp/{}-lldb-server".format(arch)
532*90c8c64dSAndroid Build Coastguard Worker            gdbrunner.start_gdbserver(
533*90c8c64dSAndroid Build Coastguard Worker                device, server_local_path, server_remote_path,
534*90c8c64dSAndroid Build Coastguard Worker                target_pid=pid, run_cmd=run_cmd, debug_socket=debug_socket,
535*90c8c64dSAndroid Build Coastguard Worker                port=args.port, run_as_cmd=cmd_prefix, lldb=True, chroot=args.chroot, cwd=args.cwd)
536*90c8c64dSAndroid Build Coastguard Worker        else:
537*90c8c64dSAndroid Build Coastguard Worker            print(
538*90c8c64dSAndroid Build Coastguard Worker                "Connecting to tracing pid {} using local port {}".format(
539*90c8c64dSAndroid Build Coastguard Worker                    tracer_pid, args.port))
540*90c8c64dSAndroid Build Coastguard Worker            gdbrunner.forward_gdbserver_port(device, local=args.port,
541*90c8c64dSAndroid Build Coastguard Worker                                             remote="tcp:{}".format(args.port))
542*90c8c64dSAndroid Build Coastguard Worker
543*90c8c64dSAndroid Build Coastguard Worker        debugger_path = get_lldb_path(toolchain_path)
544*90c8c64dSAndroid Build Coastguard Worker        debugger = args.setup_forwarding or 'lldb'
545*90c8c64dSAndroid Build Coastguard Worker
546*90c8c64dSAndroid Build Coastguard Worker        # Generate the lldb script.
547*90c8c64dSAndroid Build Coastguard Worker        setup_commands = generate_setup_script(sysroot=sysroot,
548*90c8c64dSAndroid Build Coastguard Worker                                               linker_search_dir=linker_search_dir,
549*90c8c64dSAndroid Build Coastguard Worker                                               binary_name=binary_file.name,
550*90c8c64dSAndroid Build Coastguard Worker                                               is64bit=is64bit,
551*90c8c64dSAndroid Build Coastguard Worker                                               port=args.port,
552*90c8c64dSAndroid Build Coastguard Worker                                               debugger=debugger,
553*90c8c64dSAndroid Build Coastguard Worker                                               vscode_launch_props=vscode_launch_props)
554*90c8c64dSAndroid Build Coastguard Worker
555*90c8c64dSAndroid Build Coastguard Worker        if not args.setup_forwarding:
556*90c8c64dSAndroid Build Coastguard Worker            # Print a newline to separate our messages from the GDB session.
557*90c8c64dSAndroid Build Coastguard Worker            print("")
558*90c8c64dSAndroid Build Coastguard Worker
559*90c8c64dSAndroid Build Coastguard Worker            # Start lldb.
560*90c8c64dSAndroid Build Coastguard Worker            gdbrunner.start_gdb(debugger_path, setup_commands, lldb=True)
561*90c8c64dSAndroid Build Coastguard Worker        else:
562*90c8c64dSAndroid Build Coastguard Worker            if args.setup_forwarding == "vscode-lldb" and vscode_launch_file:
563*90c8c64dSAndroid Build Coastguard Worker                write_vscode_config(pathlib.Path(vscode_launch_file) , setup_commands)
564*90c8c64dSAndroid Build Coastguard Worker                print(f"Generated config written to '{vscode_launch_file}'")
565*90c8c64dSAndroid Build Coastguard Worker            else:
566*90c8c64dSAndroid Build Coastguard Worker                print("")
567*90c8c64dSAndroid Build Coastguard Worker                print(setup_commands)
568*90c8c64dSAndroid Build Coastguard Worker                print("")
569*90c8c64dSAndroid Build Coastguard Worker                if args.setup_forwarding == "vscode-lldb":
570*90c8c64dSAndroid Build Coastguard Worker                    print(textwrap.dedent("""
571*90c8c64dSAndroid Build Coastguard Worker                            Paste the above json into .vscode/launch.json and start the debugger as
572*90c8c64dSAndroid Build Coastguard Worker                            normal."""))
573*90c8c64dSAndroid Build Coastguard Worker                else:
574*90c8c64dSAndroid Build Coastguard Worker                    print(textwrap.dedent("""
575*90c8c64dSAndroid Build Coastguard Worker                            Paste the lldb commands above into the lldb frontend to set up the
576*90c8c64dSAndroid Build Coastguard Worker                            lldb-server connection."""))
577*90c8c64dSAndroid Build Coastguard Worker
578*90c8c64dSAndroid Build Coastguard Worker            print(textwrap.dedent("""
579*90c8c64dSAndroid Build Coastguard Worker                        Press enter in this terminal once debugging is finished to shut lldb-server
580*90c8c64dSAndroid Build Coastguard Worker                        down and close all the ports."""))
581*90c8c64dSAndroid Build Coastguard Worker            print("")
582*90c8c64dSAndroid Build Coastguard Worker            input("Press enter to shut down lldb-server")
583*90c8c64dSAndroid Build Coastguard Worker
584*90c8c64dSAndroid Build Coastguard Worker
585*90c8c64dSAndroid Build Coastguard Workerdef main():
586*90c8c64dSAndroid Build Coastguard Worker    try:
587*90c8c64dSAndroid Build Coastguard Worker        do_main()
588*90c8c64dSAndroid Build Coastguard Worker    finally:
589*90c8c64dSAndroid Build Coastguard Worker        global g_temp_dirs
590*90c8c64dSAndroid Build Coastguard Worker        for temp_dir in g_temp_dirs:
591*90c8c64dSAndroid Build Coastguard Worker            shutil.rmtree(temp_dir)
592*90c8c64dSAndroid Build Coastguard Worker
593*90c8c64dSAndroid Build Coastguard Worker
594*90c8c64dSAndroid Build Coastguard Workerif __name__ == "__main__":
595*90c8c64dSAndroid Build Coastguard Worker    main()
596