blob: d33280ca0ee72241a5ff904fb34b367c72886338 [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,
Marc Grimme5441aba2023-04-14 16:56:36 +0200110 gdb_binary,
Alex Klein1699fab2022-09-08 08:46:06 -0600111 ):
112 self.board = board
113 self.sysroot = None
114 self.prompt = "(gdb) "
115 self.inf_cmd = inf_cmd
116 self.run_as_root = False
117 self.gdb_args = gdb_args
118 self.inf_args = inf_args
119 self.remote = remote.hostname if remote else None
120 self.pid = pid
121 self.remote_process_name = remote_process_name
122 # Port used for sending ssh commands to DUT.
123 self.remote_port = remote.port if remote else None
124 # Port for communicating between gdb & gdbserver.
125 self.gdbserver_port = remote_access.GetUnusedPort()
126 self.ssh_settings = remote_access.CompileSSHConnectSettings(
127 **self._EXTRA_SSH_SETTINGS
128 )
129 self.cgdb = cgdb_flag
130 self.framework = "auto"
131 self.qemu = None
132 self.device = None
Marc Grimme5441aba2023-04-14 16:56:36 +0200133 self.cross_gdb = gdb_binary
Alex Klein1699fab2022-09-08 08:46:06 -0600134 self.ping = ping
135 self.binary = binary
136 self.in_chroot = None
137 self.chrome_path = None
138 self.sdk_path = None
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 def IsInChroot(self):
141 """Decide whether we are in chroot or chrome-sdk."""
142 return os.path.exists("/mnt/host/source/chromite/")
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700143
Alex Klein1699fab2022-09-08 08:46:06 -0600144 def SimpleChromeGdb(self):
145 """Get the name of the cross gdb based on board name."""
146 bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
147 cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
148 self.sdk_path,
149 self.board,
150 )
151 bin_path = os.path.join(bin_path, "bin")
152 for f in os.listdir(bin_path):
153 if f.endswith("gdb"):
154 return os.path.join(bin_path, f)
155 raise GdbMissingDebuggerError(
156 "Cannot find cross gdb for %s." % self.board
157 )
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700158
Alex Klein1699fab2022-09-08 08:46:06 -0600159 def SimpleChromeSysroot(self):
160 """Get the sysroot in simple chrome."""
161 sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
162 constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board
163 )
164 if not sysroot:
165 raise GdbMissingSysrootError(
166 "Cannot find sysroot for %s at %s" % (self.board, self.sdk_path)
167 )
168 return sysroot
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 def GetSimpleChromeBinary(self):
171 """Get path to the binary in simple chrome."""
172 if self.binary:
173 return self.binary
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 output_dir = os.path.join(self.chrome_path, "src", f"out_{self.board}")
176 target_binary = None
177 binary_name = os.path.basename(self.inf_cmd)
178 for root, _, files in os.walk(output_dir):
179 for f in files:
180 if f == binary_name:
181 if target_binary is None:
182 target_binary = os.path.join(root, f)
183 else:
184 raise GdbSimpleChromeBinaryError(
185 "There are multiple %s under %s. Please specify the path to "
186 "the binary via --binary"
187 % (binary_name, output_dir)
188 )
189 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700190 raise GdbSimpleChromeBinaryError(
Alex Klein1699fab2022-09-08 08:46:06 -0600191 "There is no %s under %s." % (binary_name, output_dir)
192 )
193 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800194
Alex Klein1699fab2022-09-08 08:46:06 -0600195 def VerifyAndFinishInitialization(self, device):
196 """Verify files/processes exist and flags are correct."""
197 if not self.board:
198 if self.remote:
199 self.board = cros_build_lib.GetBoard(
200 device_board=device.board,
201 override_board=self.board,
202 strict=True,
203 )
204 else:
205 raise GdbCannotDetectBoardError(
206 "Cannot determine which board to use. "
207 "Please specify the with --board flag."
208 )
209 self.in_chroot = self.IsInChroot()
210 self.prompt = "(%s-gdb) " % self.board
211 if self.in_chroot:
212 self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
213 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
Marc Grimme5441aba2023-04-14 16:56:36 +0200214 if not self.cross_gdb:
215 self.cross_gdb = self.GetCrossGdb()
Alex Klein1699fab2022-09-08 08:46:06 -0600216 else:
217 self.chrome_path = os.path.realpath(
218 os.path.join(
219 os.path.dirname(os.path.realpath(__file__)), "../../../.."
220 )
221 )
222 self.sdk_path = path_util.FindCacheDir()
223 self.sysroot = self.SimpleChromeSysroot()
Marc Grimme5441aba2023-04-14 16:56:36 +0200224 if not self.cross_gdb:
225 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700226
Alex Klein1699fab2022-09-08 08:46:06 -0600227 if self.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600228 # If given remote process name, find pid & inf_cmd on remote device.
229 if self.remote_process_name or self.pid:
230 self._FindRemoteProcess(device)
cmticef23cb132015-04-10 15:13:00 -0700231
Alex Klein1699fab2022-09-08 08:46:06 -0600232 # Verify that sysroot is valid (exists).
233 if not os.path.isdir(self.sysroot):
234 raise GdbMissingSysrootError(
235 "Sysroot does not exist: %s" % self.sysroot
236 )
cmticef23cb132015-04-10 15:13:00 -0700237
Alex Klein1699fab2022-09-08 08:46:06 -0600238 self.device = device
239 if not self.in_chroot:
cmticeb70801a2014-12-11 14:29:34 -0800240 return
241
Alex Klein1699fab2022-09-08 08:46:06 -0600242 sysroot_inf_cmd = ""
243 if self.inf_cmd:
244 sysroot_inf_cmd = os.path.join(
245 self.sysroot, self.inf_cmd.lstrip("/")
246 )
cmticeb70801a2014-12-11 14:29:34 -0800247
Alex Klein1699fab2022-09-08 08:46:06 -0600248 # Verify that inf_cmd, if given, exists.
249 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
250 raise GdbMissingInferiorError(
251 "Cannot find file %s (in sysroot)." % sysroot_inf_cmd
252 )
cmticeb70801a2014-12-11 14:29:34 -0800253
Alex Klein1699fab2022-09-08 08:46:06 -0600254 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
255 # exists. If not, tell user and give them the option of quitting & getting
256 # the debug info.
257 if sysroot_inf_cmd:
258 stripped_info = cros_build_lib.run(
259 ["file", sysroot_inf_cmd], capture_output=True, encoding="utf-8"
260 ).stdout
261 if " not stripped" not in stripped_info:
262 debug_file = os.path.join(
263 self.sysroot, "usr/lib/debug", self.inf_cmd.lstrip("/")
264 )
265 debug_file += ".debug"
266 if not os.path.exists(debug_file):
267 equery = "equery-%s" % self.board
268 package = cros_build_lib.run(
269 [equery, "-q", "b", self.inf_cmd],
270 capture_output=True,
271 encoding="utf-8",
272 ).stdout
273 # pylint: disable=logging-not-lazy
274 logging.info(
275 self._MISSING_DEBUG_INFO_MSG
276 % {
277 "board": self.board,
278 "inf_cmd": self.inf_cmd,
279 "package": package,
280 "debug_file": debug_file,
281 }
282 )
283 answer = cros_build_lib.BooleanPrompt()
284 if not answer:
285 raise GdbEarlyExitError(
286 "Exiting early, at user request."
287 )
cmticeb70801a2014-12-11 14:29:34 -0800288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 # Set up qemu, if appropriate.
290 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
291 if qemu_arch is None:
292 self.framework = "ldso"
cmticef23cb132015-04-10 15:13:00 -0700293 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600294 self.framework = "qemu"
295 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticef23cb132015-04-10 15:13:00 -0700296
Alex Klein1699fab2022-09-08 08:46:06 -0600297 if self.remote:
298 # Verify cgdb flag info.
299 if self.cgdb:
300 if osutils.Which("cgdb") is None:
301 raise GdbMissingDebuggerError(
302 "Cannot find cgdb. Please install " "cgdb first."
303 )
cmticef23cb132015-04-10 15:13:00 -0700304
Alex Klein1699fab2022-09-08 08:46:06 -0600305 def RemoveSysrootPrefix(self, path):
306 """Returns the given path with any sysroot prefix removed."""
307 # If the sysroot is /, then the paths are already normalized.
308 if self.sysroot != "/" and path.startswith(self.sysroot):
309 path = path.replace(self.sysroot, "", 1)
310 return path
cmticef23cb132015-04-10 15:13:00 -0700311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 @staticmethod
313 def GetNonRootAccount():
314 """Return details about the non-root account we want to use.
cmticef23cb132015-04-10 15:13:00 -0700315
Alex Klein1699fab2022-09-08 08:46:06 -0600316 Returns:
317 A tuple of (username, uid, gid, home).
318 """
319 return (
320 os.environ.get("SUDO_USER", "nobody"),
321 int(os.environ.get("SUDO_UID", "65534")),
322 int(os.environ.get("SUDO_GID", "65534")),
323 # Should we find a better home?
324 "/tmp/portage",
325 )
cmticef23cb132015-04-10 15:13:00 -0700326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 @staticmethod
328 @contextlib.contextmanager
329 def LockDb(db):
330 """Lock an account database.
cmticef23cb132015-04-10 15:13:00 -0700331
Alex Klein1699fab2022-09-08 08:46:06 -0600332 We use the same algorithm as shadow/user.eclass. This way we don't race
333 and corrupt things in parallel.
334 """
335 lock = "%s.lock" % db
336 _, tmplock = tempfile.mkstemp(prefix="%s.platform." % lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600337
Alex Klein1699fab2022-09-08 08:46:06 -0600338 # First try forever to grab the lock.
339 retry = lambda e: e.errno == errno.EEXIST
340 # Retry quickly at first, but slow down over time.
341 try:
342 retry_util.GenericRetry(
343 retry, 60, os.link, tmplock, lock, sleep=0.1
344 )
345 except Exception as e:
346 raise Exception("Could not grab lock %s. %s" % (lock, e))
Raul E Rangel746c45d2018-05-09 09:27:31 -0600347
Alex Klein1699fab2022-09-08 08:46:06 -0600348 # Yield while holding the lock, but try to clean it no matter what.
349 try:
350 os.unlink(tmplock)
351 yield lock
352 finally:
353 os.unlink(lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600354
Alex Klein1699fab2022-09-08 08:46:06 -0600355 def SetupUser(self):
356 """Propogate the user name<->id mapping from outside the chroot.
cmticef23cb132015-04-10 15:13:00 -0700357
Alex Klein1699fab2022-09-08 08:46:06 -0600358 Some unittests use getpwnam($USER), as does bash. If the account
359 is not registered in the sysroot, they get back errors.
360 """
361 MAGIC_GECOS = (
362 "Added by your friendly platform test helper; do not modify"
363 )
364 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
365 SDK_GECOS = "ChromeOS Developer"
366
367 user, uid, gid, home = self.GetNonRootAccount()
368 if user == "nobody":
369 return
370
371 passwd_db = os.path.join(self.sysroot, "etc", "passwd")
372 with self.LockDb(passwd_db):
373 data = osutils.ReadFile(passwd_db)
374 accts = data.splitlines()
375 for acct in accts:
376 passwd = acct.split(":")
377 if passwd[0] == user:
378 # Did the sdk make this account?
379 if passwd[4] == SDK_GECOS:
380 # Don't modify it (see below) since we didn't create it.
381 return
382
383 # Did we make this account?
384 if passwd[4] != MAGIC_GECOS:
385 raise RuntimeError(
386 "your passwd db (%s) has unmanaged acct %s"
387 % (passwd_db, user)
388 )
389
390 # Maybe we should see if it needs to be updated? Like if they
391 # changed UIDs? But we don't really check that elsewhere ...
392 return
393
394 acct = (
395 "%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s"
396 % {
397 "name": user,
398 "uid": uid,
399 "gid": gid,
400 "gecos": MAGIC_GECOS,
401 "homedir": home,
402 "shell": "/bin/bash",
403 }
404 )
Mike Frysinger31fdddd2023-02-24 15:50:55 -0500405 with open(passwd_db, "a", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -0600406 if data[-1] != "\n":
407 f.write("\n")
408 f.write("%s\n" % acct)
409
410 def _FindRemoteProcess(self, device):
411 """Find a named process (or a pid) running on a remote device."""
412 if not self.remote_process_name and not self.pid:
413 return
414
415 if self.remote_process_name:
416 # Look for a process with the specified name on the remote device; if
417 # found, get its pid.
418 pname = self.remote_process_name
419 if pname == "browser":
420 all_chrome_pids = set(
421 device.GetRunningPids("/opt/google/chrome/chrome")
422 )
423 non_main_chrome_pids = set(device.GetRunningPids("type="))
424 pids = list(all_chrome_pids - non_main_chrome_pids)
425 elif pname == "renderer" or pname == "gpu-process":
426 pids = device.GetRunningPids("type=%s" % pname)
427 else:
428 pids = device.GetRunningPids(pname)
429
430 if pids:
431 if len(pids) == 1:
432 self.pid = pids[0]
433 else:
434 raise GdbTooManyPidsError(
435 "Multiple pids found for %s process: %s. "
436 "You must specify the correct pid."
437 % (pname, repr(pids))
438 )
439 else:
440 raise GdbCannotFindRemoteProcessError(
441 'Cannot find pid for "%s" on %s' % (pname, self.remote)
442 )
443
444 # Find full path for process, from pid (and verify pid).
445 command = [
446 "readlink",
447 "-e",
448 "/proc/%s/exe" % self.pid,
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700449 ]
Alex Klein1699fab2022-09-08 08:46:06 -0600450 try:
451 res = device.run(command, capture_output=True)
452 if res.returncode == 0:
453 self.inf_cmd = res.stdout.rstrip("\n")
454 except cros_build_lib.RunCommandError:
455 raise GdbCannotFindRemoteProcessError(
456 "Unable to find name of process "
457 "with pid %s on %s" % (self.pid, self.remote)
458 )
Raul E Rangel746c45d2018-05-09 09:27:31 -0600459
Alex Klein1699fab2022-09-08 08:46:06 -0600460 def GetCrossGdb(self):
461 """Find the appropriate cross-version of gdb for the board."""
462 toolchains = toolchain.GetToolchainsForBoard(self.board)
463 tc = list(toolchain.FilterToolchains(toolchains, "default", True))
464 cross_gdb = tc[0] + "-gdb"
465 if not osutils.Which(cross_gdb):
466 raise GdbMissingDebuggerError(
467 "Cannot find %s; do you need to run " "setup_board?" % cross_gdb
468 )
469 return cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700470
Alex Klein1699fab2022-09-08 08:46:06 -0600471 def GetGdbInitCommands(self, inferior_cmd, device=None):
472 """Generate list of commands with which to initialize the gdb session."""
473 gdb_init_commands = []
cmticef23cb132015-04-10 15:13:00 -0700474
Alex Klein1699fab2022-09-08 08:46:06 -0600475 if self.remote:
476 sysroot_var = self.sysroot
477 else:
478 sysroot_var = "/"
cmticef23cb132015-04-10 15:13:00 -0700479
Alex Klein1699fab2022-09-08 08:46:06 -0600480 gdb_init_commands = [
481 "set sysroot %s" % sysroot_var,
482 "set prompt %s" % self.prompt,
483 ]
484 if self.in_chroot:
485 gdb_init_commands += [
486 "set solib-absolute-prefix %s" % sysroot_var,
487 "set solib-search-path %s" % sysroot_var,
488 "set debug-file-directory %s/usr/lib/debug" % sysroot_var,
489 ]
cmticef23cb132015-04-10 15:13:00 -0700490
Alex Klein1699fab2022-09-08 08:46:06 -0600491 if device:
Mike Frysingerc0780a62022-08-29 04:41:56 -0400492 ssh_cmd = device.agent.GetSSHCommand(self.ssh_settings)
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 ssh_cmd.extend(["--", "gdbserver"])
cmticef23cb132015-04-10 15:13:00 -0700495
Alex Klein1699fab2022-09-08 08:46:06 -0600496 if self.pid:
497 ssh_cmd.extend(["--attach", "stdio", str(self.pid)])
498 target_type = "remote"
499 elif inferior_cmd:
500 ssh_cmd.extend(["-", inferior_cmd])
501 ssh_cmd.extend(self.inf_args)
502 target_type = "remote"
503 else:
504 ssh_cmd.extend(["--multi", "stdio"])
505 target_type = "extended-remote"
cmticef23cb132015-04-10 15:13:00 -0700506
Alex Klein1699fab2022-09-08 08:46:06 -0600507 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700508
Alex Klein1699fab2022-09-08 08:46:06 -0600509 if self.in_chroot:
510 if inferior_cmd:
511 gdb_init_commands.append(
512 "file %s"
513 % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep))
514 )
515 else:
516 binary = self.GetSimpleChromeBinary()
517 gdb_init_commands += [
518 "set debug-file-directory %s" % os.path.dirname(binary),
519 "file %s" % binary,
520 ]
cmtice932e0aa2015-02-27 11:49:12 -0800521
Alex Klein1699fab2022-09-08 08:46:06 -0600522 gdb_init_commands.append("target %s | %s" % (target_type, ssh_cmd))
523 else:
524 if inferior_cmd:
525 gdb_init_commands.append("file %s " % inferior_cmd)
526 gdb_init_commands.append(
527 "set args %s" % " ".join(self.inf_args)
528 )
cmticeb70801a2014-12-11 14:29:34 -0800529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 return gdb_init_commands
cmticef23cb132015-04-10 15:13:00 -0700531
Alex Klein1699fab2022-09-08 08:46:06 -0600532 def RunRemote(self):
533 """Handle remote debugging, via gdbserver & cross debugger."""
534 with remote_access.ChromiumOSDeviceHandler(
535 self.remote,
536 port=self.remote_port,
537 connect_settings=self.ssh_settings,
538 ping=self.ping,
539 ) as device:
Alex Klein1699fab2022-09-08 08:46:06 -0600540 self.VerifyAndFinishInitialization(device)
541 gdb_cmd = self.cross_gdb
cmticeb70801a2014-12-11 14:29:34 -0800542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
544 gdb_args = ["--quiet"] + [
545 "--eval-command=%s" % x for x in gdb_commands
546 ]
547 gdb_args += self.gdb_args
cmticeb70801a2014-12-11 14:29:34 -0800548
Alex Klein1699fab2022-09-08 08:46:06 -0600549 if self.cgdb:
550 gdb_args = ["-d", gdb_cmd, "--"] + gdb_args
551 gdb_cmd = "cgdb"
cmtice932e0aa2015-02-27 11:49:12 -0800552
Alex Klein1699fab2022-09-08 08:46:06 -0600553 cros_build_lib.run(
554 [gdb_cmd] + gdb_args,
555 ignore_sigint=True,
556 print_cmd=True,
557 cwd=self.sysroot,
558 )
559
560 def Run(self):
561 """Runs the debugger in a proper environment (e.g. qemu)."""
562
563 self.VerifyAndFinishInitialization(None)
564 self.SetupUser()
565 if self.framework == "qemu":
566 self.qemu.Install(self.sysroot)
567 self.qemu.RegisterBinfmt()
568
569 for mount in self._BIND_MOUNT_PATHS:
570 path = os.path.join(self.sysroot, mount)
571 osutils.SafeMakedirs(path)
572 osutils.Mount("/" + mount, path, "none", osutils.MS_BIND)
573
574 gdb_cmd = self._GDB
575 inferior_cmd = self.inf_cmd
576
577 gdb_argv = self.gdb_args[:]
578 if gdb_argv:
579 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
580 # Some programs expect to find data files via $CWD, so doing a chroot
581 # and dropping them into / would make them fail.
582 cwd = self.RemoveSysrootPrefix(os.getcwd())
583
584 os.chroot(self.sysroot)
585 os.chdir(cwd)
586 # The TERM the user is leveraging might not exist in the sysroot.
587 # Force a reasonable default that supports standard color sequences.
588 os.environ["TERM"] = "ansi"
589 # Some progs want this like bash else they get super confused.
590 os.environ["PWD"] = cwd
591 if not self.run_as_root:
592 _, uid, gid, home = self.GetNonRootAccount()
593 os.setgid(gid)
594 os.setuid(uid)
595 os.environ["HOME"] = home
596
597 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
598
599 gdb_args = [gdb_cmd, "--quiet"] + [
600 "--eval-command=%s" % x for x in gdb_commands
601 ]
602 gdb_args += self.gdb_args
603
604 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800605
606
cmtice932e0aa2015-02-27 11:49:12 -0800607def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600608 """Re-execute gdb as root.
cmticeb70801a2014-12-11 14:29:34 -0800609
Alex Klein1699fab2022-09-08 08:46:06 -0600610 We often need to do things as root, so make sure we're that. Like chroot
611 for proper library environment or do bind mounts.
cmticeb70801a2014-12-11 14:29:34 -0800612
Alex Klein1699fab2022-09-08 08:46:06 -0600613 Also unshare the mount namespace so as to ensure that doing bind mounts for
614 tests don't leak out to the normal chroot. Also unshare the UTS namespace
615 so changes to `hostname` do not impact the host.
616 """
617 if osutils.IsNonRootUser():
618 cmd = ["sudo", "-E", "--"] + argv
619 os.execvp(cmd[0], cmd)
620 else:
621 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
cmtice932e0aa2015-02-27 11:49:12 -0800622
623
cmticef23cb132015-04-10 15:13:00 -0700624def FindInferior(arg_list):
Alex Klein1699fab2022-09-08 08:46:06 -0600625 """Look for the name of the inferior (to be debugged) in arg list."""
cmtice932e0aa2015-02-27 11:49:12 -0800626
Alex Klein1699fab2022-09-08 08:46:06 -0600627 program_name = ""
628 new_list = []
629 for item in arg_list:
630 if item[0] == "-":
631 new_list.append(item)
632 elif not program_name:
633 program_name = item
634 else:
635 raise RuntimeError(
636 "Found multiple program names: %s %s" % (program_name, item)
637 )
cmtice932e0aa2015-02-27 11:49:12 -0800638
Alex Klein1699fab2022-09-08 08:46:06 -0600639 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800640
641
642def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600643 parser = commandline.ArgumentParser(description=__doc__)
cmticeb70801a2014-12-11 14:29:34 -0800644
Alex Klein1699fab2022-09-08 08:46:06 -0600645 parser.add_argument("--board", default=None, help="board to debug for")
646 parser.add_argument(
Marc Grimme5441aba2023-04-14 16:56:36 +0200647 "--gdb",
648 type="file_exists",
649 help="Path to gdb binary. Default is that gdb binary is auto detected.",
650 )
651 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600652 "-g",
653 "--gdb_args",
654 action="append",
655 default=[],
656 help="Arguments to gdb itself. If multiple arguments are"
657 " passed, each argument needs a separate '-g' flag.",
658 )
659 parser.add_argument(
660 "--remote",
661 default=None,
662 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
663 help="Remote device on which to run the binary. Use"
664 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
665 " already running local virtual machine.",
666 )
667 parser.add_argument(
668 "--pid",
669 default="",
670 help="Process ID of the (already) running process on the"
671 " remote device to which to attach.",
672 )
673 parser.add_argument(
674 "--remote_pid",
675 dest="pid",
676 default="",
677 help="Deprecated alias for --pid.",
678 )
679 parser.add_argument(
680 "--no-ping",
681 dest="ping",
682 default=True,
683 action="store_false",
684 help="Do not ping remote before attempting to connect.",
685 )
686 parser.add_argument(
687 "--attach",
688 dest="attach_name",
689 default="",
690 help="Name of existing process to which to attach, on"
691 ' remote device (remote debugging only). "--attach'
692 ' browser" will find the main chrome browser process;'
693 ' "--attach renderer" will find a chrome renderer'
694 ' process; "--attach gpu-process" will find the chrome'
695 " gpu process.",
696 )
697 parser.add_argument(
698 "--cgdb",
699 default=False,
700 action="store_true",
701 help="Use cgdb curses interface rather than plain gdb."
702 "This option is only valid for remote debugging.",
703 )
704 parser.add_argument(
705 "inf_args",
706 nargs=argparse.REMAINDER,
707 help="Arguments for gdb to pass to the program being"
708 " debugged. These are positional and must come at the end"
709 " of the command line. This will not work if attaching"
710 " to an already running program.",
711 )
712 parser.add_argument(
713 "--binary",
714 default="",
Brian Norriscc3331c2022-04-22 13:52:00 -0700715 help="full path to the binary being debugged."
Alex Klein1699fab2022-09-08 08:46:06 -0600716 " This is only useful for simple chrome."
Brian Norriscc3331c2022-04-22 13:52:00 -0700717 " An example is --binary /home/out_falco/chrome.",
Alex Klein1699fab2022-09-08 08:46:06 -0600718 )
cmticeb70801a2014-12-11 14:29:34 -0800719
Alex Klein1699fab2022-09-08 08:46:06 -0600720 options = parser.parse_args(argv)
721 options.Freeze()
cmticeb70801a2014-12-11 14:29:34 -0800722
Alex Klein1699fab2022-09-08 08:46:06 -0600723 gdb_args = []
724 inf_args = []
725 inf_cmd = ""
cmtice932e0aa2015-02-27 11:49:12 -0800726
Alex Klein1699fab2022-09-08 08:46:06 -0600727 if options.inf_args:
728 inf_cmd = options.inf_args[0]
729 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800730
Alex Klein1699fab2022-09-08 08:46:06 -0600731 if options.gdb_args:
732 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800733
Alex Klein1699fab2022-09-08 08:46:06 -0600734 if inf_cmd:
735 fname = os.path.join(
736 build_target_lib.get_default_sysroot_path(options.board),
737 inf_cmd.lstrip("/"),
738 )
739 if not os.path.exists(fname):
Alex Kleindf8ee502022-10-18 09:48:15 -0600740 cros_build_lib.Die("Cannot find program %s.", fname)
Alex Klein1699fab2022-09-08 08:46:06 -0600741 else:
742 if inf_args:
743 parser.error("Cannot specify arguments without a program.")
cmtice932e0aa2015-02-27 11:49:12 -0800744
Alex Klein1699fab2022-09-08 08:46:06 -0600745 if inf_args and (options.pid or options.attach_name):
746 parser.error(
747 "Cannot pass arguments to an already"
748 " running process (--remote-pid or --attach)."
749 )
cmticef23cb132015-04-10 15:13:00 -0700750
cmticef23cb132015-04-10 15:13:00 -0700751 if options.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600752 if options.attach_name and options.attach_name == "browser":
753 inf_cmd = "/opt/google/chrome/chrome"
cmticef23cb132015-04-10 15:13:00 -0700754 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600755 if options.cgdb:
756 parser.error(
757 "--cgdb option can only be used with remote debugging."
758 )
759 if options.pid:
760 parser.error(
761 "Must specify a remote device (--remote) if you want "
762 "to attach to a remote pid."
763 )
764 if options.attach_name:
765 parser.error(
766 "Must specify remote device (--remote) when using"
767 " --attach option."
768 )
769 if options.binary:
770 if not os.path.exists(options.binary):
771 parser.error("%s does not exist." % options.binary)
cmticef23cb132015-04-10 15:13:00 -0700772
Alex Klein1699fab2022-09-08 08:46:06 -0600773 # Once we've finished checking args, make sure we're root.
774 if not options.remote:
775 _ReExecuteIfNeeded([sys.argv[0]] + argv)
776
777 gdb = BoardSpecificGdb(
778 options.board,
779 gdb_args,
780 inf_cmd,
781 inf_args,
782 options.remote,
783 options.pid,
784 options.attach_name,
785 options.cgdb,
786 options.ping,
787 options.binary,
Marc Grimme5441aba2023-04-14 16:56:36 +0200788 options.gdb,
Alex Klein1699fab2022-09-08 08:46:06 -0600789 )
790
791 try:
792 if options.remote:
793 gdb.RunRemote()
794 else:
795 gdb.Run()
796
797 except GdbException as e:
798 if options.debug:
799 raise
800 else:
801 raise cros_build_lib.Die(str(e))