blob: a7a647f30be581e2f88198117dffb77751125c7b [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 Klein1699fab2022-09-08 08:46:06 -060070 """Raised when board isn't specified and can't be automatically determined."""
71
cmticeb70801a2014-12-11 14:29:34 -080072
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070073class GdbSimpleChromeBinaryError(GdbException):
Alex Klein1699fab2022-09-08 08:46:06 -060074 """Raised when none or multiple chrome binaries are under out_${board} dir."""
75
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"""
97
Alex Klein1699fab2022-09-08 08:46:06 -060098 def __init__(
99 self,
100 board,
101 gdb_args,
102 inf_cmd,
103 inf_args,
104 remote,
105 pid,
106 remote_process_name,
107 cgdb_flag,
108 ping,
109 binary,
110 ):
111 self.board = board
112 self.sysroot = None
113 self.prompt = "(gdb) "
114 self.inf_cmd = inf_cmd
115 self.run_as_root = False
116 self.gdb_args = gdb_args
117 self.inf_args = inf_args
118 self.remote = remote.hostname if remote else None
119 self.pid = pid
120 self.remote_process_name = remote_process_name
121 # Port used for sending ssh commands to DUT.
122 self.remote_port = remote.port if remote else None
123 # Port for communicating between gdb & gdbserver.
124 self.gdbserver_port = remote_access.GetUnusedPort()
125 self.ssh_settings = remote_access.CompileSSHConnectSettings(
126 **self._EXTRA_SSH_SETTINGS
127 )
128 self.cgdb = cgdb_flag
129 self.framework = "auto"
130 self.qemu = None
131 self.device = None
132 self.cross_gdb = None
133 self.ping = ping
134 self.binary = binary
135 self.in_chroot = None
136 self.chrome_path = None
137 self.sdk_path = None
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 def IsInChroot(self):
140 """Decide whether we are in chroot or chrome-sdk."""
141 return os.path.exists("/mnt/host/source/chromite/")
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700142
Alex Klein1699fab2022-09-08 08:46:06 -0600143 def SimpleChromeGdb(self):
144 """Get the name of the cross gdb based on board name."""
145 bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
146 cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
147 self.sdk_path,
148 self.board,
149 )
150 bin_path = os.path.join(bin_path, "bin")
151 for f in os.listdir(bin_path):
152 if f.endswith("gdb"):
153 return os.path.join(bin_path, f)
154 raise GdbMissingDebuggerError(
155 "Cannot find cross gdb for %s." % self.board
156 )
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700157
Alex Klein1699fab2022-09-08 08:46:06 -0600158 def SimpleChromeSysroot(self):
159 """Get the sysroot in simple chrome."""
160 sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
161 constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board
162 )
163 if not sysroot:
164 raise GdbMissingSysrootError(
165 "Cannot find sysroot for %s at %s" % (self.board, self.sdk_path)
166 )
167 return sysroot
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 def GetSimpleChromeBinary(self):
170 """Get path to the binary in simple chrome."""
171 if self.binary:
172 return self.binary
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 output_dir = os.path.join(self.chrome_path, "src", f"out_{self.board}")
175 target_binary = None
176 binary_name = os.path.basename(self.inf_cmd)
177 for root, _, files in os.walk(output_dir):
178 for f in files:
179 if f == binary_name:
180 if target_binary is None:
181 target_binary = os.path.join(root, f)
182 else:
183 raise GdbSimpleChromeBinaryError(
184 "There are multiple %s under %s. Please specify the path to "
185 "the binary via --binary"
186 % (binary_name, output_dir)
187 )
188 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700189 raise GdbSimpleChromeBinaryError(
Alex Klein1699fab2022-09-08 08:46:06 -0600190 "There is no %s under %s." % (binary_name, output_dir)
191 )
192 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800193
Alex Klein1699fab2022-09-08 08:46:06 -0600194 def VerifyAndFinishInitialization(self, device):
195 """Verify files/processes exist and flags are correct."""
196 if not self.board:
197 if self.remote:
198 self.board = cros_build_lib.GetBoard(
199 device_board=device.board,
200 override_board=self.board,
201 strict=True,
202 )
203 else:
204 raise GdbCannotDetectBoardError(
205 "Cannot determine which board to use. "
206 "Please specify the with --board flag."
207 )
208 self.in_chroot = self.IsInChroot()
209 self.prompt = "(%s-gdb) " % self.board
210 if self.in_chroot:
211 self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
212 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
213 self.cross_gdb = self.GetCrossGdb()
214 else:
215 self.chrome_path = os.path.realpath(
216 os.path.join(
217 os.path.dirname(os.path.realpath(__file__)), "../../../.."
218 )
219 )
220 self.sdk_path = path_util.FindCacheDir()
221 self.sysroot = self.SimpleChromeSysroot()
222 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700223
Alex Klein1699fab2022-09-08 08:46:06 -0600224 if self.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600225 # If given remote process name, find pid & inf_cmd on remote device.
226 if self.remote_process_name or self.pid:
227 self._FindRemoteProcess(device)
cmticef23cb132015-04-10 15:13:00 -0700228
Alex Klein1699fab2022-09-08 08:46:06 -0600229 # Verify that sysroot is valid (exists).
230 if not os.path.isdir(self.sysroot):
231 raise GdbMissingSysrootError(
232 "Sysroot does not exist: %s" % self.sysroot
233 )
cmticef23cb132015-04-10 15:13:00 -0700234
Alex Klein1699fab2022-09-08 08:46:06 -0600235 self.device = device
236 if not self.in_chroot:
cmticeb70801a2014-12-11 14:29:34 -0800237 return
238
Alex Klein1699fab2022-09-08 08:46:06 -0600239 sysroot_inf_cmd = ""
240 if self.inf_cmd:
241 sysroot_inf_cmd = os.path.join(
242 self.sysroot, self.inf_cmd.lstrip("/")
243 )
cmticeb70801a2014-12-11 14:29:34 -0800244
Alex Klein1699fab2022-09-08 08:46:06 -0600245 # Verify that inf_cmd, if given, exists.
246 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
247 raise GdbMissingInferiorError(
248 "Cannot find file %s (in sysroot)." % sysroot_inf_cmd
249 )
cmticeb70801a2014-12-11 14:29:34 -0800250
Alex Klein1699fab2022-09-08 08:46:06 -0600251 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
252 # exists. If not, tell user and give them the option of quitting & getting
253 # the debug info.
254 if sysroot_inf_cmd:
255 stripped_info = cros_build_lib.run(
256 ["file", sysroot_inf_cmd], capture_output=True, encoding="utf-8"
257 ).stdout
258 if " not stripped" not in stripped_info:
259 debug_file = os.path.join(
260 self.sysroot, "usr/lib/debug", self.inf_cmd.lstrip("/")
261 )
262 debug_file += ".debug"
263 if not os.path.exists(debug_file):
264 equery = "equery-%s" % self.board
265 package = cros_build_lib.run(
266 [equery, "-q", "b", self.inf_cmd],
267 capture_output=True,
268 encoding="utf-8",
269 ).stdout
270 # pylint: disable=logging-not-lazy
271 logging.info(
272 self._MISSING_DEBUG_INFO_MSG
273 % {
274 "board": self.board,
275 "inf_cmd": self.inf_cmd,
276 "package": package,
277 "debug_file": debug_file,
278 }
279 )
280 answer = cros_build_lib.BooleanPrompt()
281 if not answer:
282 raise GdbEarlyExitError(
283 "Exiting early, at user request."
284 )
cmticeb70801a2014-12-11 14:29:34 -0800285
Alex Klein1699fab2022-09-08 08:46:06 -0600286 # Set up qemu, if appropriate.
287 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
288 if qemu_arch is None:
289 self.framework = "ldso"
cmticef23cb132015-04-10 15:13:00 -0700290 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600291 self.framework = "qemu"
292 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticef23cb132015-04-10 15:13:00 -0700293
Alex Klein1699fab2022-09-08 08:46:06 -0600294 if self.remote:
295 # Verify cgdb flag info.
296 if self.cgdb:
297 if osutils.Which("cgdb") is None:
298 raise GdbMissingDebuggerError(
299 "Cannot find cgdb. Please install " "cgdb first."
300 )
cmticef23cb132015-04-10 15:13:00 -0700301
Alex Klein1699fab2022-09-08 08:46:06 -0600302 def RemoveSysrootPrefix(self, path):
303 """Returns the given path with any sysroot prefix removed."""
304 # If the sysroot is /, then the paths are already normalized.
305 if self.sysroot != "/" and path.startswith(self.sysroot):
306 path = path.replace(self.sysroot, "", 1)
307 return path
cmticef23cb132015-04-10 15:13:00 -0700308
Alex Klein1699fab2022-09-08 08:46:06 -0600309 @staticmethod
310 def GetNonRootAccount():
311 """Return details about the non-root account we want to use.
cmticef23cb132015-04-10 15:13:00 -0700312
Alex Klein1699fab2022-09-08 08:46:06 -0600313 Returns:
314 A tuple of (username, uid, gid, home).
315 """
316 return (
317 os.environ.get("SUDO_USER", "nobody"),
318 int(os.environ.get("SUDO_UID", "65534")),
319 int(os.environ.get("SUDO_GID", "65534")),
320 # Should we find a better home?
321 "/tmp/portage",
322 )
cmticef23cb132015-04-10 15:13:00 -0700323
Alex Klein1699fab2022-09-08 08:46:06 -0600324 @staticmethod
325 @contextlib.contextmanager
326 def LockDb(db):
327 """Lock an account database.
cmticef23cb132015-04-10 15:13:00 -0700328
Alex Klein1699fab2022-09-08 08:46:06 -0600329 We use the same algorithm as shadow/user.eclass. This way we don't race
330 and corrupt things in parallel.
331 """
332 lock = "%s.lock" % db
333 _, tmplock = tempfile.mkstemp(prefix="%s.platform." % lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600334
Alex Klein1699fab2022-09-08 08:46:06 -0600335 # First try forever to grab the lock.
336 retry = lambda e: e.errno == errno.EEXIST
337 # Retry quickly at first, but slow down over time.
338 try:
339 retry_util.GenericRetry(
340 retry, 60, os.link, tmplock, lock, sleep=0.1
341 )
342 except Exception as e:
343 raise Exception("Could not grab lock %s. %s" % (lock, e))
Raul E Rangel746c45d2018-05-09 09:27:31 -0600344
Alex Klein1699fab2022-09-08 08:46:06 -0600345 # Yield while holding the lock, but try to clean it no matter what.
346 try:
347 os.unlink(tmplock)
348 yield lock
349 finally:
350 os.unlink(lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600351
Alex Klein1699fab2022-09-08 08:46:06 -0600352 def SetupUser(self):
353 """Propogate the user name<->id mapping from outside the chroot.
cmticef23cb132015-04-10 15:13:00 -0700354
Alex Klein1699fab2022-09-08 08:46:06 -0600355 Some unittests use getpwnam($USER), as does bash. If the account
356 is not registered in the sysroot, they get back errors.
357 """
358 MAGIC_GECOS = (
359 "Added by your friendly platform test helper; do not modify"
360 )
361 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
362 SDK_GECOS = "ChromeOS Developer"
363
364 user, uid, gid, home = self.GetNonRootAccount()
365 if user == "nobody":
366 return
367
368 passwd_db = os.path.join(self.sysroot, "etc", "passwd")
369 with self.LockDb(passwd_db):
370 data = osutils.ReadFile(passwd_db)
371 accts = data.splitlines()
372 for acct in accts:
373 passwd = acct.split(":")
374 if passwd[0] == user:
375 # Did the sdk make this account?
376 if passwd[4] == SDK_GECOS:
377 # Don't modify it (see below) since we didn't create it.
378 return
379
380 # Did we make this account?
381 if passwd[4] != MAGIC_GECOS:
382 raise RuntimeError(
383 "your passwd db (%s) has unmanaged acct %s"
384 % (passwd_db, user)
385 )
386
387 # Maybe we should see if it needs to be updated? Like if they
388 # changed UIDs? But we don't really check that elsewhere ...
389 return
390
391 acct = (
392 "%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s"
393 % {
394 "name": user,
395 "uid": uid,
396 "gid": gid,
397 "gecos": MAGIC_GECOS,
398 "homedir": home,
399 "shell": "/bin/bash",
400 }
401 )
Mike Frysinger31fdddd2023-02-24 15:50:55 -0500402 with open(passwd_db, "a", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -0600403 if data[-1] != "\n":
404 f.write("\n")
405 f.write("%s\n" % acct)
406
407 def _FindRemoteProcess(self, device):
408 """Find a named process (or a pid) running on a remote device."""
409 if not self.remote_process_name and not self.pid:
410 return
411
412 if self.remote_process_name:
413 # Look for a process with the specified name on the remote device; if
414 # found, get its pid.
415 pname = self.remote_process_name
416 if pname == "browser":
417 all_chrome_pids = set(
418 device.GetRunningPids("/opt/google/chrome/chrome")
419 )
420 non_main_chrome_pids = set(device.GetRunningPids("type="))
421 pids = list(all_chrome_pids - non_main_chrome_pids)
422 elif pname == "renderer" or pname == "gpu-process":
423 pids = device.GetRunningPids("type=%s" % pname)
424 else:
425 pids = device.GetRunningPids(pname)
426
427 if pids:
428 if len(pids) == 1:
429 self.pid = pids[0]
430 else:
431 raise GdbTooManyPidsError(
432 "Multiple pids found for %s process: %s. "
433 "You must specify the correct pid."
434 % (pname, repr(pids))
435 )
436 else:
437 raise GdbCannotFindRemoteProcessError(
438 'Cannot find pid for "%s" on %s' % (pname, self.remote)
439 )
440
441 # Find full path for process, from pid (and verify pid).
442 command = [
443 "readlink",
444 "-e",
445 "/proc/%s/exe" % self.pid,
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700446 ]
Alex Klein1699fab2022-09-08 08:46:06 -0600447 try:
448 res = device.run(command, capture_output=True)
449 if res.returncode == 0:
450 self.inf_cmd = res.stdout.rstrip("\n")
451 except cros_build_lib.RunCommandError:
452 raise GdbCannotFindRemoteProcessError(
453 "Unable to find name of process "
454 "with pid %s on %s" % (self.pid, self.remote)
455 )
Raul E Rangel746c45d2018-05-09 09:27:31 -0600456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 def GetCrossGdb(self):
458 """Find the appropriate cross-version of gdb for the board."""
459 toolchains = toolchain.GetToolchainsForBoard(self.board)
460 tc = list(toolchain.FilterToolchains(toolchains, "default", True))
461 cross_gdb = tc[0] + "-gdb"
462 if not osutils.Which(cross_gdb):
463 raise GdbMissingDebuggerError(
464 "Cannot find %s; do you need to run " "setup_board?" % cross_gdb
465 )
466 return cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 def GetGdbInitCommands(self, inferior_cmd, device=None):
469 """Generate list of commands with which to initialize the gdb session."""
470 gdb_init_commands = []
cmticef23cb132015-04-10 15:13:00 -0700471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 if self.remote:
473 sysroot_var = self.sysroot
474 else:
475 sysroot_var = "/"
cmticef23cb132015-04-10 15:13:00 -0700476
Alex Klein1699fab2022-09-08 08:46:06 -0600477 gdb_init_commands = [
478 "set sysroot %s" % sysroot_var,
479 "set prompt %s" % self.prompt,
480 ]
481 if self.in_chroot:
482 gdb_init_commands += [
483 "set solib-absolute-prefix %s" % sysroot_var,
484 "set solib-search-path %s" % sysroot_var,
485 "set debug-file-directory %s/usr/lib/debug" % sysroot_var,
486 ]
cmticef23cb132015-04-10 15:13:00 -0700487
Alex Klein1699fab2022-09-08 08:46:06 -0600488 if device:
Mike Frysingerc0780a62022-08-29 04:41:56 -0400489 ssh_cmd = device.agent.GetSSHCommand(self.ssh_settings)
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600490
Alex Klein1699fab2022-09-08 08:46:06 -0600491 ssh_cmd.extend(["--", "gdbserver"])
cmticef23cb132015-04-10 15:13:00 -0700492
Alex Klein1699fab2022-09-08 08:46:06 -0600493 if self.pid:
494 ssh_cmd.extend(["--attach", "stdio", str(self.pid)])
495 target_type = "remote"
496 elif inferior_cmd:
497 ssh_cmd.extend(["-", inferior_cmd])
498 ssh_cmd.extend(self.inf_args)
499 target_type = "remote"
500 else:
501 ssh_cmd.extend(["--multi", "stdio"])
502 target_type = "extended-remote"
cmticef23cb132015-04-10 15:13:00 -0700503
Alex Klein1699fab2022-09-08 08:46:06 -0600504 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700505
Alex Klein1699fab2022-09-08 08:46:06 -0600506 if self.in_chroot:
507 if inferior_cmd:
508 gdb_init_commands.append(
509 "file %s"
510 % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep))
511 )
512 else:
513 binary = self.GetSimpleChromeBinary()
514 gdb_init_commands += [
515 "set debug-file-directory %s" % os.path.dirname(binary),
516 "file %s" % binary,
517 ]
cmtice932e0aa2015-02-27 11:49:12 -0800518
Alex Klein1699fab2022-09-08 08:46:06 -0600519 gdb_init_commands.append("target %s | %s" % (target_type, ssh_cmd))
520 else:
521 if inferior_cmd:
522 gdb_init_commands.append("file %s " % inferior_cmd)
523 gdb_init_commands.append(
524 "set args %s" % " ".join(self.inf_args)
525 )
cmticeb70801a2014-12-11 14:29:34 -0800526
Alex Klein1699fab2022-09-08 08:46:06 -0600527 return gdb_init_commands
cmticef23cb132015-04-10 15:13:00 -0700528
Alex Klein1699fab2022-09-08 08:46:06 -0600529 def RunRemote(self):
530 """Handle remote debugging, via gdbserver & cross debugger."""
531 with remote_access.ChromiumOSDeviceHandler(
532 self.remote,
533 port=self.remote_port,
534 connect_settings=self.ssh_settings,
535 ping=self.ping,
536 ) as device:
Alex Klein1699fab2022-09-08 08:46:06 -0600537 self.VerifyAndFinishInitialization(device)
538 gdb_cmd = self.cross_gdb
cmticeb70801a2014-12-11 14:29:34 -0800539
Alex Klein1699fab2022-09-08 08:46:06 -0600540 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
541 gdb_args = ["--quiet"] + [
542 "--eval-command=%s" % x for x in gdb_commands
543 ]
544 gdb_args += self.gdb_args
cmticeb70801a2014-12-11 14:29:34 -0800545
Alex Klein1699fab2022-09-08 08:46:06 -0600546 if self.cgdb:
547 gdb_args = ["-d", gdb_cmd, "--"] + gdb_args
548 gdb_cmd = "cgdb"
cmtice932e0aa2015-02-27 11:49:12 -0800549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 cros_build_lib.run(
551 [gdb_cmd] + gdb_args,
552 ignore_sigint=True,
553 print_cmd=True,
554 cwd=self.sysroot,
555 )
556
557 def Run(self):
558 """Runs the debugger in a proper environment (e.g. qemu)."""
559
560 self.VerifyAndFinishInitialization(None)
561 self.SetupUser()
562 if self.framework == "qemu":
563 self.qemu.Install(self.sysroot)
564 self.qemu.RegisterBinfmt()
565
566 for mount in self._BIND_MOUNT_PATHS:
567 path = os.path.join(self.sysroot, mount)
568 osutils.SafeMakedirs(path)
569 osutils.Mount("/" + mount, path, "none", osutils.MS_BIND)
570
571 gdb_cmd = self._GDB
572 inferior_cmd = self.inf_cmd
573
574 gdb_argv = self.gdb_args[:]
575 if gdb_argv:
576 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
577 # Some programs expect to find data files via $CWD, so doing a chroot
578 # and dropping them into / would make them fail.
579 cwd = self.RemoveSysrootPrefix(os.getcwd())
580
581 os.chroot(self.sysroot)
582 os.chdir(cwd)
583 # The TERM the user is leveraging might not exist in the sysroot.
584 # Force a reasonable default that supports standard color sequences.
585 os.environ["TERM"] = "ansi"
586 # Some progs want this like bash else they get super confused.
587 os.environ["PWD"] = cwd
588 if not self.run_as_root:
589 _, uid, gid, home = self.GetNonRootAccount()
590 os.setgid(gid)
591 os.setuid(uid)
592 os.environ["HOME"] = home
593
594 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
595
596 gdb_args = [gdb_cmd, "--quiet"] + [
597 "--eval-command=%s" % x for x in gdb_commands
598 ]
599 gdb_args += self.gdb_args
600
601 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800602
603
cmtice932e0aa2015-02-27 11:49:12 -0800604def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600605 """Re-execute gdb as root.
cmticeb70801a2014-12-11 14:29:34 -0800606
Alex Klein1699fab2022-09-08 08:46:06 -0600607 We often need to do things as root, so make sure we're that. Like chroot
608 for proper library environment or do bind mounts.
cmticeb70801a2014-12-11 14:29:34 -0800609
Alex Klein1699fab2022-09-08 08:46:06 -0600610 Also unshare the mount namespace so as to ensure that doing bind mounts for
611 tests don't leak out to the normal chroot. Also unshare the UTS namespace
612 so changes to `hostname` do not impact the host.
613 """
614 if osutils.IsNonRootUser():
615 cmd = ["sudo", "-E", "--"] + argv
616 os.execvp(cmd[0], cmd)
617 else:
618 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
cmtice932e0aa2015-02-27 11:49:12 -0800619
620
cmticef23cb132015-04-10 15:13:00 -0700621def FindInferior(arg_list):
Alex Klein1699fab2022-09-08 08:46:06 -0600622 """Look for the name of the inferior (to be debugged) in arg list."""
cmtice932e0aa2015-02-27 11:49:12 -0800623
Alex Klein1699fab2022-09-08 08:46:06 -0600624 program_name = ""
625 new_list = []
626 for item in arg_list:
627 if item[0] == "-":
628 new_list.append(item)
629 elif not program_name:
630 program_name = item
631 else:
632 raise RuntimeError(
633 "Found multiple program names: %s %s" % (program_name, item)
634 )
cmtice932e0aa2015-02-27 11:49:12 -0800635
Alex Klein1699fab2022-09-08 08:46:06 -0600636 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800637
638
639def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600640 parser = commandline.ArgumentParser(description=__doc__)
cmticeb70801a2014-12-11 14:29:34 -0800641
Alex Klein1699fab2022-09-08 08:46:06 -0600642 parser.add_argument("--board", default=None, help="board to debug for")
643 parser.add_argument(
644 "-g",
645 "--gdb_args",
646 action="append",
647 default=[],
648 help="Arguments to gdb itself. If multiple arguments are"
649 " passed, each argument needs a separate '-g' flag.",
650 )
651 parser.add_argument(
652 "--remote",
653 default=None,
654 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
655 help="Remote device on which to run the binary. Use"
656 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
657 " already running local virtual machine.",
658 )
659 parser.add_argument(
660 "--pid",
661 default="",
662 help="Process ID of the (already) running process on the"
663 " remote device to which to attach.",
664 )
665 parser.add_argument(
666 "--remote_pid",
667 dest="pid",
668 default="",
669 help="Deprecated alias for --pid.",
670 )
671 parser.add_argument(
672 "--no-ping",
673 dest="ping",
674 default=True,
675 action="store_false",
676 help="Do not ping remote before attempting to connect.",
677 )
678 parser.add_argument(
679 "--attach",
680 dest="attach_name",
681 default="",
682 help="Name of existing process to which to attach, on"
683 ' remote device (remote debugging only). "--attach'
684 ' browser" will find the main chrome browser process;'
685 ' "--attach renderer" will find a chrome renderer'
686 ' process; "--attach gpu-process" will find the chrome'
687 " gpu process.",
688 )
689 parser.add_argument(
690 "--cgdb",
691 default=False,
692 action="store_true",
693 help="Use cgdb curses interface rather than plain gdb."
694 "This option is only valid for remote debugging.",
695 )
696 parser.add_argument(
697 "inf_args",
698 nargs=argparse.REMAINDER,
699 help="Arguments for gdb to pass to the program being"
700 " debugged. These are positional and must come at the end"
701 " of the command line. This will not work if attaching"
702 " to an already running program.",
703 )
704 parser.add_argument(
705 "--binary",
706 default="",
Brian Norriscc3331c2022-04-22 13:52:00 -0700707 help="full path to the binary being debugged."
Alex Klein1699fab2022-09-08 08:46:06 -0600708 " This is only useful for simple chrome."
Brian Norriscc3331c2022-04-22 13:52:00 -0700709 " An example is --binary /home/out_falco/chrome.",
Alex Klein1699fab2022-09-08 08:46:06 -0600710 )
cmticeb70801a2014-12-11 14:29:34 -0800711
Alex Klein1699fab2022-09-08 08:46:06 -0600712 options = parser.parse_args(argv)
713 options.Freeze()
cmticeb70801a2014-12-11 14:29:34 -0800714
Alex Klein1699fab2022-09-08 08:46:06 -0600715 gdb_args = []
716 inf_args = []
717 inf_cmd = ""
cmtice932e0aa2015-02-27 11:49:12 -0800718
Alex Klein1699fab2022-09-08 08:46:06 -0600719 if options.inf_args:
720 inf_cmd = options.inf_args[0]
721 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800722
Alex Klein1699fab2022-09-08 08:46:06 -0600723 if options.gdb_args:
724 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 if inf_cmd:
727 fname = os.path.join(
728 build_target_lib.get_default_sysroot_path(options.board),
729 inf_cmd.lstrip("/"),
730 )
731 if not os.path.exists(fname):
Alex Kleindf8ee502022-10-18 09:48:15 -0600732 cros_build_lib.Die("Cannot find program %s.", fname)
Alex Klein1699fab2022-09-08 08:46:06 -0600733 else:
734 if inf_args:
735 parser.error("Cannot specify arguments without a program.")
cmtice932e0aa2015-02-27 11:49:12 -0800736
Alex Klein1699fab2022-09-08 08:46:06 -0600737 if inf_args and (options.pid or options.attach_name):
738 parser.error(
739 "Cannot pass arguments to an already"
740 " running process (--remote-pid or --attach)."
741 )
cmticef23cb132015-04-10 15:13:00 -0700742
cmticef23cb132015-04-10 15:13:00 -0700743 if options.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600744 if options.attach_name and options.attach_name == "browser":
745 inf_cmd = "/opt/google/chrome/chrome"
cmticef23cb132015-04-10 15:13:00 -0700746 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600747 if options.cgdb:
748 parser.error(
749 "--cgdb option can only be used with remote debugging."
750 )
751 if options.pid:
752 parser.error(
753 "Must specify a remote device (--remote) if you want "
754 "to attach to a remote pid."
755 )
756 if options.attach_name:
757 parser.error(
758 "Must specify remote device (--remote) when using"
759 " --attach option."
760 )
761 if options.binary:
762 if not os.path.exists(options.binary):
763 parser.error("%s does not exist." % options.binary)
cmticef23cb132015-04-10 15:13:00 -0700764
Alex Klein1699fab2022-09-08 08:46:06 -0600765 # Once we've finished checking args, make sure we're root.
766 if not options.remote:
767 _ReExecuteIfNeeded([sys.argv[0]] + argv)
768
769 gdb = BoardSpecificGdb(
770 options.board,
771 gdb_args,
772 inf_cmd,
773 inf_args,
774 options.remote,
775 options.pid,
776 options.attach_name,
777 options.cgdb,
778 options.ping,
779 options.binary,
780 )
781
782 try:
783 if options.remote:
784 gdb.RunRemote()
785 else:
786 gdb.Run()
787
788 except GdbException as e:
789 if options.debug:
790 raise
791 else:
792 raise cros_build_lib.Die(str(e))