blob: cce743352f199e15f68a54b8f89f1a0e7688eee7 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2014 The ChromiumOS Authors
cmticeb70801a2014-12-11 14:29:34 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Wrapper for running gdb.
6
7This handles the fun details like running against the right sysroot, via
8qemu, bind mounts, etc...
9"""
10
cmticeb70801a2014-12-11 14:29:34 -080011import argparse
12import contextlib
13import errno
Chris McDonald59650c32021-07-20 15:29:28 -060014import logging
cmticeb70801a2014-12-11 14:29:34 -080015import os
16import sys
17import tempfile
18
Ben Pastene8d754972019-12-04 15:03:23 -080019from chromite.cli.cros import cros_chrome_sdk
Mike Frysinger06a51c82021-04-06 11:39:17 -040020from chromite.lib import build_target_lib
cmticeb70801a2014-12-11 14:29:34 -080021from chromite.lib import commandline
Ben Pastene8d754972019-12-04 15:03:23 -080022from chromite.lib import constants
cmticeb70801a2014-12-11 14:29:34 -080023from chromite.lib import cros_build_lib
24from chromite.lib import namespaces
25from chromite.lib import osutils
Ben Pastenec228d492018-07-02 13:53:58 -070026from chromite.lib import path_util
cmtice932e0aa2015-02-27 11:49:12 -080027from chromite.lib import qemu
cmticef23cb132015-04-10 15:13:00 -070028from chromite.lib import remote_access
cmticeb70801a2014-12-11 14:29:34 -080029from chromite.lib import retry_util
cmticef23cb132015-04-10 15:13:00 -070030from chromite.lib import toolchain
cmticeb70801a2014-12-11 14:29:34 -080031
Mike Frysinger1c76d4c2020-02-08 23:35:29 -050032
cmticef23cb132015-04-10 15:13:00 -070033class GdbException(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060034 """Base exception for this module."""
cmticef23cb132015-04-10 15:13:00 -070035
36
37class GdbBadRemoteDeviceError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060038 """Raised when remote device does not exist or is not responding."""
cmticef23cb132015-04-10 15:13:00 -070039
40
41class GdbMissingSysrootError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060042 """Raised when path to sysroot cannot be found in chroot."""
cmticef23cb132015-04-10 15:13:00 -070043
44
45class GdbMissingInferiorError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060046 """Raised when the binary to be debugged cannot be found."""
cmticef23cb132015-04-10 15:13:00 -070047
48
49class GdbMissingDebuggerError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060050 """Raised when cannot find correct version of debugger."""
cmticef23cb132015-04-10 15:13:00 -070051
52
53class GdbCannotFindRemoteProcessError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060054 """Raised when cannot find requested executing process on remote device."""
cmticef23cb132015-04-10 15:13:00 -070055
56
57class GdbUnableToStartGdbserverError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060058 """Raised when error occurs trying to start gdbserver on remote device."""
cmticef23cb132015-04-10 15:13:00 -070059
60
61class GdbTooManyPidsError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060062 """Raised when more than one matching pid is found running on device."""
cmticef23cb132015-04-10 15:13:00 -070063
64
65class GdbEarlyExitError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060066 """Raised when user requests to exit early."""
cmticef23cb132015-04-10 15:13:00 -070067
68
69class GdbCannotDetectBoardError(GdbException):
Alex Klein8b444532023-04-11 16:35:24 -060070 """Board isn't specified and can't be automatically determined."""
Alex Klein1699fab2022-09-08 08:46:06 -060071
cmticeb70801a2014-12-11 14:29:34 -080072
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070073class GdbSimpleChromeBinaryError(GdbException):
Alex Klein8b444532023-04-11 16:35:24 -060074 """None or multiple chrome binaries are under out_${board} dir."""
Alex Klein1699fab2022-09-08 08:46:06 -060075
cmtice932e0aa2015-02-27 11:49:12 -080076
cmticeb70801a2014-12-11 14:29:34 -080077class BoardSpecificGdb(object):
Alex Klein1699fab2022-09-08 08:46:06 -060078 """Framework for running gdb."""
cmticeb70801a2014-12-11 14:29:34 -080079
Alex Klein1699fab2022-09-08 08:46:06 -060080 _BIND_MOUNT_PATHS = ("dev", "dev/pts", "proc", "mnt/host/source", "sys")
81 _GDB = "/usr/bin/gdb"
82 _EXTRA_SSH_SETTINGS = {
83 "CheckHostIP": "no",
84 "BatchMode": "yes",
85 "LogLevel": "QUIET",
86 }
87 _MISSING_DEBUG_INFO_MSG = """
cmticef23cb132015-04-10 15:13:00 -070088%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
89 The debug symbols for that package may not be installed. To install the debug
90 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080091
cmticef23cb132015-04-10 15:13:00 -070092 cros_install_debug_syms --board=%(board)s %(package)s
93
94To install the debug symbols for all available packages, run:
95
96 cros_install_debug_syms --board=%(board)s --all"""
chromeos-ci-prodc1e61312023-04-27 19:46:48 -070097 _ASH_CHROME_REMOTE_BIN = "/opt/google/chrome/chrome"
98 _LACROS_CHROME_REMOTE_BIN = "/usr/local/lacros-chrome/chrome"
cmticef23cb132015-04-10 15:13:00 -070099
Alex Klein1699fab2022-09-08 08:46:06 -0600100 def __init__(
101 self,
102 board,
103 gdb_args,
104 inf_cmd,
105 inf_args,
106 remote,
107 pid,
108 remote_process_name,
109 cgdb_flag,
110 ping,
111 binary,
Marc Grimme5441aba2023-04-14 16:56:36 +0200112 gdb_binary,
Alex Klein1699fab2022-09-08 08:46:06 -0600113 ):
114 self.board = board
115 self.sysroot = None
116 self.prompt = "(gdb) "
117 self.inf_cmd = inf_cmd
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700118 if not self.inf_cmd:
119 if remote_process_name.startswith("lacros-"):
120 self.inf_cmd = self._LACROS_CHROME_REMOTE_BIN
121 else:
122 self.inf_cmd = self._ASH_CHROME_REMOTE_BIN
Alex Klein1699fab2022-09-08 08:46:06 -0600123 self.run_as_root = False
124 self.gdb_args = gdb_args
125 self.inf_args = inf_args
126 self.remote = remote.hostname if remote else None
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700127 # strip off the lacros- or ash- specifics
128 self.remote_process_name = remote_process_name.lstrip("lacros-").lstrip(
129 "ash-"
130 )
Alex Klein1699fab2022-09-08 08:46:06 -0600131 self.pid = pid
Alex Klein1699fab2022-09-08 08:46:06 -0600132 # Port used for sending ssh commands to DUT.
133 self.remote_port = remote.port if remote else None
134 # Port for communicating between gdb & gdbserver.
135 self.gdbserver_port = remote_access.GetUnusedPort()
136 self.ssh_settings = remote_access.CompileSSHConnectSettings(
137 **self._EXTRA_SSH_SETTINGS
138 )
139 self.cgdb = cgdb_flag
140 self.framework = "auto"
141 self.qemu = None
142 self.device = None
Marc Grimme5441aba2023-04-14 16:56:36 +0200143 self.cross_gdb = gdb_binary
Alex Klein1699fab2022-09-08 08:46:06 -0600144 self.ping = ping
145 self.binary = binary
146 self.in_chroot = None
147 self.chrome_path = None
148 self.sdk_path = None
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 def IsInChroot(self):
151 """Decide whether we are in chroot or chrome-sdk."""
152 return os.path.exists("/mnt/host/source/chromite/")
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700153
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700154 def IsLacros(self):
155 """The --attach option specifies the type of if you want to attach to browser, renderer or gpu-process. Prefixed with either lacros-, ash- you can specify which browser you want to attach to. Default is ash."""
156 return self.inf_cmd == self._LACROS_CHROME_REMOTE_BIN
157
Alex Klein1699fab2022-09-08 08:46:06 -0600158 def SimpleChromeGdb(self):
159 """Get the name of the cross gdb based on board name."""
160 bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
161 cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
162 self.sdk_path,
163 self.board,
164 )
165 bin_path = os.path.join(bin_path, "bin")
166 for f in os.listdir(bin_path):
167 if f.endswith("gdb"):
168 return os.path.join(bin_path, f)
169 raise GdbMissingDebuggerError(
170 "Cannot find cross gdb for %s." % self.board
171 )
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700172
Alex Klein1699fab2022-09-08 08:46:06 -0600173 def SimpleChromeSysroot(self):
174 """Get the sysroot in simple chrome."""
175 sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
176 constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board
177 )
178 if not sysroot:
179 raise GdbMissingSysrootError(
180 "Cannot find sysroot for %s at %s" % (self.board, self.sdk_path)
181 )
182 return sysroot
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700183
Alex Klein1699fab2022-09-08 08:46:06 -0600184 def GetSimpleChromeBinary(self):
185 """Get path to the binary in simple chrome."""
186 if self.binary:
187 return self.binary
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700188
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700189 output_dir = (
190 os.path.join(self.chrome_path, "src", f"out_{self.board}_lacros")
191 if self.IsLacros()
192 else os.path.join(self.chrome_path, "src", f"out_{self.board}")
193 )
Alex Klein1699fab2022-09-08 08:46:06 -0600194 target_binary = None
195 binary_name = os.path.basename(self.inf_cmd)
196 for root, _, files in os.walk(output_dir):
197 for f in files:
198 if f == binary_name:
199 if target_binary is None:
200 target_binary = os.path.join(root, f)
201 else:
202 raise GdbSimpleChromeBinaryError(
Alex Klein8b444532023-04-11 16:35:24 -0600203 "There are multiple %s under %s. Please specify "
204 "the path to the binary via --binary"
Alex Klein1699fab2022-09-08 08:46:06 -0600205 % (binary_name, output_dir)
206 )
207 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700208 raise GdbSimpleChromeBinaryError(
Alex Klein1699fab2022-09-08 08:46:06 -0600209 "There is no %s under %s." % (binary_name, output_dir)
210 )
211 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800212
Alex Klein1699fab2022-09-08 08:46:06 -0600213 def VerifyAndFinishInitialization(self, device):
214 """Verify files/processes exist and flags are correct."""
215 if not self.board:
216 if self.remote:
217 self.board = cros_build_lib.GetBoard(
218 device_board=device.board,
219 override_board=self.board,
220 strict=True,
221 )
222 else:
223 raise GdbCannotDetectBoardError(
224 "Cannot determine which board to use. "
225 "Please specify the with --board flag."
226 )
227 self.in_chroot = self.IsInChroot()
228 self.prompt = "(%s-gdb) " % self.board
229 if self.in_chroot:
230 self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
231 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
Marc Grimme5441aba2023-04-14 16:56:36 +0200232 if not self.cross_gdb:
233 self.cross_gdb = self.GetCrossGdb()
Alex Klein1699fab2022-09-08 08:46:06 -0600234 else:
235 self.chrome_path = os.path.realpath(
236 os.path.join(
237 os.path.dirname(os.path.realpath(__file__)), "../../../.."
238 )
239 )
240 self.sdk_path = path_util.FindCacheDir()
241 self.sysroot = self.SimpleChromeSysroot()
Marc Grimme5441aba2023-04-14 16:56:36 +0200242 if not self.cross_gdb:
243 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700244
Alex Klein1699fab2022-09-08 08:46:06 -0600245 if self.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600246 # If given remote process name, find pid & inf_cmd on remote device.
247 if self.remote_process_name or self.pid:
248 self._FindRemoteProcess(device)
cmticef23cb132015-04-10 15:13:00 -0700249
Alex Klein1699fab2022-09-08 08:46:06 -0600250 # Verify that sysroot is valid (exists).
251 if not os.path.isdir(self.sysroot):
252 raise GdbMissingSysrootError(
253 "Sysroot does not exist: %s" % self.sysroot
254 )
cmticef23cb132015-04-10 15:13:00 -0700255
Alex Klein1699fab2022-09-08 08:46:06 -0600256 self.device = device
257 if not self.in_chroot:
cmticeb70801a2014-12-11 14:29:34 -0800258 return
259
Alex Klein1699fab2022-09-08 08:46:06 -0600260 sysroot_inf_cmd = ""
261 if self.inf_cmd:
262 sysroot_inf_cmd = os.path.join(
263 self.sysroot, self.inf_cmd.lstrip("/")
264 )
cmticeb70801a2014-12-11 14:29:34 -0800265
Alex Klein1699fab2022-09-08 08:46:06 -0600266 # Verify that inf_cmd, if given, exists.
267 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
268 raise GdbMissingInferiorError(
269 "Cannot find file %s (in sysroot)." % sysroot_inf_cmd
270 )
cmticeb70801a2014-12-11 14:29:34 -0800271
Alex Klein8b444532023-04-11 16:35:24 -0600272 # Check to see if inf_cmd is stripped, and if so, check to see if debug
273 # file exists. If not, tell user and give them the option of quitting &
274 # getting the debug info.
Alex Klein1699fab2022-09-08 08:46:06 -0600275 if sysroot_inf_cmd:
276 stripped_info = cros_build_lib.run(
277 ["file", sysroot_inf_cmd], capture_output=True, encoding="utf-8"
278 ).stdout
279 if " not stripped" not in stripped_info:
280 debug_file = os.path.join(
281 self.sysroot, "usr/lib/debug", self.inf_cmd.lstrip("/")
282 )
283 debug_file += ".debug"
284 if not os.path.exists(debug_file):
285 equery = "equery-%s" % self.board
286 package = cros_build_lib.run(
287 [equery, "-q", "b", self.inf_cmd],
288 capture_output=True,
289 encoding="utf-8",
290 ).stdout
291 # pylint: disable=logging-not-lazy
292 logging.info(
293 self._MISSING_DEBUG_INFO_MSG
294 % {
295 "board": self.board,
296 "inf_cmd": self.inf_cmd,
297 "package": package,
298 "debug_file": debug_file,
299 }
300 )
301 answer = cros_build_lib.BooleanPrompt()
302 if not answer:
303 raise GdbEarlyExitError(
304 "Exiting early, at user request."
305 )
cmticeb70801a2014-12-11 14:29:34 -0800306
Alex Klein1699fab2022-09-08 08:46:06 -0600307 # Set up qemu, if appropriate.
308 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
309 if qemu_arch is None:
310 self.framework = "ldso"
cmticef23cb132015-04-10 15:13:00 -0700311 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600312 self.framework = "qemu"
313 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticef23cb132015-04-10 15:13:00 -0700314
Alex Klein1699fab2022-09-08 08:46:06 -0600315 if self.remote:
316 # Verify cgdb flag info.
317 if self.cgdb:
318 if osutils.Which("cgdb") is None:
319 raise GdbMissingDebuggerError(
320 "Cannot find cgdb. Please install " "cgdb first."
321 )
cmticef23cb132015-04-10 15:13:00 -0700322
Alex Klein1699fab2022-09-08 08:46:06 -0600323 def RemoveSysrootPrefix(self, path):
324 """Returns the given path with any sysroot prefix removed."""
325 # If the sysroot is /, then the paths are already normalized.
326 if self.sysroot != "/" and path.startswith(self.sysroot):
327 path = path.replace(self.sysroot, "", 1)
328 return path
cmticef23cb132015-04-10 15:13:00 -0700329
Alex Klein1699fab2022-09-08 08:46:06 -0600330 @staticmethod
331 def GetNonRootAccount():
332 """Return details about the non-root account we want to use.
cmticef23cb132015-04-10 15:13:00 -0700333
Alex Klein1699fab2022-09-08 08:46:06 -0600334 Returns:
335 A tuple of (username, uid, gid, home).
336 """
337 return (
338 os.environ.get("SUDO_USER", "nobody"),
339 int(os.environ.get("SUDO_UID", "65534")),
340 int(os.environ.get("SUDO_GID", "65534")),
341 # Should we find a better home?
342 "/tmp/portage",
343 )
cmticef23cb132015-04-10 15:13:00 -0700344
Alex Klein1699fab2022-09-08 08:46:06 -0600345 @staticmethod
346 @contextlib.contextmanager
347 def LockDb(db):
348 """Lock an account database.
cmticef23cb132015-04-10 15:13:00 -0700349
Alex Klein1699fab2022-09-08 08:46:06 -0600350 We use the same algorithm as shadow/user.eclass. This way we don't race
351 and corrupt things in parallel.
352 """
353 lock = "%s.lock" % db
354 _, tmplock = tempfile.mkstemp(prefix="%s.platform." % lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600355
Alex Klein1699fab2022-09-08 08:46:06 -0600356 # First try forever to grab the lock.
357 retry = lambda e: e.errno == errno.EEXIST
358 # Retry quickly at first, but slow down over time.
359 try:
360 retry_util.GenericRetry(
361 retry, 60, os.link, tmplock, lock, sleep=0.1
362 )
363 except Exception as e:
364 raise Exception("Could not grab lock %s. %s" % (lock, e))
Raul E Rangel746c45d2018-05-09 09:27:31 -0600365
Alex Klein1699fab2022-09-08 08:46:06 -0600366 # Yield while holding the lock, but try to clean it no matter what.
367 try:
368 os.unlink(tmplock)
369 yield lock
370 finally:
371 os.unlink(lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600372
Alex Klein1699fab2022-09-08 08:46:06 -0600373 def SetupUser(self):
374 """Propogate the user name<->id mapping from outside the chroot.
cmticef23cb132015-04-10 15:13:00 -0700375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 Some unittests use getpwnam($USER), as does bash. If the account
377 is not registered in the sysroot, they get back errors.
378 """
379 MAGIC_GECOS = (
380 "Added by your friendly platform test helper; do not modify"
381 )
382 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
383 SDK_GECOS = "ChromeOS Developer"
384
385 user, uid, gid, home = self.GetNonRootAccount()
386 if user == "nobody":
387 return
388
389 passwd_db = os.path.join(self.sysroot, "etc", "passwd")
390 with self.LockDb(passwd_db):
391 data = osutils.ReadFile(passwd_db)
392 accts = data.splitlines()
393 for acct in accts:
394 passwd = acct.split(":")
395 if passwd[0] == user:
396 # Did the sdk make this account?
397 if passwd[4] == SDK_GECOS:
398 # Don't modify it (see below) since we didn't create it.
399 return
400
401 # Did we make this account?
402 if passwd[4] != MAGIC_GECOS:
403 raise RuntimeError(
404 "your passwd db (%s) has unmanaged acct %s"
405 % (passwd_db, user)
406 )
407
Alex Klein8b444532023-04-11 16:35:24 -0600408 # Maybe we should see if it needs to be updated? Like if
409 # they changed UIDs? But we don't really check that
410 # elsewhere ...
Alex Klein1699fab2022-09-08 08:46:06 -0600411 return
412
413 acct = (
414 "%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s"
415 % {
416 "name": user,
417 "uid": uid,
418 "gid": gid,
419 "gecos": MAGIC_GECOS,
420 "homedir": home,
421 "shell": "/bin/bash",
422 }
423 )
Mike Frysinger31fdddd2023-02-24 15:50:55 -0500424 with open(passwd_db, "a", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -0600425 if data[-1] != "\n":
426 f.write("\n")
427 f.write("%s\n" % acct)
428
429 def _FindRemoteProcess(self, device):
430 """Find a named process (or a pid) running on a remote device."""
431 if not self.remote_process_name and not self.pid:
432 return
433
434 if self.remote_process_name:
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700435 # Look for a process with the specified name on the remote device; if
436 # found, get its pid. Strip off the lacros- or ash- part.
437 pname = self.remote_process_name.lstrip("lacros-").lstrip("ash-")
Alex Klein1699fab2022-09-08 08:46:06 -0600438 if pname == "browser":
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700439 all_chrome_pids = set(device.GetRunningPids(self.inf_cmd))
440 non_main_chrome_pids = set(
441 device.GetRunningPids("%s.+type=" % self.inf_cmd)
Alex Klein1699fab2022-09-08 08:46:06 -0600442 )
Alex Klein1699fab2022-09-08 08:46:06 -0600443 pids = list(all_chrome_pids - non_main_chrome_pids)
Alex Klein64930532023-04-17 12:20:52 -0600444 elif pname in ("renderer", "gpu-process"):
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700445 pids = device.GetRunningPids(
446 "%s.+type=%s" % (self.inf_cmd, pname)
447 )
Alex Klein1699fab2022-09-08 08:46:06 -0600448 else:
449 pids = device.GetRunningPids(pname)
450
451 if pids:
452 if len(pids) == 1:
453 self.pid = pids[0]
454 else:
455 raise GdbTooManyPidsError(
456 "Multiple pids found for %s process: %s. "
457 "You must specify the correct pid."
458 % (pname, repr(pids))
459 )
460 else:
461 raise GdbCannotFindRemoteProcessError(
462 'Cannot find pid for "%s" on %s' % (pname, self.remote)
463 )
464
465 # Find full path for process, from pid (and verify pid).
466 command = [
467 "readlink",
468 "-e",
469 "/proc/%s/exe" % self.pid,
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700470 ]
Alex Klein1699fab2022-09-08 08:46:06 -0600471 try:
472 res = device.run(command, capture_output=True)
473 if res.returncode == 0:
474 self.inf_cmd = res.stdout.rstrip("\n")
475 except cros_build_lib.RunCommandError:
476 raise GdbCannotFindRemoteProcessError(
477 "Unable to find name of process "
478 "with pid %s on %s" % (self.pid, self.remote)
479 )
Raul E Rangel746c45d2018-05-09 09:27:31 -0600480
Alex Klein1699fab2022-09-08 08:46:06 -0600481 def GetCrossGdb(self):
482 """Find the appropriate cross-version of gdb for the board."""
483 toolchains = toolchain.GetToolchainsForBoard(self.board)
484 tc = list(toolchain.FilterToolchains(toolchains, "default", True))
485 cross_gdb = tc[0] + "-gdb"
486 if not osutils.Which(cross_gdb):
487 raise GdbMissingDebuggerError(
488 "Cannot find %s; do you need to run " "setup_board?" % cross_gdb
489 )
490 return cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700491
Alex Klein1699fab2022-09-08 08:46:06 -0600492 def GetGdbInitCommands(self, inferior_cmd, device=None):
Alex Klein8b444532023-04-11 16:35:24 -0600493 """Generate list of commands with which to initialize a gdb session."""
Alex Klein1699fab2022-09-08 08:46:06 -0600494 gdb_init_commands = []
cmticef23cb132015-04-10 15:13:00 -0700495
Alex Klein1699fab2022-09-08 08:46:06 -0600496 if self.remote:
497 sysroot_var = self.sysroot
498 else:
499 sysroot_var = "/"
cmticef23cb132015-04-10 15:13:00 -0700500
Alex Klein1699fab2022-09-08 08:46:06 -0600501 gdb_init_commands = [
502 "set sysroot %s" % sysroot_var,
503 "set prompt %s" % self.prompt,
504 ]
505 if self.in_chroot:
506 gdb_init_commands += [
507 "set solib-absolute-prefix %s" % sysroot_var,
508 "set solib-search-path %s" % sysroot_var,
509 "set debug-file-directory %s/usr/lib/debug" % sysroot_var,
510 ]
cmticef23cb132015-04-10 15:13:00 -0700511
Alex Klein1699fab2022-09-08 08:46:06 -0600512 if device:
Mike Frysingerc0780a62022-08-29 04:41:56 -0400513 ssh_cmd = device.agent.GetSSHCommand(self.ssh_settings)
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600514
Alex Klein1699fab2022-09-08 08:46:06 -0600515 ssh_cmd.extend(["--", "gdbserver"])
cmticef23cb132015-04-10 15:13:00 -0700516
Alex Klein1699fab2022-09-08 08:46:06 -0600517 if self.pid:
518 ssh_cmd.extend(["--attach", "stdio", str(self.pid)])
519 target_type = "remote"
520 elif inferior_cmd:
521 ssh_cmd.extend(["-", inferior_cmd])
522 ssh_cmd.extend(self.inf_args)
523 target_type = "remote"
524 else:
525 ssh_cmd.extend(["--multi", "stdio"])
526 target_type = "extended-remote"
cmticef23cb132015-04-10 15:13:00 -0700527
Alex Klein1699fab2022-09-08 08:46:06 -0600528 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 if self.in_chroot:
531 if inferior_cmd:
532 gdb_init_commands.append(
533 "file %s"
534 % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep))
535 )
536 else:
537 binary = self.GetSimpleChromeBinary()
538 gdb_init_commands += [
539 "set debug-file-directory %s" % os.path.dirname(binary),
540 "file %s" % binary,
541 ]
cmtice932e0aa2015-02-27 11:49:12 -0800542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 gdb_init_commands.append("target %s | %s" % (target_type, ssh_cmd))
544 else:
545 if inferior_cmd:
546 gdb_init_commands.append("file %s " % inferior_cmd)
547 gdb_init_commands.append(
548 "set args %s" % " ".join(self.inf_args)
549 )
cmticeb70801a2014-12-11 14:29:34 -0800550
Alex Klein1699fab2022-09-08 08:46:06 -0600551 return gdb_init_commands
cmticef23cb132015-04-10 15:13:00 -0700552
Alex Klein1699fab2022-09-08 08:46:06 -0600553 def RunRemote(self):
554 """Handle remote debugging, via gdbserver & cross debugger."""
555 with remote_access.ChromiumOSDeviceHandler(
556 self.remote,
557 port=self.remote_port,
558 connect_settings=self.ssh_settings,
559 ping=self.ping,
560 ) as device:
Alex Klein1699fab2022-09-08 08:46:06 -0600561 self.VerifyAndFinishInitialization(device)
562 gdb_cmd = self.cross_gdb
cmticeb70801a2014-12-11 14:29:34 -0800563
Alex Klein1699fab2022-09-08 08:46:06 -0600564 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
565 gdb_args = ["--quiet"] + [
566 "--eval-command=%s" % x for x in gdb_commands
567 ]
568 gdb_args += self.gdb_args
cmticeb70801a2014-12-11 14:29:34 -0800569
Alex Klein1699fab2022-09-08 08:46:06 -0600570 if self.cgdb:
571 gdb_args = ["-d", gdb_cmd, "--"] + gdb_args
572 gdb_cmd = "cgdb"
cmtice932e0aa2015-02-27 11:49:12 -0800573
Alex Klein1699fab2022-09-08 08:46:06 -0600574 cros_build_lib.run(
575 [gdb_cmd] + gdb_args,
576 ignore_sigint=True,
577 print_cmd=True,
578 cwd=self.sysroot,
579 )
580
581 def Run(self):
582 """Runs the debugger in a proper environment (e.g. qemu)."""
583
584 self.VerifyAndFinishInitialization(None)
585 self.SetupUser()
586 if self.framework == "qemu":
587 self.qemu.Install(self.sysroot)
588 self.qemu.RegisterBinfmt()
589
590 for mount in self._BIND_MOUNT_PATHS:
591 path = os.path.join(self.sysroot, mount)
592 osutils.SafeMakedirs(path)
593 osutils.Mount("/" + mount, path, "none", osutils.MS_BIND)
594
595 gdb_cmd = self._GDB
596 inferior_cmd = self.inf_cmd
597
598 gdb_argv = self.gdb_args[:]
599 if gdb_argv:
600 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
601 # Some programs expect to find data files via $CWD, so doing a chroot
602 # and dropping them into / would make them fail.
603 cwd = self.RemoveSysrootPrefix(os.getcwd())
604
605 os.chroot(self.sysroot)
606 os.chdir(cwd)
607 # The TERM the user is leveraging might not exist in the sysroot.
608 # Force a reasonable default that supports standard color sequences.
609 os.environ["TERM"] = "ansi"
610 # Some progs want this like bash else they get super confused.
611 os.environ["PWD"] = cwd
612 if not self.run_as_root:
613 _, uid, gid, home = self.GetNonRootAccount()
614 os.setgid(gid)
615 os.setuid(uid)
616 os.environ["HOME"] = home
617
618 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
619
620 gdb_args = [gdb_cmd, "--quiet"] + [
621 "--eval-command=%s" % x for x in gdb_commands
622 ]
623 gdb_args += self.gdb_args
624
625 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800626
627
cmtice932e0aa2015-02-27 11:49:12 -0800628def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600629 """Re-execute gdb as root.
cmticeb70801a2014-12-11 14:29:34 -0800630
Alex Klein1699fab2022-09-08 08:46:06 -0600631 We often need to do things as root, so make sure we're that. Like chroot
632 for proper library environment or do bind mounts.
cmticeb70801a2014-12-11 14:29:34 -0800633
Alex Klein1699fab2022-09-08 08:46:06 -0600634 Also unshare the mount namespace so as to ensure that doing bind mounts for
635 tests don't leak out to the normal chroot. Also unshare the UTS namespace
636 so changes to `hostname` do not impact the host.
637 """
638 if osutils.IsNonRootUser():
639 cmd = ["sudo", "-E", "--"] + argv
640 os.execvp(cmd[0], cmd)
641 else:
642 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
cmtice932e0aa2015-02-27 11:49:12 -0800643
644
cmticef23cb132015-04-10 15:13:00 -0700645def FindInferior(arg_list):
Alex Klein1699fab2022-09-08 08:46:06 -0600646 """Look for the name of the inferior (to be debugged) in arg list."""
cmtice932e0aa2015-02-27 11:49:12 -0800647
Alex Klein1699fab2022-09-08 08:46:06 -0600648 program_name = ""
649 new_list = []
650 for item in arg_list:
651 if item[0] == "-":
652 new_list.append(item)
653 elif not program_name:
654 program_name = item
655 else:
656 raise RuntimeError(
657 "Found multiple program names: %s %s" % (program_name, item)
658 )
cmtice932e0aa2015-02-27 11:49:12 -0800659
Alex Klein1699fab2022-09-08 08:46:06 -0600660 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800661
662
663def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600664 parser = commandline.ArgumentParser(description=__doc__)
cmticeb70801a2014-12-11 14:29:34 -0800665
Alex Klein1699fab2022-09-08 08:46:06 -0600666 parser.add_argument("--board", default=None, help="board to debug for")
667 parser.add_argument(
Marc Grimme5441aba2023-04-14 16:56:36 +0200668 "--gdb",
669 type="file_exists",
670 help="Path to gdb binary. Default is that gdb binary is auto detected.",
671 )
672 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600673 "-g",
Mike Frysinger3152f6b2023-04-26 23:47:48 -0400674 "--gdb-args",
Alex Klein1699fab2022-09-08 08:46:06 -0600675 action="append",
676 default=[],
677 help="Arguments to gdb itself. If multiple arguments are"
678 " passed, each argument needs a separate '-g' flag.",
679 )
Mike Frysinger3152f6b2023-04-26 23:47:48 -0400680 # TODO(build): Delete by Jan 2024.
681 parser.add_argument(
682 "--gdb_args",
683 action="append",
684 deprecated="Use --gdb-args instead",
685 help=argparse.SUPPRESS,
686 )
Alex Klein1699fab2022-09-08 08:46:06 -0600687 parser.add_argument(
688 "--remote",
689 default=None,
690 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
691 help="Remote device on which to run the binary. Use"
692 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
693 " already running local virtual machine.",
694 )
695 parser.add_argument(
696 "--pid",
697 default="",
698 help="Process ID of the (already) running process on the"
699 " remote device to which to attach.",
700 )
Mike Frysinger5c702782023-04-17 16:20:24 -0400701 # TODO(build): Delete in Jan 2024.
Alex Klein1699fab2022-09-08 08:46:06 -0600702 parser.add_argument(
703 "--remote_pid",
704 dest="pid",
Mike Frysinger5c702782023-04-17 16:20:24 -0400705 help=argparse.SUPPRESS,
706 deprecated="Use --pid instead.",
Alex Klein1699fab2022-09-08 08:46:06 -0600707 )
708 parser.add_argument(
709 "--no-ping",
710 dest="ping",
711 default=True,
712 action="store_false",
713 help="Do not ping remote before attempting to connect.",
714 )
715 parser.add_argument(
716 "--attach",
717 dest="attach_name",
718 default="",
719 help="Name of existing process to which to attach, on"
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700720 " remote device (remote debugging only)."
721 'Options are [browser, renderer, gpu-process] and can be prefixed with either "ash-" or "lacros-".',
Alex Klein1699fab2022-09-08 08:46:06 -0600722 )
723 parser.add_argument(
724 "--cgdb",
725 default=False,
726 action="store_true",
727 help="Use cgdb curses interface rather than plain gdb."
728 "This option is only valid for remote debugging.",
729 )
730 parser.add_argument(
731 "inf_args",
732 nargs=argparse.REMAINDER,
733 help="Arguments for gdb to pass to the program being"
734 " debugged. These are positional and must come at the end"
735 " of the command line. This will not work if attaching"
736 " to an already running program.",
737 )
738 parser.add_argument(
739 "--binary",
740 default="",
Brian Norriscc3331c2022-04-22 13:52:00 -0700741 help="full path to the binary being debugged."
Alex Klein1699fab2022-09-08 08:46:06 -0600742 " This is only useful for simple chrome."
Brian Norriscc3331c2022-04-22 13:52:00 -0700743 " An example is --binary /home/out_falco/chrome.",
Alex Klein1699fab2022-09-08 08:46:06 -0600744 )
cmticeb70801a2014-12-11 14:29:34 -0800745
Alex Klein1699fab2022-09-08 08:46:06 -0600746 options = parser.parse_args(argv)
747 options.Freeze()
cmticeb70801a2014-12-11 14:29:34 -0800748
Alex Klein1699fab2022-09-08 08:46:06 -0600749 gdb_args = []
750 inf_args = []
751 inf_cmd = ""
cmtice932e0aa2015-02-27 11:49:12 -0800752
Alex Klein1699fab2022-09-08 08:46:06 -0600753 if options.inf_args:
754 inf_cmd = options.inf_args[0]
755 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800756
Alex Klein1699fab2022-09-08 08:46:06 -0600757 if options.gdb_args:
758 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800759
Alex Klein1699fab2022-09-08 08:46:06 -0600760 if inf_cmd:
761 fname = os.path.join(
762 build_target_lib.get_default_sysroot_path(options.board),
763 inf_cmd.lstrip("/"),
764 )
765 if not os.path.exists(fname):
Alex Kleindf8ee502022-10-18 09:48:15 -0600766 cros_build_lib.Die("Cannot find program %s.", fname)
Alex Klein1699fab2022-09-08 08:46:06 -0600767 else:
768 if inf_args:
769 parser.error("Cannot specify arguments without a program.")
cmtice932e0aa2015-02-27 11:49:12 -0800770
Alex Klein1699fab2022-09-08 08:46:06 -0600771 if inf_args and (options.pid or options.attach_name):
772 parser.error(
773 "Cannot pass arguments to an already"
774 " running process (--remote-pid or --attach)."
775 )
cmticef23cb132015-04-10 15:13:00 -0700776
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700777 if not options.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600778 if options.cgdb:
779 parser.error(
780 "--cgdb option can only be used with remote debugging."
781 )
782 if options.pid:
783 parser.error(
784 "Must specify a remote device (--remote) if you want "
785 "to attach to a remote pid."
786 )
787 if options.attach_name:
788 parser.error(
789 "Must specify remote device (--remote) when using"
790 " --attach option."
791 )
792 if options.binary:
793 if not os.path.exists(options.binary):
794 parser.error("%s does not exist." % options.binary)
cmticef23cb132015-04-10 15:13:00 -0700795
Alex Klein1699fab2022-09-08 08:46:06 -0600796 # Once we've finished checking args, make sure we're root.
797 if not options.remote:
798 _ReExecuteIfNeeded([sys.argv[0]] + argv)
799
800 gdb = BoardSpecificGdb(
801 options.board,
802 gdb_args,
803 inf_cmd,
804 inf_args,
805 options.remote,
806 options.pid,
807 options.attach_name,
808 options.cgdb,
809 options.ping,
810 options.binary,
Marc Grimme5441aba2023-04-14 16:56:36 +0200811 options.gdb,
Alex Klein1699fab2022-09-08 08:46:06 -0600812 )
813
814 try:
815 if options.remote:
816 gdb.RunRemote()
817 else:
818 gdb.Run()
819
820 except GdbException as e:
821 if options.debug:
822 raise
823 else:
Trent Apted593c0742023-05-05 03:50:20 +0000824 # TODO(b/236161656): Fix.
825 # pylint: disable-next=raising-bad-type
Alex Klein1699fab2022-09-08 08:46:06 -0600826 raise cros_build_lib.Die(str(e))