blob: 9d735d6d627765c97731712ee8800b35c2345025 [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):
34 """Base exception for this module."""
35
36
37class GdbBadRemoteDeviceError(GdbException):
38 """Raised when remote device does not exist or is not responding."""
39
40
41class GdbMissingSysrootError(GdbException):
42 """Raised when path to sysroot cannot be found in chroot."""
43
44
45class GdbMissingInferiorError(GdbException):
46 """Raised when the binary to be debugged cannot be found."""
47
48
49class GdbMissingDebuggerError(GdbException):
50 """Raised when cannot find correct version of debugger."""
51
52
53class GdbCannotFindRemoteProcessError(GdbException):
54 """Raised when cannot find requested executing process on remote device."""
55
56
57class GdbUnableToStartGdbserverError(GdbException):
58 """Raised when error occurs trying to start gdbserver on remote device."""
59
60
61class GdbTooManyPidsError(GdbException):
62 """Raised when more than one matching pid is found running on device."""
63
64
65class GdbEarlyExitError(GdbException):
66 """Raised when user requests to exit early."""
67
68
69class GdbCannotDetectBoardError(GdbException):
70 """Raised when board isn't specified and can't be automatically determined."""
cmticeb70801a2014-12-11 14:29:34 -080071
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070072class GdbSimpleChromeBinaryError(GdbException):
73 """Raised when none or multiple chrome binaries are under out_${board} dir."""
cmtice932e0aa2015-02-27 11:49:12 -080074
cmticeb70801a2014-12-11 14:29:34 -080075class BoardSpecificGdb(object):
76 """Framework for running gdb."""
77
78 _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
cmticef23cb132015-04-10 15:13:00 -070079 _GDB = '/usr/bin/gdb'
Raul E Rangel746c45d2018-05-09 09:27:31 -060080 _EXTRA_SSH_SETTINGS = {
81 'CheckHostIP': 'no',
82 'BatchMode': 'yes',
83 'LogLevel': 'QUIET'
84 }
cmticef23cb132015-04-10 15:13:00 -070085 _MISSING_DEBUG_INFO_MSG = """
86%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
87 The debug symbols for that package may not be installed. To install the debug
88 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080089
cmticef23cb132015-04-10 15:13:00 -070090 cros_install_debug_syms --board=%(board)s %(package)s
91
92To install the debug symbols for all available packages, run:
93
94 cros_install_debug_syms --board=%(board)s --all"""
95
96 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070097 remote_process_name, cgdb_flag, ping, binary):
cmticeb70801a2014-12-11 14:29:34 -080098 self.board = board
cmticef23cb132015-04-10 15:13:00 -070099 self.sysroot = None
100 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -0800101 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -0700102 self.run_as_root = False
103 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800104 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -0700105 self.remote = remote.hostname if remote else None
106 self.pid = pid
107 self.remote_process_name = remote_process_name
108 # Port used for sending ssh commands to DUT.
109 self.remote_port = remote.port if remote else None
110 # Port for communicating between gdb & gdbserver.
111 self.gdbserver_port = remote_access.GetUnusedPort()
112 self.ssh_settings = remote_access.CompileSSHConnectSettings(
113 **self._EXTRA_SSH_SETTINGS)
114 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800115 self.framework = 'auto'
116 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700117 self.device = None
118 self.cross_gdb = None
119 self.ping = ping
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700120 self.binary = binary
121 self.in_chroot = None
122 self.chrome_path = None
123 self.sdk_path = None
124
125 def IsInChroot(self):
126 """Decide whether we are in chroot or chrome-sdk."""
Mike Frysinger80de5012019-08-01 14:10:53 -0400127 return os.path.exists('/mnt/host/source/chromite/')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700128
129 def SimpleChromeGdb(self):
130 """Get the name of the cross gdb based on board name."""
Ben Pastene8d754972019-12-04 15:03:23 -0800131 bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
132 cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
133 self.sdk_path, self.board)
134 bin_path = os.path.join(bin_path, 'bin')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700135 for f in os.listdir(bin_path):
136 if f.endswith('gdb'):
137 return os.path.join(bin_path, f)
Ben Pastene8d754972019-12-04 15:03:23 -0800138 raise GdbMissingDebuggerError('Cannot find cross gdb for %s.' % self.board)
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700139
140 def SimpleChromeSysroot(self):
141 """Get the sysroot in simple chrome."""
Ben Pastene8d754972019-12-04 15:03:23 -0800142 sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
143 constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board)
144 if not sysroot:
Jack Rosenthal9e40ae12019-12-02 10:18:10 -0700145 raise GdbMissingSysrootError('Cannot find sysroot for %s at %s'
Ben Pastene8d754972019-12-04 15:03:23 -0800146 % (self.board, self.sdk_path))
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700147 return sysroot
148
149 def GetSimpleChromeBinary(self):
150 """Get path to the binary in simple chrome."""
151 if self.binary:
152 return self.binary
153
154 output_dir = os.path.join(self.chrome_path, 'src',
155 'out_{}'.format(self.board))
156 target_binary = None
157 binary_name = os.path.basename(self.inf_cmd)
158 for root, _, files in os.walk(output_dir):
159 for f in files:
160 if f == binary_name:
Mike Frysinger82b059e2018-07-14 00:46:18 -0400161 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700162 target_binary = os.path.join(root, f)
163 else:
164 raise GdbSimpleChromeBinaryError(
165 'There are multiple %s under %s. Please specify the path to '
166 'the binary via --binary'% binary_name, output_dir)
Mike Frysinger82b059e2018-07-14 00:46:18 -0400167 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700168 raise GdbSimpleChromeBinaryError('There is no %s under %s.'
Ben Pastene8d754972019-12-04 15:03:23 -0800169 % (binary_name, output_dir))
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700170 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800171
cmticef23cb132015-04-10 15:13:00 -0700172 def VerifyAndFinishInitialization(self, device):
173 """Verify files/processes exist and flags are correct."""
174 if not self.board:
175 if self.remote:
176 self.board = cros_build_lib.GetBoard(device_board=device.board,
Eashan Bhatt2f01e422019-07-25 10:31:04 -0700177 override_board=self.board,
178 strict=True)
cmticef23cb132015-04-10 15:13:00 -0700179 else:
180 raise GdbCannotDetectBoardError('Cannot determine which board to use. '
181 'Please specify the with --board flag.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700182 self.in_chroot = self.IsInChroot()
cmticef23cb132015-04-10 15:13:00 -0700183 self.prompt = '(%s-gdb) ' % self.board
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700184 if self.in_chroot:
Mike Frysinger06a51c82021-04-06 11:39:17 -0400185 self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700186 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
187 self.cross_gdb = self.GetCrossGdb()
188 else:
189 self.chrome_path = os.path.realpath(os.path.join(os.path.dirname(
Mike Frysinger80de5012019-08-01 14:10:53 -0400190 os.path.realpath(__file__)), '../../../..'))
Ben Pastene8d754972019-12-04 15:03:23 -0800191 self.sdk_path = path_util.FindCacheDir()
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700192 self.sysroot = self.SimpleChromeSysroot()
193 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700194
195 if self.remote:
196
197 # If given remote process name, find pid & inf_cmd on remote device.
198 if self.remote_process_name or self.pid:
199 self._FindRemoteProcess(device)
200
201 # Verify that sysroot is valid (exists).
202 if not os.path.isdir(self.sysroot):
203 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
204 self.sysroot)
205
206 self.device = device
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700207 if not self.in_chroot:
208 return
209
cmticef23cb132015-04-10 15:13:00 -0700210 sysroot_inf_cmd = ''
211 if self.inf_cmd:
212 sysroot_inf_cmd = os.path.join(self.sysroot,
213 self.inf_cmd.lstrip('/'))
214
215 # Verify that inf_cmd, if given, exists.
216 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
217 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
218 sysroot_inf_cmd)
219
220 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
221 # exists. If not, tell user and give them the option of quitting & getting
222 # the debug info.
223 if sysroot_inf_cmd:
Mike Frysinger45602c72019-09-22 02:15:11 -0400224 stripped_info = cros_build_lib.run(['file', sysroot_inf_cmd],
Allen Webb763da8a2020-02-25 14:55:54 -0800225 capture_output=True,
226 encoding='utf-8').stdout
Mike Frysinger266e4ff2018-07-14 00:41:05 -0400227 if ' not stripped' not in stripped_info:
cmticef23cb132015-04-10 15:13:00 -0700228 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
229 self.inf_cmd.lstrip('/'))
230 debug_file += '.debug'
231 if not os.path.exists(debug_file):
232 equery = 'equery-%s' % self.board
Mike Frysinger45602c72019-09-22 02:15:11 -0400233 package = cros_build_lib.run([equery, '-q', 'b', self.inf_cmd],
Allen Webb763da8a2020-02-25 14:55:54 -0800234 capture_output=True,
235 encoding='utf-8').stdout
Lann Martinffb95162018-08-28 12:02:54 -0600236 # pylint: disable=logging-not-lazy
cmticef23cb132015-04-10 15:13:00 -0700237 logging.info(self._MISSING_DEBUG_INFO_MSG % {
238 'board': self.board,
239 'inf_cmd': self.inf_cmd,
240 'package': package,
241 'debug_file': debug_file})
242 answer = cros_build_lib.BooleanPrompt()
243 if not answer:
244 raise GdbEarlyExitError('Exiting early, at user request.')
245
246 # Set up qemu, if appropriate.
247 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800248 if qemu_arch is None:
249 self.framework = 'ldso'
250 else:
251 self.framework = 'qemu'
252 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800253
cmticef23cb132015-04-10 15:13:00 -0700254 if self.remote:
255 # Verify cgdb flag info.
256 if self.cgdb:
257 if osutils.Which('cgdb') is None:
258 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
259 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800260
cmticef23cb132015-04-10 15:13:00 -0700261 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800262 """Returns the given path with any sysroot prefix removed."""
263 # If the sysroot is /, then the paths are already normalized.
264 if self.sysroot != '/' and path.startswith(self.sysroot):
265 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800266 return path
267
268 @staticmethod
269 def GetNonRootAccount():
270 """Return details about the non-root account we want to use.
271
272 Returns:
273 A tuple of (username, uid, gid, home).
274 """
275 return (
276 os.environ.get('SUDO_USER', 'nobody'),
277 int(os.environ.get('SUDO_UID', '65534')),
278 int(os.environ.get('SUDO_GID', '65534')),
279 # Should we find a better home?
280 '/tmp/portage',
281 )
282
283 @staticmethod
284 @contextlib.contextmanager
285 def LockDb(db):
286 """Lock an account database.
287
288 We use the same algorithm as shadow/user.eclass. This way we don't race
289 and corrupt things in parallel.
290 """
291 lock = '%s.lock' % db
292 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
293
294 # First try forever to grab the lock.
295 retry = lambda e: e.errno == errno.EEXIST
296 # Retry quickly at first, but slow down over time.
297 try:
298 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700299 except Exception as e:
300 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800301
302 # Yield while holding the lock, but try to clean it no matter what.
303 try:
304 os.unlink(tmplock)
305 yield lock
306 finally:
307 os.unlink(lock)
308
309 def SetupUser(self):
310 """Propogate the user name<->id mapping from outside the chroot.
311
312 Some unittests use getpwnam($USER), as does bash. If the account
313 is not registered in the sysroot, they get back errors.
314 """
315 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
316 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
317 SDK_GECOS = 'ChromeOS Developer'
318
319 user, uid, gid, home = self.GetNonRootAccount()
320 if user == 'nobody':
321 return
322
323 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
324 with self.LockDb(passwd_db):
325 data = osutils.ReadFile(passwd_db)
326 accts = data.splitlines()
327 for acct in accts:
328 passwd = acct.split(':')
329 if passwd[0] == user:
330 # Did the sdk make this account?
331 if passwd[4] == SDK_GECOS:
332 # Don't modify it (see below) since we didn't create it.
333 return
334
335 # Did we make this account?
336 if passwd[4] != MAGIC_GECOS:
337 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
338 (passwd_db, user))
339
340 # Maybe we should see if it needs to be updated? Like if they
341 # changed UIDs? But we don't really check that elsewhere ...
342 return
343
344 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
345 'name': user,
346 'uid': uid,
347 'gid': gid,
348 'gecos': MAGIC_GECOS,
349 'homedir': home,
350 'shell': '/bin/bash',
351 }
352 with open(passwd_db, 'a') as f:
353 if data[-1] != '\n':
354 f.write('\n')
355 f.write('%s\n' % acct)
356
cmticef23cb132015-04-10 15:13:00 -0700357 def _FindRemoteProcess(self, device):
358 """Find a named process (or a pid) running on a remote device."""
359 if not self.remote_process_name and not self.pid:
360 return
cmticeb70801a2014-12-11 14:29:34 -0800361
cmticef23cb132015-04-10 15:13:00 -0700362 if self.remote_process_name:
363 # Look for a process with the specified name on the remote device; if
364 # found, get its pid.
365 pname = self.remote_process_name
366 if pname == 'browser':
367 all_chrome_pids = set(device.GetRunningPids(
368 '/opt/google/chrome/chrome'))
cmticef23cb132015-04-10 15:13:00 -0700369 non_main_chrome_pids = set(device.GetRunningPids('type='))
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500370 pids = list(all_chrome_pids - non_main_chrome_pids)
cmticef23cb132015-04-10 15:13:00 -0700371 elif pname == 'renderer' or pname == 'gpu-process':
372 pids = device.GetRunningPids('type=%s'% pname)
373 else:
374 pids = device.GetRunningPids(pname)
375
376 if pids:
377 if len(pids) == 1:
378 self.pid = pids[0]
379 else:
380 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
381 'You must specify the correct pid.'
382 % (pname, repr(pids)))
383 else:
384 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
385 (pname, self.remote))
386
387 # Find full path for process, from pid (and verify pid).
388 command = [
389 'readlink',
390 '-e', '/proc/%s/exe' % self.pid,
391 ]
392 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400393 res = device.run(command, capture_output=True)
cmticef23cb132015-04-10 15:13:00 -0700394 if res.returncode == 0:
395 self.inf_cmd = res.output.rstrip('\n')
396 except cros_build_lib.RunCommandError:
397 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
398 'with pid %s on %s' %
399 (self.pid, self.remote))
400
401 def GetCrossGdb(self):
402 """Find the appropriate cross-version of gdb for the board."""
403 toolchains = toolchain.GetToolchainsForBoard(self.board)
Mike Frysinger818d9632019-08-24 14:43:05 -0400404 tc = list(toolchain.FilterToolchains(toolchains, 'default', True))
cmticef23cb132015-04-10 15:13:00 -0700405 cross_gdb = tc[0] + '-gdb'
406 if not osutils.Which(cross_gdb):
407 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
408 'setup_board?' % cross_gdb)
409 return cross_gdb
410
Raul E Rangel746c45d2018-05-09 09:27:31 -0600411 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700412 """Generate list of commands with which to initialize the gdb session."""
413 gdb_init_commands = []
414
415 if self.remote:
416 sysroot_var = self.sysroot
417 else:
418 sysroot_var = '/'
419
420 gdb_init_commands = [
421 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700422 'set prompt %s' % self.prompt,
423 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700424 if self.in_chroot:
425 gdb_init_commands += [
426 'set solib-absolute-prefix %s' % sysroot_var,
427 'set solib-search-path %s' % sysroot_var,
428 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
429 ]
cmticef23cb132015-04-10 15:13:00 -0700430
Raul E Rangel746c45d2018-05-09 09:27:31 -0600431 if device:
432 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
433
434 ssh_cmd.extend(['--', 'gdbserver'])
435
436 if self.pid:
437 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
438 target_type = 'remote'
439 elif inferior_cmd:
440 ssh_cmd.extend(['-', inferior_cmd])
441 ssh_cmd.extend(self.inf_args)
442 target_type = 'remote'
443 else:
444 ssh_cmd.extend(['--multi', 'stdio'])
445 target_type = 'extended-remote'
446
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600447 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700448
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700449 if self.in_chroot:
450 if inferior_cmd:
451 gdb_init_commands.append(
452 'file %s' % os.path.join(sysroot_var,
453 inferior_cmd.lstrip(os.sep)))
454 else:
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700455 binary = self.GetSimpleChromeBinary()
456 gdb_init_commands += [
457 'set debug-file-directory %s' % os.path.dirname(binary),
458 'file %s' % binary
459 ]
Raul E Rangel746c45d2018-05-09 09:27:31 -0600460
461 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700462 else:
463 if inferior_cmd:
464 gdb_init_commands.append('file %s ' % inferior_cmd)
465 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
466
467 return gdb_init_commands
468
469 def RunRemote(self):
470 """Handle remote debugging, via gdbserver & cross debugger."""
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600471 with remote_access.ChromiumOSDeviceHandler(
472 self.remote,
473 port=self.remote_port,
474 connect_settings=self.ssh_settings,
475 ping=self.ping) as device:
cmticef23cb132015-04-10 15:13:00 -0700476
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600477 self.VerifyAndFinishInitialization(device)
478 gdb_cmd = self.cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700479
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600480 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
481 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
482 gdb_args += self.gdb_args
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600483
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600484 if self.cgdb:
485 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
486 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700487
Mike Frysinger45602c72019-09-22 02:15:11 -0400488 cros_build_lib.run(
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600489 [gdb_cmd] + gdb_args,
490 ignore_sigint=True,
491 print_cmd=True,
492 cwd=self.sysroot)
cmticef23cb132015-04-10 15:13:00 -0700493
494 def Run(self):
495 """Runs the debugger in a proper environment (e.g. qemu)."""
496
497 self.VerifyAndFinishInitialization(None)
498 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800499 if self.framework == 'qemu':
500 self.qemu.Install(self.sysroot)
501 self.qemu.RegisterBinfmt()
502
cmticeb70801a2014-12-11 14:29:34 -0800503 for mount in self._BIND_MOUNT_PATHS:
504 path = os.path.join(self.sysroot, mount)
505 osutils.SafeMakedirs(path)
506 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
507
cmticef23cb132015-04-10 15:13:00 -0700508 gdb_cmd = self._GDB
509 inferior_cmd = self.inf_cmd
510
cmtice932e0aa2015-02-27 11:49:12 -0800511 gdb_argv = self.gdb_args[:]
512 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700513 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800514 # Some programs expect to find data files via $CWD, so doing a chroot
515 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700516 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800517
cmticeb70801a2014-12-11 14:29:34 -0800518 os.chroot(self.sysroot)
519 os.chdir(cwd)
520 # The TERM the user is leveraging might not exist in the sysroot.
521 # Force a sane default that supports standard color sequences.
522 os.environ['TERM'] = 'ansi'
523 # Some progs want this like bash else they get super confused.
524 os.environ['PWD'] = cwd
525 if not self.run_as_root:
526 _, uid, gid, home = self.GetNonRootAccount()
527 os.setgid(gid)
528 os.setuid(uid)
529 os.environ['HOME'] = home
530
cmticef23cb132015-04-10 15:13:00 -0700531 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800532
cmticef23cb132015-04-10 15:13:00 -0700533 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
534 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800535 gdb_args += self.gdb_args
536
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600537 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800538
539
cmtice932e0aa2015-02-27 11:49:12 -0800540def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800541 """Re-execute gdb as root.
542
543 We often need to do things as root, so make sure we're that. Like chroot
544 for proper library environment or do bind mounts.
545
546 Also unshare the mount namespace so as to ensure that doing bind mounts for
547 tests don't leak out to the normal chroot. Also unshare the UTS namespace
548 so changes to `hostname` do not impact the host.
549 """
550 if os.geteuid() != 0:
551 cmd = ['sudo', '-E', '--'] + argv
552 os.execvp(cmd[0], cmd)
553 else:
cmtice932e0aa2015-02-27 11:49:12 -0800554 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
555
556
cmticef23cb132015-04-10 15:13:00 -0700557def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800558 """Look for the name of the inferior (to be debugged) in arg list."""
559
560 program_name = ''
561 new_list = []
562 for item in arg_list:
563 if item[0] == '-':
564 new_list.append(item)
565 elif not program_name:
566 program_name = item
567 else:
568 raise RuntimeError('Found multiple program names: %s %s'
569 % (program_name, item))
570
571 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800572
573
574def main(argv):
575
576 parser = commandline.ArgumentParser(description=__doc__)
577
cmticef23cb132015-04-10 15:13:00 -0700578 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800579 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700580 parser.add_argument('-g', '--gdb_args', action='append', default=[],
581 help='Arguments to gdb itself. If multiple arguments are'
Mike Frysinger80de5012019-08-01 14:10:53 -0400582 " passed, each argument needs a separate '-g' flag.")
cmticef23cb132015-04-10 15:13:00 -0700583 parser.add_argument(
584 '--remote', default=None,
585 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
586 help='Remote device on which to run the binary. Use'
587 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
588 ' already running local virtual machine.')
589 parser.add_argument('--pid', default='',
590 help='Process ID of the (already) running process on the'
591 ' remote device to which to attach.')
592 parser.add_argument('--remote_pid', dest='pid', default='',
593 help='Deprecated alias for --pid.')
594 parser.add_argument('--no-ping', dest='ping', default=True,
595 action='store_false',
596 help='Do not ping remote before attempting to connect.')
597 parser.add_argument('--attach', dest='attach_name', default='',
598 help='Name of existing process to which to attach, on'
599 ' remote device (remote debugging only). "--attach'
600 ' browser" will find the main chrome browser process;'
601 ' "--attach renderer" will find a chrome renderer'
602 ' process; "--attach gpu-process" will find the chrome'
603 ' gpu process.')
604 parser.add_argument('--cgdb', default=False,
605 action='store_true',
606 help='Use cgdb curses interface rather than plain gdb.'
607 'This option is only valid for remote debugging.')
608 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
609 help='Arguments for gdb to pass to the program being'
610 ' debugged. These are positional and must come at the end'
611 ' of the command line. This will not work if attaching'
612 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700613 parser.add_argument('--binary', default='',
614 help='full path to the binary being debuged.'
615 ' This is only useful for simple chrome.'
616 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800617
618 options = parser.parse_args(argv)
619 options.Freeze()
620
cmtice932e0aa2015-02-27 11:49:12 -0800621 gdb_args = []
622 inf_args = []
623 inf_cmd = ''
624
cmticef23cb132015-04-10 15:13:00 -0700625 if options.inf_args:
626 inf_cmd = options.inf_args[0]
627 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800628
cmticef23cb132015-04-10 15:13:00 -0700629 if options.gdb_args:
630 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800631
632 if inf_cmd:
Mike Frysinger06a51c82021-04-06 11:39:17 -0400633 fname = os.path.join(
634 build_target_lib.get_default_sysroot_path(options.board),
635 inf_cmd.lstrip('/'))
cmtice932e0aa2015-02-27 11:49:12 -0800636 if not os.path.exists(fname):
637 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700638 else:
639 if inf_args:
640 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800641
cmticef23cb132015-04-10 15:13:00 -0700642 if inf_args and (options.pid or options.attach_name):
643 parser.error('Cannot pass arguments to an already'
644 ' running process (--remote-pid or --attach).')
645
646 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700647 if options.attach_name and options.attach_name == 'browser':
648 inf_cmd = '/opt/google/chrome/chrome'
649 else:
650 if options.cgdb:
651 parser.error('--cgdb option can only be used with remote debugging.')
652 if options.pid:
653 parser.error('Must specify a remote device (--remote) if you want '
654 'to attach to a remote pid.')
655 if options.attach_name:
656 parser.error('Must specify remote device (--remote) when using'
657 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700658 if options.binary:
659 if not os.path.exists(options.binary):
660 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800661
cmticeb70801a2014-12-11 14:29:34 -0800662 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700663 if not options.remote:
664 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800665
cmticef23cb132015-04-10 15:13:00 -0700666 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
667 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700668 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800669
cmticef23cb132015-04-10 15:13:00 -0700670 try:
671 if options.remote:
672 gdb.RunRemote()
673 else:
674 gdb.Run()
675
676 except GdbException as e:
677 if options.debug:
678 raise
679 else:
680 raise cros_build_lib.Die(str(e))