blob: 97e348621f97c86f780055655c17cfdfbd85e216 [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):
Trent Apted380d1c62023-05-16 09:19:31 +1000155 """Whether the Lacros chrome binary is in use.
156
157 The --attach option specifies the type of if you want to attach to
158 browser, renderer or gpu-process. Prefixed with either lacros- or ash-.
159 You can specify which browser you want to attach to. Default is ash.
160 """
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700161 return self.inf_cmd == self._LACROS_CHROME_REMOTE_BIN
162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 def SimpleChromeGdb(self):
164 """Get the name of the cross gdb based on board name."""
165 bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
166 cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
167 self.sdk_path,
168 self.board,
169 )
170 bin_path = os.path.join(bin_path, "bin")
171 for f in os.listdir(bin_path):
172 if f.endswith("gdb"):
173 return os.path.join(bin_path, f)
174 raise GdbMissingDebuggerError(
175 "Cannot find cross gdb for %s." % self.board
176 )
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700177
Alex Klein1699fab2022-09-08 08:46:06 -0600178 def SimpleChromeSysroot(self):
179 """Get the sysroot in simple chrome."""
180 sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
181 constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board
182 )
183 if not sysroot:
184 raise GdbMissingSysrootError(
185 "Cannot find sysroot for %s at %s" % (self.board, self.sdk_path)
186 )
187 return sysroot
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 def GetSimpleChromeBinary(self):
190 """Get path to the binary in simple chrome."""
191 if self.binary:
192 return self.binary
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700193
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700194 output_dir = (
195 os.path.join(self.chrome_path, "src", f"out_{self.board}_lacros")
196 if self.IsLacros()
197 else os.path.join(self.chrome_path, "src", f"out_{self.board}")
198 )
Alex Klein1699fab2022-09-08 08:46:06 -0600199 target_binary = None
200 binary_name = os.path.basename(self.inf_cmd)
201 for root, _, files in os.walk(output_dir):
202 for f in files:
203 if f == binary_name:
204 if target_binary is None:
205 target_binary = os.path.join(root, f)
206 else:
207 raise GdbSimpleChromeBinaryError(
Alex Klein8b444532023-04-11 16:35:24 -0600208 "There are multiple %s under %s. Please specify "
209 "the path to the binary via --binary"
Alex Klein1699fab2022-09-08 08:46:06 -0600210 % (binary_name, output_dir)
211 )
212 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700213 raise GdbSimpleChromeBinaryError(
Alex Klein1699fab2022-09-08 08:46:06 -0600214 "There is no %s under %s." % (binary_name, output_dir)
215 )
216 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800217
Alex Klein1699fab2022-09-08 08:46:06 -0600218 def VerifyAndFinishInitialization(self, device):
219 """Verify files/processes exist and flags are correct."""
220 if not self.board:
221 if self.remote:
222 self.board = cros_build_lib.GetBoard(
223 device_board=device.board,
224 override_board=self.board,
225 strict=True,
226 )
227 else:
228 raise GdbCannotDetectBoardError(
229 "Cannot determine which board to use. "
230 "Please specify the with --board flag."
231 )
232 self.in_chroot = self.IsInChroot()
233 self.prompt = "(%s-gdb) " % self.board
234 if self.in_chroot:
235 self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
236 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
Marc Grimme5441aba2023-04-14 16:56:36 +0200237 if not self.cross_gdb:
238 self.cross_gdb = self.GetCrossGdb()
Alex Klein1699fab2022-09-08 08:46:06 -0600239 else:
240 self.chrome_path = os.path.realpath(
241 os.path.join(
242 os.path.dirname(os.path.realpath(__file__)), "../../../.."
243 )
244 )
245 self.sdk_path = path_util.FindCacheDir()
246 self.sysroot = self.SimpleChromeSysroot()
Marc Grimme5441aba2023-04-14 16:56:36 +0200247 if not self.cross_gdb:
248 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700249
Alex Klein1699fab2022-09-08 08:46:06 -0600250 if self.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600251 # If given remote process name, find pid & inf_cmd on remote device.
252 if self.remote_process_name or self.pid:
253 self._FindRemoteProcess(device)
cmticef23cb132015-04-10 15:13:00 -0700254
Alex Klein1699fab2022-09-08 08:46:06 -0600255 # Verify that sysroot is valid (exists).
256 if not os.path.isdir(self.sysroot):
257 raise GdbMissingSysrootError(
258 "Sysroot does not exist: %s" % self.sysroot
259 )
cmticef23cb132015-04-10 15:13:00 -0700260
Alex Klein1699fab2022-09-08 08:46:06 -0600261 self.device = device
262 if not self.in_chroot:
cmticeb70801a2014-12-11 14:29:34 -0800263 return
264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 sysroot_inf_cmd = ""
266 if self.inf_cmd:
267 sysroot_inf_cmd = os.path.join(
268 self.sysroot, self.inf_cmd.lstrip("/")
269 )
cmticeb70801a2014-12-11 14:29:34 -0800270
Alex Klein1699fab2022-09-08 08:46:06 -0600271 # Verify that inf_cmd, if given, exists.
272 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
273 raise GdbMissingInferiorError(
274 "Cannot find file %s (in sysroot)." % sysroot_inf_cmd
275 )
cmticeb70801a2014-12-11 14:29:34 -0800276
Alex Klein8b444532023-04-11 16:35:24 -0600277 # Check to see if inf_cmd is stripped, and if so, check to see if debug
278 # file exists. If not, tell user and give them the option of quitting &
279 # getting the debug info.
Alex Klein1699fab2022-09-08 08:46:06 -0600280 if sysroot_inf_cmd:
281 stripped_info = cros_build_lib.run(
282 ["file", sysroot_inf_cmd], capture_output=True, encoding="utf-8"
283 ).stdout
284 if " not stripped" not in stripped_info:
285 debug_file = os.path.join(
286 self.sysroot, "usr/lib/debug", self.inf_cmd.lstrip("/")
287 )
288 debug_file += ".debug"
289 if not os.path.exists(debug_file):
290 equery = "equery-%s" % self.board
291 package = cros_build_lib.run(
292 [equery, "-q", "b", self.inf_cmd],
293 capture_output=True,
294 encoding="utf-8",
295 ).stdout
296 # pylint: disable=logging-not-lazy
297 logging.info(
298 self._MISSING_DEBUG_INFO_MSG
299 % {
300 "board": self.board,
301 "inf_cmd": self.inf_cmd,
302 "package": package,
303 "debug_file": debug_file,
304 }
305 )
306 answer = cros_build_lib.BooleanPrompt()
307 if not answer:
308 raise GdbEarlyExitError(
309 "Exiting early, at user request."
310 )
cmticeb70801a2014-12-11 14:29:34 -0800311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 # Set up qemu, if appropriate.
313 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
314 if qemu_arch is None:
315 self.framework = "ldso"
cmticef23cb132015-04-10 15:13:00 -0700316 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600317 self.framework = "qemu"
318 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticef23cb132015-04-10 15:13:00 -0700319
Alex Klein1699fab2022-09-08 08:46:06 -0600320 if self.remote:
321 # Verify cgdb flag info.
322 if self.cgdb:
323 if osutils.Which("cgdb") is None:
324 raise GdbMissingDebuggerError(
325 "Cannot find cgdb. Please install " "cgdb first."
326 )
cmticef23cb132015-04-10 15:13:00 -0700327
Alex Klein1699fab2022-09-08 08:46:06 -0600328 def RemoveSysrootPrefix(self, path):
329 """Returns the given path with any sysroot prefix removed."""
330 # If the sysroot is /, then the paths are already normalized.
331 if self.sysroot != "/" and path.startswith(self.sysroot):
332 path = path.replace(self.sysroot, "", 1)
333 return path
cmticef23cb132015-04-10 15:13:00 -0700334
Alex Klein1699fab2022-09-08 08:46:06 -0600335 @staticmethod
336 def GetNonRootAccount():
337 """Return details about the non-root account we want to use.
cmticef23cb132015-04-10 15:13:00 -0700338
Alex Klein1699fab2022-09-08 08:46:06 -0600339 Returns:
340 A tuple of (username, uid, gid, home).
341 """
342 return (
343 os.environ.get("SUDO_USER", "nobody"),
344 int(os.environ.get("SUDO_UID", "65534")),
345 int(os.environ.get("SUDO_GID", "65534")),
346 # Should we find a better home?
347 "/tmp/portage",
348 )
cmticef23cb132015-04-10 15:13:00 -0700349
Alex Klein1699fab2022-09-08 08:46:06 -0600350 @staticmethod
351 @contextlib.contextmanager
352 def LockDb(db):
353 """Lock an account database.
cmticef23cb132015-04-10 15:13:00 -0700354
Alex Klein1699fab2022-09-08 08:46:06 -0600355 We use the same algorithm as shadow/user.eclass. This way we don't race
356 and corrupt things in parallel.
357 """
358 lock = "%s.lock" % db
359 _, tmplock = tempfile.mkstemp(prefix="%s.platform." % lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600360
Alex Klein1699fab2022-09-08 08:46:06 -0600361 # First try forever to grab the lock.
362 retry = lambda e: e.errno == errno.EEXIST
363 # Retry quickly at first, but slow down over time.
364 try:
365 retry_util.GenericRetry(
366 retry, 60, os.link, tmplock, lock, sleep=0.1
367 )
368 except Exception as e:
369 raise Exception("Could not grab lock %s. %s" % (lock, e))
Raul E Rangel746c45d2018-05-09 09:27:31 -0600370
Alex Klein1699fab2022-09-08 08:46:06 -0600371 # Yield while holding the lock, but try to clean it no matter what.
372 try:
373 os.unlink(tmplock)
374 yield lock
375 finally:
376 os.unlink(lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600377
Alex Klein1699fab2022-09-08 08:46:06 -0600378 def SetupUser(self):
379 """Propogate the user name<->id mapping from outside the chroot.
cmticef23cb132015-04-10 15:13:00 -0700380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 Some unittests use getpwnam($USER), as does bash. If the account
382 is not registered in the sysroot, they get back errors.
383 """
384 MAGIC_GECOS = (
385 "Added by your friendly platform test helper; do not modify"
386 )
387 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
388 SDK_GECOS = "ChromeOS Developer"
389
390 user, uid, gid, home = self.GetNonRootAccount()
391 if user == "nobody":
392 return
393
394 passwd_db = os.path.join(self.sysroot, "etc", "passwd")
395 with self.LockDb(passwd_db):
396 data = osutils.ReadFile(passwd_db)
397 accts = data.splitlines()
398 for acct in accts:
399 passwd = acct.split(":")
400 if passwd[0] == user:
401 # Did the sdk make this account?
402 if passwd[4] == SDK_GECOS:
403 # Don't modify it (see below) since we didn't create it.
404 return
405
406 # Did we make this account?
407 if passwd[4] != MAGIC_GECOS:
408 raise RuntimeError(
409 "your passwd db (%s) has unmanaged acct %s"
410 % (passwd_db, user)
411 )
412
Alex Klein8b444532023-04-11 16:35:24 -0600413 # Maybe we should see if it needs to be updated? Like if
414 # they changed UIDs? But we don't really check that
415 # elsewhere ...
Alex Klein1699fab2022-09-08 08:46:06 -0600416 return
417
418 acct = (
419 "%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s"
420 % {
421 "name": user,
422 "uid": uid,
423 "gid": gid,
424 "gecos": MAGIC_GECOS,
425 "homedir": home,
426 "shell": "/bin/bash",
427 }
428 )
Mike Frysinger31fdddd2023-02-24 15:50:55 -0500429 with open(passwd_db, "a", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -0600430 if data[-1] != "\n":
431 f.write("\n")
432 f.write("%s\n" % acct)
433
434 def _FindRemoteProcess(self, device):
435 """Find a named process (or a pid) running on a remote device."""
436 if not self.remote_process_name and not self.pid:
437 return
438
439 if self.remote_process_name:
Trent Apted380d1c62023-05-16 09:19:31 +1000440 # Look for a process with the specified name on the remote device;
441 # if found, get its pid. Strip off the lacros- or ash- part.
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700442 pname = self.remote_process_name.lstrip("lacros-").lstrip("ash-")
Alex Klein1699fab2022-09-08 08:46:06 -0600443 if pname == "browser":
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700444 all_chrome_pids = set(device.GetRunningPids(self.inf_cmd))
445 non_main_chrome_pids = set(
446 device.GetRunningPids("%s.+type=" % self.inf_cmd)
Alex Klein1699fab2022-09-08 08:46:06 -0600447 )
Alex Klein1699fab2022-09-08 08:46:06 -0600448 pids = list(all_chrome_pids - non_main_chrome_pids)
Alex Klein64930532023-04-17 12:20:52 -0600449 elif pname in ("renderer", "gpu-process"):
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700450 pids = device.GetRunningPids(
451 "%s.+type=%s" % (self.inf_cmd, pname)
452 )
Alex Klein1699fab2022-09-08 08:46:06 -0600453 else:
454 pids = device.GetRunningPids(pname)
455
456 if pids:
457 if len(pids) == 1:
458 self.pid = pids[0]
459 else:
460 raise GdbTooManyPidsError(
461 "Multiple pids found for %s process: %s. "
462 "You must specify the correct pid."
463 % (pname, repr(pids))
464 )
465 else:
466 raise GdbCannotFindRemoteProcessError(
467 'Cannot find pid for "%s" on %s' % (pname, self.remote)
468 )
469
470 # Find full path for process, from pid (and verify pid).
471 command = [
472 "readlink",
473 "-e",
474 "/proc/%s/exe" % self.pid,
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700475 ]
Alex Klein1699fab2022-09-08 08:46:06 -0600476 try:
477 res = device.run(command, capture_output=True)
478 if res.returncode == 0:
479 self.inf_cmd = res.stdout.rstrip("\n")
480 except cros_build_lib.RunCommandError:
481 raise GdbCannotFindRemoteProcessError(
482 "Unable to find name of process "
483 "with pid %s on %s" % (self.pid, self.remote)
484 )
Raul E Rangel746c45d2018-05-09 09:27:31 -0600485
Alex Klein1699fab2022-09-08 08:46:06 -0600486 def GetCrossGdb(self):
487 """Find the appropriate cross-version of gdb for the board."""
488 toolchains = toolchain.GetToolchainsForBoard(self.board)
489 tc = list(toolchain.FilterToolchains(toolchains, "default", True))
490 cross_gdb = tc[0] + "-gdb"
491 if not osutils.Which(cross_gdb):
492 raise GdbMissingDebuggerError(
493 "Cannot find %s; do you need to run " "setup_board?" % cross_gdb
494 )
495 return cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700496
Alex Klein1699fab2022-09-08 08:46:06 -0600497 def GetGdbInitCommands(self, inferior_cmd, device=None):
Alex Klein8b444532023-04-11 16:35:24 -0600498 """Generate list of commands with which to initialize a gdb session."""
Alex Klein1699fab2022-09-08 08:46:06 -0600499 gdb_init_commands = []
cmticef23cb132015-04-10 15:13:00 -0700500
Alex Klein1699fab2022-09-08 08:46:06 -0600501 if self.remote:
502 sysroot_var = self.sysroot
503 else:
504 sysroot_var = "/"
cmticef23cb132015-04-10 15:13:00 -0700505
Alex Klein1699fab2022-09-08 08:46:06 -0600506 gdb_init_commands = [
507 "set sysroot %s" % sysroot_var,
508 "set prompt %s" % self.prompt,
509 ]
510 if self.in_chroot:
511 gdb_init_commands += [
512 "set solib-absolute-prefix %s" % sysroot_var,
513 "set solib-search-path %s" % sysroot_var,
514 "set debug-file-directory %s/usr/lib/debug" % sysroot_var,
515 ]
cmticef23cb132015-04-10 15:13:00 -0700516
Alex Klein1699fab2022-09-08 08:46:06 -0600517 if device:
Mike Frysingerc0780a62022-08-29 04:41:56 -0400518 ssh_cmd = device.agent.GetSSHCommand(self.ssh_settings)
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600519
Alex Klein1699fab2022-09-08 08:46:06 -0600520 ssh_cmd.extend(["--", "gdbserver"])
cmticef23cb132015-04-10 15:13:00 -0700521
Alex Klein1699fab2022-09-08 08:46:06 -0600522 if self.pid:
523 ssh_cmd.extend(["--attach", "stdio", str(self.pid)])
524 target_type = "remote"
525 elif inferior_cmd:
526 ssh_cmd.extend(["-", inferior_cmd])
527 ssh_cmd.extend(self.inf_args)
528 target_type = "remote"
529 else:
530 ssh_cmd.extend(["--multi", "stdio"])
531 target_type = "extended-remote"
cmticef23cb132015-04-10 15:13:00 -0700532
Alex Klein1699fab2022-09-08 08:46:06 -0600533 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700534
Alex Klein1699fab2022-09-08 08:46:06 -0600535 if self.in_chroot:
536 if inferior_cmd:
537 gdb_init_commands.append(
538 "file %s"
539 % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep))
540 )
541 else:
542 binary = self.GetSimpleChromeBinary()
543 gdb_init_commands += [
544 "set debug-file-directory %s" % os.path.dirname(binary),
545 "file %s" % binary,
546 ]
cmtice932e0aa2015-02-27 11:49:12 -0800547
Alex Klein1699fab2022-09-08 08:46:06 -0600548 gdb_init_commands.append("target %s | %s" % (target_type, ssh_cmd))
549 else:
550 if inferior_cmd:
551 gdb_init_commands.append("file %s " % inferior_cmd)
552 gdb_init_commands.append(
553 "set args %s" % " ".join(self.inf_args)
554 )
cmticeb70801a2014-12-11 14:29:34 -0800555
Alex Klein1699fab2022-09-08 08:46:06 -0600556 return gdb_init_commands
cmticef23cb132015-04-10 15:13:00 -0700557
Alex Klein1699fab2022-09-08 08:46:06 -0600558 def RunRemote(self):
559 """Handle remote debugging, via gdbserver & cross debugger."""
560 with remote_access.ChromiumOSDeviceHandler(
561 self.remote,
562 port=self.remote_port,
563 connect_settings=self.ssh_settings,
564 ping=self.ping,
565 ) as device:
Alex Klein1699fab2022-09-08 08:46:06 -0600566 self.VerifyAndFinishInitialization(device)
567 gdb_cmd = self.cross_gdb
cmticeb70801a2014-12-11 14:29:34 -0800568
Alex Klein1699fab2022-09-08 08:46:06 -0600569 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
570 gdb_args = ["--quiet"] + [
571 "--eval-command=%s" % x for x in gdb_commands
572 ]
573 gdb_args += self.gdb_args
cmticeb70801a2014-12-11 14:29:34 -0800574
Alex Klein1699fab2022-09-08 08:46:06 -0600575 if self.cgdb:
576 gdb_args = ["-d", gdb_cmd, "--"] + gdb_args
577 gdb_cmd = "cgdb"
cmtice932e0aa2015-02-27 11:49:12 -0800578
Alex Klein1699fab2022-09-08 08:46:06 -0600579 cros_build_lib.run(
580 [gdb_cmd] + gdb_args,
581 ignore_sigint=True,
582 print_cmd=True,
583 cwd=self.sysroot,
584 )
585
586 def Run(self):
587 """Runs the debugger in a proper environment (e.g. qemu)."""
588
589 self.VerifyAndFinishInitialization(None)
590 self.SetupUser()
591 if self.framework == "qemu":
592 self.qemu.Install(self.sysroot)
593 self.qemu.RegisterBinfmt()
594
595 for mount in self._BIND_MOUNT_PATHS:
596 path = os.path.join(self.sysroot, mount)
597 osutils.SafeMakedirs(path)
598 osutils.Mount("/" + mount, path, "none", osutils.MS_BIND)
599
600 gdb_cmd = self._GDB
601 inferior_cmd = self.inf_cmd
602
603 gdb_argv = self.gdb_args[:]
604 if gdb_argv:
605 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
606 # Some programs expect to find data files via $CWD, so doing a chroot
607 # and dropping them into / would make them fail.
608 cwd = self.RemoveSysrootPrefix(os.getcwd())
609
610 os.chroot(self.sysroot)
611 os.chdir(cwd)
612 # The TERM the user is leveraging might not exist in the sysroot.
613 # Force a reasonable default that supports standard color sequences.
614 os.environ["TERM"] = "ansi"
615 # Some progs want this like bash else they get super confused.
616 os.environ["PWD"] = cwd
617 if not self.run_as_root:
618 _, uid, gid, home = self.GetNonRootAccount()
619 os.setgid(gid)
620 os.setuid(uid)
621 os.environ["HOME"] = home
622
623 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
624
625 gdb_args = [gdb_cmd, "--quiet"] + [
626 "--eval-command=%s" % x for x in gdb_commands
627 ]
628 gdb_args += self.gdb_args
629
630 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800631
632
cmtice932e0aa2015-02-27 11:49:12 -0800633def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600634 """Re-execute gdb as root.
cmticeb70801a2014-12-11 14:29:34 -0800635
Alex Klein1699fab2022-09-08 08:46:06 -0600636 We often need to do things as root, so make sure we're that. Like chroot
637 for proper library environment or do bind mounts.
cmticeb70801a2014-12-11 14:29:34 -0800638
Alex Klein1699fab2022-09-08 08:46:06 -0600639 Also unshare the mount namespace so as to ensure that doing bind mounts for
640 tests don't leak out to the normal chroot. Also unshare the UTS namespace
641 so changes to `hostname` do not impact the host.
642 """
643 if osutils.IsNonRootUser():
644 cmd = ["sudo", "-E", "--"] + argv
645 os.execvp(cmd[0], cmd)
646 else:
647 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
cmtice932e0aa2015-02-27 11:49:12 -0800648
649
cmticef23cb132015-04-10 15:13:00 -0700650def FindInferior(arg_list):
Alex Klein1699fab2022-09-08 08:46:06 -0600651 """Look for the name of the inferior (to be debugged) in arg list."""
cmtice932e0aa2015-02-27 11:49:12 -0800652
Alex Klein1699fab2022-09-08 08:46:06 -0600653 program_name = ""
654 new_list = []
655 for item in arg_list:
656 if item[0] == "-":
657 new_list.append(item)
658 elif not program_name:
659 program_name = item
660 else:
661 raise RuntimeError(
662 "Found multiple program names: %s %s" % (program_name, item)
663 )
cmtice932e0aa2015-02-27 11:49:12 -0800664
Alex Klein1699fab2022-09-08 08:46:06 -0600665 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800666
667
668def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600669 parser = commandline.ArgumentParser(description=__doc__)
cmticeb70801a2014-12-11 14:29:34 -0800670
Alex Klein1699fab2022-09-08 08:46:06 -0600671 parser.add_argument("--board", default=None, help="board to debug for")
672 parser.add_argument(
Marc Grimme5441aba2023-04-14 16:56:36 +0200673 "--gdb",
674 type="file_exists",
675 help="Path to gdb binary. Default is that gdb binary is auto detected.",
676 )
677 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600678 "-g",
Mike Frysinger3152f6b2023-04-26 23:47:48 -0400679 "--gdb-args",
Alex Klein1699fab2022-09-08 08:46:06 -0600680 action="append",
681 default=[],
682 help="Arguments to gdb itself. If multiple arguments are"
683 " passed, each argument needs a separate '-g' flag.",
684 )
Mike Frysinger3152f6b2023-04-26 23:47:48 -0400685 # TODO(build): Delete by Jan 2024.
686 parser.add_argument(
687 "--gdb_args",
688 action="append",
689 deprecated="Use --gdb-args instead",
690 help=argparse.SUPPRESS,
691 )
Alex Klein1699fab2022-09-08 08:46:06 -0600692 parser.add_argument(
693 "--remote",
694 default=None,
695 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
696 help="Remote device on which to run the binary. Use"
697 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
698 " already running local virtual machine.",
699 )
700 parser.add_argument(
701 "--pid",
702 default="",
703 help="Process ID of the (already) running process on the"
704 " remote device to which to attach.",
705 )
Mike Frysinger5c702782023-04-17 16:20:24 -0400706 # TODO(build): Delete in Jan 2024.
Alex Klein1699fab2022-09-08 08:46:06 -0600707 parser.add_argument(
708 "--remote_pid",
709 dest="pid",
Mike Frysinger5c702782023-04-17 16:20:24 -0400710 help=argparse.SUPPRESS,
711 deprecated="Use --pid instead.",
Alex Klein1699fab2022-09-08 08:46:06 -0600712 )
713 parser.add_argument(
714 "--no-ping",
715 dest="ping",
716 default=True,
717 action="store_false",
718 help="Do not ping remote before attempting to connect.",
719 )
720 parser.add_argument(
721 "--attach",
722 dest="attach_name",
723 default="",
724 help="Name of existing process to which to attach, on"
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700725 " remote device (remote debugging only)."
Trent Apted380d1c62023-05-16 09:19:31 +1000726 "Options are [browser, renderer, gpu-process] and can be prefixed with"
727 ' either "ash-" or "lacros-".',
Alex Klein1699fab2022-09-08 08:46:06 -0600728 )
729 parser.add_argument(
730 "--cgdb",
731 default=False,
732 action="store_true",
733 help="Use cgdb curses interface rather than plain gdb."
734 "This option is only valid for remote debugging.",
735 )
736 parser.add_argument(
737 "inf_args",
738 nargs=argparse.REMAINDER,
739 help="Arguments for gdb to pass to the program being"
740 " debugged. These are positional and must come at the end"
741 " of the command line. This will not work if attaching"
742 " to an already running program.",
743 )
744 parser.add_argument(
745 "--binary",
746 default="",
Brian Norriscc3331c2022-04-22 13:52:00 -0700747 help="full path to the binary being debugged."
Alex Klein1699fab2022-09-08 08:46:06 -0600748 " This is only useful for simple chrome."
Brian Norriscc3331c2022-04-22 13:52:00 -0700749 " An example is --binary /home/out_falco/chrome.",
Alex Klein1699fab2022-09-08 08:46:06 -0600750 )
cmticeb70801a2014-12-11 14:29:34 -0800751
Alex Klein1699fab2022-09-08 08:46:06 -0600752 options = parser.parse_args(argv)
753 options.Freeze()
cmticeb70801a2014-12-11 14:29:34 -0800754
Alex Klein1699fab2022-09-08 08:46:06 -0600755 gdb_args = []
756 inf_args = []
757 inf_cmd = ""
cmtice932e0aa2015-02-27 11:49:12 -0800758
Alex Klein1699fab2022-09-08 08:46:06 -0600759 if options.inf_args:
760 inf_cmd = options.inf_args[0]
761 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800762
Alex Klein1699fab2022-09-08 08:46:06 -0600763 if options.gdb_args:
764 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800765
Alex Klein1699fab2022-09-08 08:46:06 -0600766 if inf_cmd:
767 fname = os.path.join(
768 build_target_lib.get_default_sysroot_path(options.board),
769 inf_cmd.lstrip("/"),
770 )
771 if not os.path.exists(fname):
Alex Kleindf8ee502022-10-18 09:48:15 -0600772 cros_build_lib.Die("Cannot find program %s.", fname)
Alex Klein1699fab2022-09-08 08:46:06 -0600773 else:
774 if inf_args:
775 parser.error("Cannot specify arguments without a program.")
cmtice932e0aa2015-02-27 11:49:12 -0800776
Alex Klein1699fab2022-09-08 08:46:06 -0600777 if inf_args and (options.pid or options.attach_name):
778 parser.error(
779 "Cannot pass arguments to an already"
780 " running process (--remote-pid or --attach)."
781 )
cmticef23cb132015-04-10 15:13:00 -0700782
chromeos-ci-prodc1e61312023-04-27 19:46:48 -0700783 if not options.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600784 if options.cgdb:
785 parser.error(
786 "--cgdb option can only be used with remote debugging."
787 )
788 if options.pid:
789 parser.error(
790 "Must specify a remote device (--remote) if you want "
791 "to attach to a remote pid."
792 )
793 if options.attach_name:
794 parser.error(
795 "Must specify remote device (--remote) when using"
796 " --attach option."
797 )
798 if options.binary:
799 if not os.path.exists(options.binary):
800 parser.error("%s does not exist." % options.binary)
cmticef23cb132015-04-10 15:13:00 -0700801
Alex Klein1699fab2022-09-08 08:46:06 -0600802 # Once we've finished checking args, make sure we're root.
803 if not options.remote:
804 _ReExecuteIfNeeded([sys.argv[0]] + argv)
805
806 gdb = BoardSpecificGdb(
807 options.board,
808 gdb_args,
809 inf_cmd,
810 inf_args,
811 options.remote,
812 options.pid,
813 options.attach_name,
814 options.cgdb,
815 options.ping,
816 options.binary,
Marc Grimme5441aba2023-04-14 16:56:36 +0200817 options.gdb,
Alex Klein1699fab2022-09-08 08:46:06 -0600818 )
819
820 try:
821 if options.remote:
822 gdb.RunRemote()
823 else:
824 gdb.Run()
825
826 except GdbException as e:
827 if options.debug:
828 raise
829 else:
Trent Apted593c0742023-05-05 03:50:20 +0000830 # TODO(b/236161656): Fix.
831 # pylint: disable-next=raising-bad-type
Alex Klein1699fab2022-09-08 08:46:06 -0600832 raise cros_build_lib.Die(str(e))