blob: 9ea800bbdf426ad6d6ff511b03b8754733422488 [file] [log] [blame]
cmticeb70801a2014-12-11 14:29:34 -08001# Copyright 2014 The Chromium OS Authors. All rights reserved.
2# 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:
cmticef23cb132015-04-10 15:13:00 -0700225
Alex Klein1699fab2022-09-08 08:46:06 -0600226 # If given remote process name, find pid & inf_cmd on remote device.
227 if self.remote_process_name or self.pid:
228 self._FindRemoteProcess(device)
cmticef23cb132015-04-10 15:13:00 -0700229
Alex Klein1699fab2022-09-08 08:46:06 -0600230 # Verify that sysroot is valid (exists).
231 if not os.path.isdir(self.sysroot):
232 raise GdbMissingSysrootError(
233 "Sysroot does not exist: %s" % self.sysroot
234 )
cmticef23cb132015-04-10 15:13:00 -0700235
Alex Klein1699fab2022-09-08 08:46:06 -0600236 self.device = device
237 if not self.in_chroot:
cmticeb70801a2014-12-11 14:29:34 -0800238 return
239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 sysroot_inf_cmd = ""
241 if self.inf_cmd:
242 sysroot_inf_cmd = os.path.join(
243 self.sysroot, self.inf_cmd.lstrip("/")
244 )
cmticeb70801a2014-12-11 14:29:34 -0800245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 # Verify that inf_cmd, if given, exists.
247 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
248 raise GdbMissingInferiorError(
249 "Cannot find file %s (in sysroot)." % sysroot_inf_cmd
250 )
cmticeb70801a2014-12-11 14:29:34 -0800251
Alex Klein1699fab2022-09-08 08:46:06 -0600252 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
253 # exists. If not, tell user and give them the option of quitting & getting
254 # the debug info.
255 if sysroot_inf_cmd:
256 stripped_info = cros_build_lib.run(
257 ["file", sysroot_inf_cmd], capture_output=True, encoding="utf-8"
258 ).stdout
259 if " not stripped" not in stripped_info:
260 debug_file = os.path.join(
261 self.sysroot, "usr/lib/debug", self.inf_cmd.lstrip("/")
262 )
263 debug_file += ".debug"
264 if not os.path.exists(debug_file):
265 equery = "equery-%s" % self.board
266 package = cros_build_lib.run(
267 [equery, "-q", "b", self.inf_cmd],
268 capture_output=True,
269 encoding="utf-8",
270 ).stdout
271 # pylint: disable=logging-not-lazy
272 logging.info(
273 self._MISSING_DEBUG_INFO_MSG
274 % {
275 "board": self.board,
276 "inf_cmd": self.inf_cmd,
277 "package": package,
278 "debug_file": debug_file,
279 }
280 )
281 answer = cros_build_lib.BooleanPrompt()
282 if not answer:
283 raise GdbEarlyExitError(
284 "Exiting early, at user request."
285 )
cmticeb70801a2014-12-11 14:29:34 -0800286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 # Set up qemu, if appropriate.
288 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
289 if qemu_arch is None:
290 self.framework = "ldso"
cmticef23cb132015-04-10 15:13:00 -0700291 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600292 self.framework = "qemu"
293 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticef23cb132015-04-10 15:13:00 -0700294
Alex Klein1699fab2022-09-08 08:46:06 -0600295 if self.remote:
296 # Verify cgdb flag info.
297 if self.cgdb:
298 if osutils.Which("cgdb") is None:
299 raise GdbMissingDebuggerError(
300 "Cannot find cgdb. Please install " "cgdb first."
301 )
cmticef23cb132015-04-10 15:13:00 -0700302
Alex Klein1699fab2022-09-08 08:46:06 -0600303 def RemoveSysrootPrefix(self, path):
304 """Returns the given path with any sysroot prefix removed."""
305 # If the sysroot is /, then the paths are already normalized.
306 if self.sysroot != "/" and path.startswith(self.sysroot):
307 path = path.replace(self.sysroot, "", 1)
308 return path
cmticef23cb132015-04-10 15:13:00 -0700309
Alex Klein1699fab2022-09-08 08:46:06 -0600310 @staticmethod
311 def GetNonRootAccount():
312 """Return details about the non-root account we want to use.
cmticef23cb132015-04-10 15:13:00 -0700313
Alex Klein1699fab2022-09-08 08:46:06 -0600314 Returns:
315 A tuple of (username, uid, gid, home).
316 """
317 return (
318 os.environ.get("SUDO_USER", "nobody"),
319 int(os.environ.get("SUDO_UID", "65534")),
320 int(os.environ.get("SUDO_GID", "65534")),
321 # Should we find a better home?
322 "/tmp/portage",
323 )
cmticef23cb132015-04-10 15:13:00 -0700324
Alex Klein1699fab2022-09-08 08:46:06 -0600325 @staticmethod
326 @contextlib.contextmanager
327 def LockDb(db):
328 """Lock an account database.
cmticef23cb132015-04-10 15:13:00 -0700329
Alex Klein1699fab2022-09-08 08:46:06 -0600330 We use the same algorithm as shadow/user.eclass. This way we don't race
331 and corrupt things in parallel.
332 """
333 lock = "%s.lock" % db
334 _, tmplock = tempfile.mkstemp(prefix="%s.platform." % lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600335
Alex Klein1699fab2022-09-08 08:46:06 -0600336 # First try forever to grab the lock.
337 retry = lambda e: e.errno == errno.EEXIST
338 # Retry quickly at first, but slow down over time.
339 try:
340 retry_util.GenericRetry(
341 retry, 60, os.link, tmplock, lock, sleep=0.1
342 )
343 except Exception as e:
344 raise Exception("Could not grab lock %s. %s" % (lock, e))
Raul E Rangel746c45d2018-05-09 09:27:31 -0600345
Alex Klein1699fab2022-09-08 08:46:06 -0600346 # Yield while holding the lock, but try to clean it no matter what.
347 try:
348 os.unlink(tmplock)
349 yield lock
350 finally:
351 os.unlink(lock)
Raul E Rangel746c45d2018-05-09 09:27:31 -0600352
Alex Klein1699fab2022-09-08 08:46:06 -0600353 def SetupUser(self):
354 """Propogate the user name<->id mapping from outside the chroot.
cmticef23cb132015-04-10 15:13:00 -0700355
Alex Klein1699fab2022-09-08 08:46:06 -0600356 Some unittests use getpwnam($USER), as does bash. If the account
357 is not registered in the sysroot, they get back errors.
358 """
359 MAGIC_GECOS = (
360 "Added by your friendly platform test helper; do not modify"
361 )
362 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
363 SDK_GECOS = "ChromeOS Developer"
364
365 user, uid, gid, home = self.GetNonRootAccount()
366 if user == "nobody":
367 return
368
369 passwd_db = os.path.join(self.sysroot, "etc", "passwd")
370 with self.LockDb(passwd_db):
371 data = osutils.ReadFile(passwd_db)
372 accts = data.splitlines()
373 for acct in accts:
374 passwd = acct.split(":")
375 if passwd[0] == user:
376 # Did the sdk make this account?
377 if passwd[4] == SDK_GECOS:
378 # Don't modify it (see below) since we didn't create it.
379 return
380
381 # Did we make this account?
382 if passwd[4] != MAGIC_GECOS:
383 raise RuntimeError(
384 "your passwd db (%s) has unmanaged acct %s"
385 % (passwd_db, user)
386 )
387
388 # Maybe we should see if it needs to be updated? Like if they
389 # changed UIDs? But we don't really check that elsewhere ...
390 return
391
392 acct = (
393 "%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s"
394 % {
395 "name": user,
396 "uid": uid,
397 "gid": gid,
398 "gecos": MAGIC_GECOS,
399 "homedir": home,
400 "shell": "/bin/bash",
401 }
402 )
403 with open(passwd_db, "a") as f:
404 if data[-1] != "\n":
405 f.write("\n")
406 f.write("%s\n" % acct)
407
408 def _FindRemoteProcess(self, device):
409 """Find a named process (or a pid) running on a remote device."""
410 if not self.remote_process_name and not self.pid:
411 return
412
413 if self.remote_process_name:
414 # Look for a process with the specified name on the remote device; if
415 # found, get its pid.
416 pname = self.remote_process_name
417 if pname == "browser":
418 all_chrome_pids = set(
419 device.GetRunningPids("/opt/google/chrome/chrome")
420 )
421 non_main_chrome_pids = set(device.GetRunningPids("type="))
422 pids = list(all_chrome_pids - non_main_chrome_pids)
423 elif pname == "renderer" or pname == "gpu-process":
424 pids = device.GetRunningPids("type=%s" % pname)
425 else:
426 pids = device.GetRunningPids(pname)
427
428 if pids:
429 if len(pids) == 1:
430 self.pid = pids[0]
431 else:
432 raise GdbTooManyPidsError(
433 "Multiple pids found for %s process: %s. "
434 "You must specify the correct pid."
435 % (pname, repr(pids))
436 )
437 else:
438 raise GdbCannotFindRemoteProcessError(
439 'Cannot find pid for "%s" on %s' % (pname, self.remote)
440 )
441
442 # Find full path for process, from pid (and verify pid).
443 command = [
444 "readlink",
445 "-e",
446 "/proc/%s/exe" % self.pid,
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700447 ]
Alex Klein1699fab2022-09-08 08:46:06 -0600448 try:
449 res = device.run(command, capture_output=True)
450 if res.returncode == 0:
451 self.inf_cmd = res.stdout.rstrip("\n")
452 except cros_build_lib.RunCommandError:
453 raise GdbCannotFindRemoteProcessError(
454 "Unable to find name of process "
455 "with pid %s on %s" % (self.pid, self.remote)
456 )
Raul E Rangel746c45d2018-05-09 09:27:31 -0600457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 def GetCrossGdb(self):
459 """Find the appropriate cross-version of gdb for the board."""
460 toolchains = toolchain.GetToolchainsForBoard(self.board)
461 tc = list(toolchain.FilterToolchains(toolchains, "default", True))
462 cross_gdb = tc[0] + "-gdb"
463 if not osutils.Which(cross_gdb):
464 raise GdbMissingDebuggerError(
465 "Cannot find %s; do you need to run " "setup_board?" % cross_gdb
466 )
467 return cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700468
Alex Klein1699fab2022-09-08 08:46:06 -0600469 def GetGdbInitCommands(self, inferior_cmd, device=None):
470 """Generate list of commands with which to initialize the gdb session."""
471 gdb_init_commands = []
cmticef23cb132015-04-10 15:13:00 -0700472
Alex Klein1699fab2022-09-08 08:46:06 -0600473 if self.remote:
474 sysroot_var = self.sysroot
475 else:
476 sysroot_var = "/"
cmticef23cb132015-04-10 15:13:00 -0700477
Alex Klein1699fab2022-09-08 08:46:06 -0600478 gdb_init_commands = [
479 "set sysroot %s" % sysroot_var,
480 "set prompt %s" % self.prompt,
481 ]
482 if self.in_chroot:
483 gdb_init_commands += [
484 "set solib-absolute-prefix %s" % sysroot_var,
485 "set solib-search-path %s" % sysroot_var,
486 "set debug-file-directory %s/usr/lib/debug" % sysroot_var,
487 ]
cmticef23cb132015-04-10 15:13:00 -0700488
Alex Klein1699fab2022-09-08 08:46:06 -0600489 if device:
490 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600491
Alex Klein1699fab2022-09-08 08:46:06 -0600492 ssh_cmd.extend(["--", "gdbserver"])
cmticef23cb132015-04-10 15:13:00 -0700493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 if self.pid:
495 ssh_cmd.extend(["--attach", "stdio", str(self.pid)])
496 target_type = "remote"
497 elif inferior_cmd:
498 ssh_cmd.extend(["-", inferior_cmd])
499 ssh_cmd.extend(self.inf_args)
500 target_type = "remote"
501 else:
502 ssh_cmd.extend(["--multi", "stdio"])
503 target_type = "extended-remote"
cmticef23cb132015-04-10 15:13:00 -0700504
Alex Klein1699fab2022-09-08 08:46:06 -0600505 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700506
Alex Klein1699fab2022-09-08 08:46:06 -0600507 if self.in_chroot:
508 if inferior_cmd:
509 gdb_init_commands.append(
510 "file %s"
511 % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep))
512 )
513 else:
514 binary = self.GetSimpleChromeBinary()
515 gdb_init_commands += [
516 "set debug-file-directory %s" % os.path.dirname(binary),
517 "file %s" % binary,
518 ]
cmtice932e0aa2015-02-27 11:49:12 -0800519
Alex Klein1699fab2022-09-08 08:46:06 -0600520 gdb_init_commands.append("target %s | %s" % (target_type, ssh_cmd))
521 else:
522 if inferior_cmd:
523 gdb_init_commands.append("file %s " % inferior_cmd)
524 gdb_init_commands.append(
525 "set args %s" % " ".join(self.inf_args)
526 )
cmticeb70801a2014-12-11 14:29:34 -0800527
Alex Klein1699fab2022-09-08 08:46:06 -0600528 return gdb_init_commands
cmticef23cb132015-04-10 15:13:00 -0700529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 def RunRemote(self):
531 """Handle remote debugging, via gdbserver & cross debugger."""
532 with remote_access.ChromiumOSDeviceHandler(
533 self.remote,
534 port=self.remote_port,
535 connect_settings=self.ssh_settings,
536 ping=self.ping,
537 ) as device:
cmticeb70801a2014-12-11 14:29:34 -0800538
Alex Klein1699fab2022-09-08 08:46:06 -0600539 self.VerifyAndFinishInitialization(device)
540 gdb_cmd = self.cross_gdb
cmticeb70801a2014-12-11 14:29:34 -0800541
Alex Klein1699fab2022-09-08 08:46:06 -0600542 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
543 gdb_args = ["--quiet"] + [
544 "--eval-command=%s" % x for x in gdb_commands
545 ]
546 gdb_args += self.gdb_args
cmticeb70801a2014-12-11 14:29:34 -0800547
Alex Klein1699fab2022-09-08 08:46:06 -0600548 if self.cgdb:
549 gdb_args = ["-d", gdb_cmd, "--"] + gdb_args
550 gdb_cmd = "cgdb"
cmtice932e0aa2015-02-27 11:49:12 -0800551
Alex Klein1699fab2022-09-08 08:46:06 -0600552 cros_build_lib.run(
553 [gdb_cmd] + gdb_args,
554 ignore_sigint=True,
555 print_cmd=True,
556 cwd=self.sysroot,
557 )
558
559 def Run(self):
560 """Runs the debugger in a proper environment (e.g. qemu)."""
561
562 self.VerifyAndFinishInitialization(None)
563 self.SetupUser()
564 if self.framework == "qemu":
565 self.qemu.Install(self.sysroot)
566 self.qemu.RegisterBinfmt()
567
568 for mount in self._BIND_MOUNT_PATHS:
569 path = os.path.join(self.sysroot, mount)
570 osutils.SafeMakedirs(path)
571 osutils.Mount("/" + mount, path, "none", osutils.MS_BIND)
572
573 gdb_cmd = self._GDB
574 inferior_cmd = self.inf_cmd
575
576 gdb_argv = self.gdb_args[:]
577 if gdb_argv:
578 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
579 # Some programs expect to find data files via $CWD, so doing a chroot
580 # and dropping them into / would make them fail.
581 cwd = self.RemoveSysrootPrefix(os.getcwd())
582
583 os.chroot(self.sysroot)
584 os.chdir(cwd)
585 # The TERM the user is leveraging might not exist in the sysroot.
586 # Force a reasonable default that supports standard color sequences.
587 os.environ["TERM"] = "ansi"
588 # Some progs want this like bash else they get super confused.
589 os.environ["PWD"] = cwd
590 if not self.run_as_root:
591 _, uid, gid, home = self.GetNonRootAccount()
592 os.setgid(gid)
593 os.setuid(uid)
594 os.environ["HOME"] = home
595
596 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
597
598 gdb_args = [gdb_cmd, "--quiet"] + [
599 "--eval-command=%s" % x for x in gdb_commands
600 ]
601 gdb_args += self.gdb_args
602
603 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800604
605
cmtice932e0aa2015-02-27 11:49:12 -0800606def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600607 """Re-execute gdb as root.
cmticeb70801a2014-12-11 14:29:34 -0800608
Alex Klein1699fab2022-09-08 08:46:06 -0600609 We often need to do things as root, so make sure we're that. Like chroot
610 for proper library environment or do bind mounts.
cmticeb70801a2014-12-11 14:29:34 -0800611
Alex Klein1699fab2022-09-08 08:46:06 -0600612 Also unshare the mount namespace so as to ensure that doing bind mounts for
613 tests don't leak out to the normal chroot. Also unshare the UTS namespace
614 so changes to `hostname` do not impact the host.
615 """
616 if osutils.IsNonRootUser():
617 cmd = ["sudo", "-E", "--"] + argv
618 os.execvp(cmd[0], cmd)
619 else:
620 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
cmtice932e0aa2015-02-27 11:49:12 -0800621
622
cmticef23cb132015-04-10 15:13:00 -0700623def FindInferior(arg_list):
Alex Klein1699fab2022-09-08 08:46:06 -0600624 """Look for the name of the inferior (to be debugged) in arg list."""
cmtice932e0aa2015-02-27 11:49:12 -0800625
Alex Klein1699fab2022-09-08 08:46:06 -0600626 program_name = ""
627 new_list = []
628 for item in arg_list:
629 if item[0] == "-":
630 new_list.append(item)
631 elif not program_name:
632 program_name = item
633 else:
634 raise RuntimeError(
635 "Found multiple program names: %s %s" % (program_name, item)
636 )
cmtice932e0aa2015-02-27 11:49:12 -0800637
Alex Klein1699fab2022-09-08 08:46:06 -0600638 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800639
640
641def main(argv):
642
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(
647 "-g",
648 "--gdb_args",
649 action="append",
650 default=[],
651 help="Arguments to gdb itself. If multiple arguments are"
652 " passed, each argument needs a separate '-g' flag.",
653 )
654 parser.add_argument(
655 "--remote",
656 default=None,
657 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
658 help="Remote device on which to run the binary. Use"
659 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
660 " already running local virtual machine.",
661 )
662 parser.add_argument(
663 "--pid",
664 default="",
665 help="Process ID of the (already) running process on the"
666 " remote device to which to attach.",
667 )
668 parser.add_argument(
669 "--remote_pid",
670 dest="pid",
671 default="",
672 help="Deprecated alias for --pid.",
673 )
674 parser.add_argument(
675 "--no-ping",
676 dest="ping",
677 default=True,
678 action="store_false",
679 help="Do not ping remote before attempting to connect.",
680 )
681 parser.add_argument(
682 "--attach",
683 dest="attach_name",
684 default="",
685 help="Name of existing process to which to attach, on"
686 ' remote device (remote debugging only). "--attach'
687 ' browser" will find the main chrome browser process;'
688 ' "--attach renderer" will find a chrome renderer'
689 ' process; "--attach gpu-process" will find the chrome'
690 " gpu process.",
691 )
692 parser.add_argument(
693 "--cgdb",
694 default=False,
695 action="store_true",
696 help="Use cgdb curses interface rather than plain gdb."
697 "This option is only valid for remote debugging.",
698 )
699 parser.add_argument(
700 "inf_args",
701 nargs=argparse.REMAINDER,
702 help="Arguments for gdb to pass to the program being"
703 " debugged. These are positional and must come at the end"
704 " of the command line. This will not work if attaching"
705 " to an already running program.",
706 )
707 parser.add_argument(
708 "--binary",
709 default="",
710 help="full path to the binary being debuged."
711 " This is only useful for simple chrome."
712 " An example is --bianry /home/out_falco/chrome.",
713 )
cmticeb70801a2014-12-11 14:29:34 -0800714
Alex Klein1699fab2022-09-08 08:46:06 -0600715 options = parser.parse_args(argv)
716 options.Freeze()
cmticeb70801a2014-12-11 14:29:34 -0800717
Alex Klein1699fab2022-09-08 08:46:06 -0600718 gdb_args = []
719 inf_args = []
720 inf_cmd = ""
cmtice932e0aa2015-02-27 11:49:12 -0800721
Alex Klein1699fab2022-09-08 08:46:06 -0600722 if options.inf_args:
723 inf_cmd = options.inf_args[0]
724 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 if options.gdb_args:
727 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800728
Alex Klein1699fab2022-09-08 08:46:06 -0600729 if inf_cmd:
730 fname = os.path.join(
731 build_target_lib.get_default_sysroot_path(options.board),
732 inf_cmd.lstrip("/"),
733 )
734 if not os.path.exists(fname):
735 cros_build_lib.Die("Cannot find program %s." % fname)
736 else:
737 if inf_args:
738 parser.error("Cannot specify arguments without a program.")
cmtice932e0aa2015-02-27 11:49:12 -0800739
Alex Klein1699fab2022-09-08 08:46:06 -0600740 if inf_args and (options.pid or options.attach_name):
741 parser.error(
742 "Cannot pass arguments to an already"
743 " running process (--remote-pid or --attach)."
744 )
cmticef23cb132015-04-10 15:13:00 -0700745
cmticef23cb132015-04-10 15:13:00 -0700746 if options.remote:
Alex Klein1699fab2022-09-08 08:46:06 -0600747 if options.attach_name and options.attach_name == "browser":
748 inf_cmd = "/opt/google/chrome/chrome"
cmticef23cb132015-04-10 15:13:00 -0700749 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600750 if options.cgdb:
751 parser.error(
752 "--cgdb option can only be used with remote debugging."
753 )
754 if options.pid:
755 parser.error(
756 "Must specify a remote device (--remote) if you want "
757 "to attach to a remote pid."
758 )
759 if options.attach_name:
760 parser.error(
761 "Must specify remote device (--remote) when using"
762 " --attach option."
763 )
764 if options.binary:
765 if not os.path.exists(options.binary):
766 parser.error("%s does not exist." % options.binary)
cmticef23cb132015-04-10 15:13:00 -0700767
Alex Klein1699fab2022-09-08 08:46:06 -0600768 # Once we've finished checking args, make sure we're root.
769 if not options.remote:
770 _ReExecuteIfNeeded([sys.argv[0]] + argv)
771
772 gdb = BoardSpecificGdb(
773 options.board,
774 gdb_args,
775 inf_cmd,
776 inf_args,
777 options.remote,
778 options.pid,
779 options.attach_name,
780 options.cgdb,
781 options.ping,
782 options.binary,
783 )
784
785 try:
786 if options.remote:
787 gdb.RunRemote()
788 else:
789 gdb.Run()
790
791 except GdbException as e:
792 if options.debug:
793 raise
794 else:
795 raise cros_build_lib.Die(str(e))