blob: 9c1f098fb27906c391d0cb44fcc2dbff521d815f [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"""
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(
Alex Klein8b444532023-04-11 16:35:24 -0600185 "There are multiple %s under %s. Please specify "
186 "the path to the binary via --binary"
Alex Klein1699fab2022-09-08 08:46:06 -0600187 % (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 Klein8b444532023-04-11 16:35:24 -0600254 # Check to see if inf_cmd is stripped, and if so, check to see if debug
255 # file exists. If not, tell user and give them the option of quitting &
256 # getting the debug info.
Alex Klein1699fab2022-09-08 08:46:06 -0600257 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
Alex Klein8b444532023-04-11 16:35:24 -0600390 # Maybe we should see if it needs to be updated? Like if
391 # they changed UIDs? But we don't really check that
392 # elsewhere ...
Alex Klein1699fab2022-09-08 08:46:06 -0600393 return
394
395 acct = (
396 "%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s"
397 % {
398 "name": user,
399 "uid": uid,
400 "gid": gid,
401 "gecos": MAGIC_GECOS,
402 "homedir": home,
403 "shell": "/bin/bash",
404 }
405 )
Mike Frysinger31fdddd2023-02-24 15:50:55 -0500406 with open(passwd_db, "a", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -0600407 if data[-1] != "\n":
408 f.write("\n")
409 f.write("%s\n" % acct)
410
411 def _FindRemoteProcess(self, device):
412 """Find a named process (or a pid) running on a remote device."""
413 if not self.remote_process_name and not self.pid:
414 return
415
416 if self.remote_process_name:
Alex Klein8b444532023-04-11 16:35:24 -0600417 # Look for a process with the specified name on the remote device;
418 # if found, get its pid.
Alex Klein1699fab2022-09-08 08:46:06 -0600419 pname = self.remote_process_name
420 if pname == "browser":
421 all_chrome_pids = set(
422 device.GetRunningPids("/opt/google/chrome/chrome")
423 )
424 non_main_chrome_pids = set(device.GetRunningPids("type="))
425 pids = list(all_chrome_pids - non_main_chrome_pids)
Alex Klein64930532023-04-17 12:20:52 -0600426 elif pname in ("renderer", "gpu-process"):
Alex Klein1699fab2022-09-08 08:46:06 -0600427 pids = device.GetRunningPids("type=%s" % pname)
428 else:
429 pids = device.GetRunningPids(pname)
430
431 if pids:
432 if len(pids) == 1:
433 self.pid = pids[0]
434 else:
435 raise GdbTooManyPidsError(
436 "Multiple pids found for %s process: %s. "
437 "You must specify the correct pid."
438 % (pname, repr(pids))
439 )
440 else:
441 raise GdbCannotFindRemoteProcessError(
442 'Cannot find pid for "%s" on %s' % (pname, self.remote)
443 )
444
445 # Find full path for process, from pid (and verify pid).
446 command = [
447 "readlink",
448 "-e",
449 "/proc/%s/exe" % self.pid,
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700450 ]
Alex Klein1699fab2022-09-08 08:46:06 -0600451 try:
452 res = device.run(command, capture_output=True)
453 if res.returncode == 0:
454 self.inf_cmd = res.stdout.rstrip("\n")
455 except cros_build_lib.RunCommandError:
456 raise GdbCannotFindRemoteProcessError(
457 "Unable to find name of process "
458 "with pid %s on %s" % (self.pid, self.remote)
459 )
Raul E Rangel746c45d2018-05-09 09:27:31 -0600460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 def GetCrossGdb(self):
462 """Find the appropriate cross-version of gdb for the board."""
463 toolchains = toolchain.GetToolchainsForBoard(self.board)
464 tc = list(toolchain.FilterToolchains(toolchains, "default", True))
465 cross_gdb = tc[0] + "-gdb"
466 if not osutils.Which(cross_gdb):
467 raise GdbMissingDebuggerError(
468 "Cannot find %s; do you need to run " "setup_board?" % cross_gdb
469 )
470 return cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 def GetGdbInitCommands(self, inferior_cmd, device=None):
Alex Klein8b444532023-04-11 16:35:24 -0600473 """Generate list of commands with which to initialize a gdb session."""
Alex Klein1699fab2022-09-08 08:46:06 -0600474 gdb_init_commands = []
cmticef23cb132015-04-10 15:13:00 -0700475
Alex Klein1699fab2022-09-08 08:46:06 -0600476 if self.remote:
477 sysroot_var = self.sysroot
478 else:
479 sysroot_var = "/"
cmticef23cb132015-04-10 15:13:00 -0700480
Alex Klein1699fab2022-09-08 08:46:06 -0600481 gdb_init_commands = [
482 "set sysroot %s" % sysroot_var,
483 "set prompt %s" % self.prompt,
484 ]
485 if self.in_chroot:
486 gdb_init_commands += [
487 "set solib-absolute-prefix %s" % sysroot_var,
488 "set solib-search-path %s" % sysroot_var,
489 "set debug-file-directory %s/usr/lib/debug" % sysroot_var,
490 ]
cmticef23cb132015-04-10 15:13:00 -0700491
Alex Klein1699fab2022-09-08 08:46:06 -0600492 if device:
Mike Frysingerc0780a62022-08-29 04:41:56 -0400493 ssh_cmd = device.agent.GetSSHCommand(self.ssh_settings)
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600494
Alex Klein1699fab2022-09-08 08:46:06 -0600495 ssh_cmd.extend(["--", "gdbserver"])
cmticef23cb132015-04-10 15:13:00 -0700496
Alex Klein1699fab2022-09-08 08:46:06 -0600497 if self.pid:
498 ssh_cmd.extend(["--attach", "stdio", str(self.pid)])
499 target_type = "remote"
500 elif inferior_cmd:
501 ssh_cmd.extend(["-", inferior_cmd])
502 ssh_cmd.extend(self.inf_args)
503 target_type = "remote"
504 else:
505 ssh_cmd.extend(["--multi", "stdio"])
506 target_type = "extended-remote"
cmticef23cb132015-04-10 15:13:00 -0700507
Alex Klein1699fab2022-09-08 08:46:06 -0600508 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 if self.in_chroot:
511 if inferior_cmd:
512 gdb_init_commands.append(
513 "file %s"
514 % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep))
515 )
516 else:
517 binary = self.GetSimpleChromeBinary()
518 gdb_init_commands += [
519 "set debug-file-directory %s" % os.path.dirname(binary),
520 "file %s" % binary,
521 ]
cmtice932e0aa2015-02-27 11:49:12 -0800522
Alex Klein1699fab2022-09-08 08:46:06 -0600523 gdb_init_commands.append("target %s | %s" % (target_type, ssh_cmd))
524 else:
525 if inferior_cmd:
526 gdb_init_commands.append("file %s " % inferior_cmd)
527 gdb_init_commands.append(
528 "set args %s" % " ".join(self.inf_args)
529 )
cmticeb70801a2014-12-11 14:29:34 -0800530
Alex Klein1699fab2022-09-08 08:46:06 -0600531 return gdb_init_commands
cmticef23cb132015-04-10 15:13:00 -0700532
Alex Klein1699fab2022-09-08 08:46:06 -0600533 def RunRemote(self):
534 """Handle remote debugging, via gdbserver & cross debugger."""
535 with remote_access.ChromiumOSDeviceHandler(
536 self.remote,
537 port=self.remote_port,
538 connect_settings=self.ssh_settings,
539 ping=self.ping,
540 ) as device:
Alex Klein1699fab2022-09-08 08:46:06 -0600541 self.VerifyAndFinishInitialization(device)
542 gdb_cmd = self.cross_gdb
cmticeb70801a2014-12-11 14:29:34 -0800543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
545 gdb_args = ["--quiet"] + [
546 "--eval-command=%s" % x for x in gdb_commands
547 ]
548 gdb_args += self.gdb_args
cmticeb70801a2014-12-11 14:29:34 -0800549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 if self.cgdb:
551 gdb_args = ["-d", gdb_cmd, "--"] + gdb_args
552 gdb_cmd = "cgdb"
cmtice932e0aa2015-02-27 11:49:12 -0800553
Alex Klein1699fab2022-09-08 08:46:06 -0600554 cros_build_lib.run(
555 [gdb_cmd] + gdb_args,
556 ignore_sigint=True,
557 print_cmd=True,
558 cwd=self.sysroot,
559 )
560
561 def Run(self):
562 """Runs the debugger in a proper environment (e.g. qemu)."""
563
564 self.VerifyAndFinishInitialization(None)
565 self.SetupUser()
566 if self.framework == "qemu":
567 self.qemu.Install(self.sysroot)
568 self.qemu.RegisterBinfmt()
569
570 for mount in self._BIND_MOUNT_PATHS:
571 path = os.path.join(self.sysroot, mount)
572 osutils.SafeMakedirs(path)
573 osutils.Mount("/" + mount, path, "none", osutils.MS_BIND)
574
575 gdb_cmd = self._GDB
576 inferior_cmd = self.inf_cmd
577
578 gdb_argv = self.gdb_args[:]
579 if gdb_argv:
580 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
581 # Some programs expect to find data files via $CWD, so doing a chroot
582 # and dropping them into / would make them fail.
583 cwd = self.RemoveSysrootPrefix(os.getcwd())
584
585 os.chroot(self.sysroot)
586 os.chdir(cwd)
587 # The TERM the user is leveraging might not exist in the sysroot.
588 # Force a reasonable default that supports standard color sequences.
589 os.environ["TERM"] = "ansi"
590 # Some progs want this like bash else they get super confused.
591 os.environ["PWD"] = cwd
592 if not self.run_as_root:
593 _, uid, gid, home = self.GetNonRootAccount()
594 os.setgid(gid)
595 os.setuid(uid)
596 os.environ["HOME"] = home
597
598 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
599
600 gdb_args = [gdb_cmd, "--quiet"] + [
601 "--eval-command=%s" % x for x in gdb_commands
602 ]
603 gdb_args += self.gdb_args
604
605 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800606
607
cmtice932e0aa2015-02-27 11:49:12 -0800608def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600609 """Re-execute gdb as root.
cmticeb70801a2014-12-11 14:29:34 -0800610
Alex Klein1699fab2022-09-08 08:46:06 -0600611 We often need to do things as root, so make sure we're that. Like chroot
612 for proper library environment or do bind mounts.
cmticeb70801a2014-12-11 14:29:34 -0800613
Alex Klein1699fab2022-09-08 08:46:06 -0600614 Also unshare the mount namespace so as to ensure that doing bind mounts for
615 tests don't leak out to the normal chroot. Also unshare the UTS namespace
616 so changes to `hostname` do not impact the host.
617 """
618 if osutils.IsNonRootUser():
619 cmd = ["sudo", "-E", "--"] + argv
620 os.execvp(cmd[0], cmd)
621 else:
622 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
cmtice932e0aa2015-02-27 11:49:12 -0800623
624
cmticef23cb132015-04-10 15:13:00 -0700625def FindInferior(arg_list):
Alex Klein1699fab2022-09-08 08:46:06 -0600626 """Look for the name of the inferior (to be debugged) in arg list."""
cmtice932e0aa2015-02-27 11:49:12 -0800627
Alex Klein1699fab2022-09-08 08:46:06 -0600628 program_name = ""
629 new_list = []
630 for item in arg_list:
631 if item[0] == "-":
632 new_list.append(item)
633 elif not program_name:
634 program_name = item
635 else:
636 raise RuntimeError(
637 "Found multiple program names: %s %s" % (program_name, item)
638 )
cmtice932e0aa2015-02-27 11:49:12 -0800639
Alex Klein1699fab2022-09-08 08:46:06 -0600640 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800641
642
643def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600644 parser = commandline.ArgumentParser(description=__doc__)
cmticeb70801a2014-12-11 14:29:34 -0800645
Alex Klein1699fab2022-09-08 08:46:06 -0600646 parser.add_argument("--board", default=None, help="board to debug for")
647 parser.add_argument(
Marc Grimme5441aba2023-04-14 16:56:36 +0200648 "--gdb",
649 type="file_exists",
650 help="Path to gdb binary. Default is that gdb binary is auto detected.",
651 )
652 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600653 "-g",
Mike Frysinger3152f6b2023-04-26 23:47:48 -0400654 "--gdb-args",
Alex Klein1699fab2022-09-08 08:46:06 -0600655 action="append",
656 default=[],
657 help="Arguments to gdb itself. If multiple arguments are"
658 " passed, each argument needs a separate '-g' flag.",
659 )
Mike Frysinger3152f6b2023-04-26 23:47:48 -0400660 # TODO(build): Delete by Jan 2024.
661 parser.add_argument(
662 "--gdb_args",
663 action="append",
664 deprecated="Use --gdb-args instead",
665 help=argparse.SUPPRESS,
666 )
Alex Klein1699fab2022-09-08 08:46:06 -0600667 parser.add_argument(
668 "--remote",
669 default=None,
670 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
671 help="Remote device on which to run the binary. Use"
672 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
673 " already running local virtual machine.",
674 )
675 parser.add_argument(
676 "--pid",
677 default="",
678 help="Process ID of the (already) running process on the"
679 " remote device to which to attach.",
680 )
Mike Frysinger5c702782023-04-17 16:20:24 -0400681 # TODO(build): Delete in Jan 2024.
Alex Klein1699fab2022-09-08 08:46:06 -0600682 parser.add_argument(
683 "--remote_pid",
684 dest="pid",
Mike Frysinger5c702782023-04-17 16:20:24 -0400685 help=argparse.SUPPRESS,
686 deprecated="Use --pid instead.",
Alex Klein1699fab2022-09-08 08:46:06 -0600687 )
688 parser.add_argument(
689 "--no-ping",
690 dest="ping",
691 default=True,
692 action="store_false",
693 help="Do not ping remote before attempting to connect.",
694 )
695 parser.add_argument(
696 "--attach",
697 dest="attach_name",
698 default="",
699 help="Name of existing process to which to attach, on"
700 ' remote device (remote debugging only). "--attach'
701 ' browser" will find the main chrome browser process;'
702 ' "--attach renderer" will find a chrome renderer'
703 ' process; "--attach gpu-process" will find the chrome'
704 " gpu process.",
705 )
706 parser.add_argument(
707 "--cgdb",
708 default=False,
709 action="store_true",
710 help="Use cgdb curses interface rather than plain gdb."
711 "This option is only valid for remote debugging.",
712 )
713 parser.add_argument(
714 "inf_args",
715 nargs=argparse.REMAINDER,
716 help="Arguments for gdb to pass to the program being"
717 " debugged. These are positional and must come at the end"
718 " of the command line. This will not work if attaching"
719 " to an already running program.",
720 )
721 parser.add_argument(
722 "--binary",
723 default="",
Brian Norriscc3331c2022-04-22 13:52:00 -0700724 help="full path to the binary being debugged."
Alex Klein1699fab2022-09-08 08:46:06 -0600725 " This is only useful for simple chrome."
Brian Norriscc3331c2022-04-22 13:52:00 -0700726 " An example is --binary /home/out_falco/chrome.",
Alex Klein1699fab2022-09-08 08:46:06 -0600727 )
cmticeb70801a2014-12-11 14:29:34 -0800728
Alex Klein1699fab2022-09-08 08:46:06 -0600729 options = parser.parse_args(argv)
730 options.Freeze()
cmticeb70801a2014-12-11 14:29:34 -0800731
Alex Klein1699fab2022-09-08 08:46:06 -0600732 gdb_args = []
733 inf_args = []
734 inf_cmd = ""
cmtice932e0aa2015-02-27 11:49:12 -0800735
Alex Klein1699fab2022-09-08 08:46:06 -0600736 if options.inf_args:
737 inf_cmd = options.inf_args[0]
738 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800739
Alex Klein1699fab2022-09-08 08:46:06 -0600740 if options.gdb_args:
741 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800742
Alex Klein1699fab2022-09-08 08:46:06 -0600743 if inf_cmd:
744 fname = os.path.join(
745 build_target_lib.get_default_sysroot_path(options.board),
746 inf_cmd.lstrip("/"),
747 )
748 if not os.path.exists(fname):
Alex Kleindf8ee502022-10-18 09:48:15 -0600749 cros_build_lib.Die("Cannot find program %s.", fname)
Alex Klein1699fab2022-09-08 08:46:06 -0600750 else:
751 if inf_args:
752 parser.error("Cannot specify arguments without a program.")
cmtice932e0aa2015-02-27 11:49:12 -0800753
Alex Klein1699fab2022-09-08 08:46:06 -0600754 if inf_args and (options.pid or options.attach_name):
755 parser.error(
756 "Cannot pass arguments to an already"
757 " running process (--remote-pid or --attach)."
758 )
cmticef23cb132015-04-10 15:13:00 -0700759
cmticef23cb132015-04-10 15:13:00 -0700760 if options.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600761 if options.attach_name and options.attach_name == "browser":
762 inf_cmd = "/opt/google/chrome/chrome"
cmticef23cb132015-04-10 15:13:00 -0700763 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600764 if options.cgdb:
765 parser.error(
766 "--cgdb option can only be used with remote debugging."
767 )
768 if options.pid:
769 parser.error(
770 "Must specify a remote device (--remote) if you want "
771 "to attach to a remote pid."
772 )
773 if options.attach_name:
774 parser.error(
775 "Must specify remote device (--remote) when using"
776 " --attach option."
777 )
778 if options.binary:
779 if not os.path.exists(options.binary):
780 parser.error("%s does not exist." % options.binary)
cmticef23cb132015-04-10 15:13:00 -0700781
Alex Klein1699fab2022-09-08 08:46:06 -0600782 # Once we've finished checking args, make sure we're root.
783 if not options.remote:
784 _ReExecuteIfNeeded([sys.argv[0]] + argv)
785
786 gdb = BoardSpecificGdb(
787 options.board,
788 gdb_args,
789 inf_cmd,
790 inf_args,
791 options.remote,
792 options.pid,
793 options.attach_name,
794 options.cgdb,
795 options.ping,
796 options.binary,
Marc Grimme5441aba2023-04-14 16:56:36 +0200797 options.gdb,
Alex Klein1699fab2022-09-08 08:46:06 -0600798 )
799
800 try:
801 if options.remote:
802 gdb.RunRemote()
803 else:
804 gdb.Run()
805
806 except GdbException as e:
807 if options.debug:
808 raise
809 else:
810 raise cros_build_lib.Die(str(e))