blob: 5000160c90d7527020beadd020593b4b1a908ff3 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
cmticeb70801a2014-12-11 14:29:34 -08002# Copyright 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Wrapper for running gdb.
7
8This handles the fun details like running against the right sysroot, via
9qemu, bind mounts, etc...
10"""
11
12from __future__ import print_function
13
14import argparse
15import contextlib
16import errno
17import os
18import sys
19import tempfile
20
Ben Pastene8d754972019-12-04 15:03:23 -080021from chromite.cli.cros import cros_chrome_sdk
Mike Frysingerd8d25cd2021-04-06 11:39:17 -040022from chromite.lib import build_target_lib
cmticeb70801a2014-12-11 14:29:34 -080023from chromite.lib import commandline
Ben Pastene8d754972019-12-04 15:03:23 -080024from chromite.lib import constants
cmticeb70801a2014-12-11 14:29:34 -080025from chromite.lib import cros_build_lib
cmticef23cb132015-04-10 15:13:00 -070026from chromite.lib import cros_logging as logging
cmticeb70801a2014-12-11 14:29:34 -080027from chromite.lib import namespaces
28from chromite.lib import osutils
Ben Pastenec228d492018-07-02 13:53:58 -070029from chromite.lib import path_util
cmtice932e0aa2015-02-27 11:49:12 -080030from chromite.lib import qemu
cmticef23cb132015-04-10 15:13:00 -070031from chromite.lib import remote_access
cmticeb70801a2014-12-11 14:29:34 -080032from chromite.lib import retry_util
cmticef23cb132015-04-10 15:13:00 -070033from chromite.lib import toolchain
cmticeb70801a2014-12-11 14:29:34 -080034
Mike Frysinger1c76d4c2020-02-08 23:35:29 -050035
36assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
37
38
cmticef23cb132015-04-10 15:13:00 -070039class GdbException(Exception):
40 """Base exception for this module."""
41
42
43class GdbBadRemoteDeviceError(GdbException):
44 """Raised when remote device does not exist or is not responding."""
45
46
47class GdbMissingSysrootError(GdbException):
48 """Raised when path to sysroot cannot be found in chroot."""
49
50
51class GdbMissingInferiorError(GdbException):
52 """Raised when the binary to be debugged cannot be found."""
53
54
55class GdbMissingDebuggerError(GdbException):
56 """Raised when cannot find correct version of debugger."""
57
58
59class GdbCannotFindRemoteProcessError(GdbException):
60 """Raised when cannot find requested executing process on remote device."""
61
62
63class GdbUnableToStartGdbserverError(GdbException):
64 """Raised when error occurs trying to start gdbserver on remote device."""
65
66
67class GdbTooManyPidsError(GdbException):
68 """Raised when more than one matching pid is found running on device."""
69
70
71class GdbEarlyExitError(GdbException):
72 """Raised when user requests to exit early."""
73
74
75class GdbCannotDetectBoardError(GdbException):
76 """Raised when board isn't specified and can't be automatically determined."""
cmticeb70801a2014-12-11 14:29:34 -080077
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070078class GdbSimpleChromeBinaryError(GdbException):
79 """Raised when none or multiple chrome binaries are under out_${board} dir."""
cmtice932e0aa2015-02-27 11:49:12 -080080
cmticeb70801a2014-12-11 14:29:34 -080081class BoardSpecificGdb(object):
82 """Framework for running gdb."""
83
84 _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
cmticef23cb132015-04-10 15:13:00 -070085 _GDB = '/usr/bin/gdb'
Raul E Rangel746c45d2018-05-09 09:27:31 -060086 _EXTRA_SSH_SETTINGS = {
87 'CheckHostIP': 'no',
88 'BatchMode': 'yes',
89 'LogLevel': 'QUIET'
90 }
cmticef23cb132015-04-10 15:13:00 -070091 _MISSING_DEBUG_INFO_MSG = """
92%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
93 The debug symbols for that package may not be installed. To install the debug
94 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080095
cmticef23cb132015-04-10 15:13:00 -070096 cros_install_debug_syms --board=%(board)s %(package)s
97
98To install the debug symbols for all available packages, run:
99
100 cros_install_debug_syms --board=%(board)s --all"""
101
102 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700103 remote_process_name, cgdb_flag, ping, binary):
cmticeb70801a2014-12-11 14:29:34 -0800104 self.board = board
cmticef23cb132015-04-10 15:13:00 -0700105 self.sysroot = None
106 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -0800107 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -0700108 self.run_as_root = False
109 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800110 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -0700111 self.remote = remote.hostname if remote else None
112 self.pid = pid
113 self.remote_process_name = remote_process_name
114 # Port used for sending ssh commands to DUT.
115 self.remote_port = remote.port if remote else None
116 # Port for communicating between gdb & gdbserver.
117 self.gdbserver_port = remote_access.GetUnusedPort()
118 self.ssh_settings = remote_access.CompileSSHConnectSettings(
119 **self._EXTRA_SSH_SETTINGS)
120 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800121 self.framework = 'auto'
122 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700123 self.device = None
124 self.cross_gdb = None
125 self.ping = ping
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700126 self.binary = binary
127 self.in_chroot = None
128 self.chrome_path = None
129 self.sdk_path = None
130
131 def IsInChroot(self):
132 """Decide whether we are in chroot or chrome-sdk."""
Mike Frysinger80de5012019-08-01 14:10:53 -0400133 return os.path.exists('/mnt/host/source/chromite/')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700134
135 def SimpleChromeGdb(self):
136 """Get the name of the cross gdb based on board name."""
Ben Pastene8d754972019-12-04 15:03:23 -0800137 bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
138 cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
139 self.sdk_path, self.board)
140 bin_path = os.path.join(bin_path, 'bin')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700141 for f in os.listdir(bin_path):
142 if f.endswith('gdb'):
143 return os.path.join(bin_path, f)
Ben Pastene8d754972019-12-04 15:03:23 -0800144 raise GdbMissingDebuggerError('Cannot find cross gdb for %s.' % self.board)
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700145
146 def SimpleChromeSysroot(self):
147 """Get the sysroot in simple chrome."""
Ben Pastene8d754972019-12-04 15:03:23 -0800148 sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
149 constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board)
150 if not sysroot:
Jack Rosenthal9e40ae12019-12-02 10:18:10 -0700151 raise GdbMissingSysrootError('Cannot find sysroot for %s at %s'
Ben Pastene8d754972019-12-04 15:03:23 -0800152 % (self.board, self.sdk_path))
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700153 return sysroot
154
155 def GetSimpleChromeBinary(self):
156 """Get path to the binary in simple chrome."""
157 if self.binary:
158 return self.binary
159
160 output_dir = os.path.join(self.chrome_path, 'src',
161 'out_{}'.format(self.board))
162 target_binary = None
163 binary_name = os.path.basename(self.inf_cmd)
164 for root, _, files in os.walk(output_dir):
165 for f in files:
166 if f == binary_name:
Mike Frysinger82b059e2018-07-14 00:46:18 -0400167 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700168 target_binary = os.path.join(root, f)
169 else:
170 raise GdbSimpleChromeBinaryError(
171 'There are multiple %s under %s. Please specify the path to '
172 'the binary via --binary'% binary_name, output_dir)
Mike Frysinger82b059e2018-07-14 00:46:18 -0400173 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700174 raise GdbSimpleChromeBinaryError('There is no %s under %s.'
Ben Pastene8d754972019-12-04 15:03:23 -0800175 % (binary_name, output_dir))
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700176 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800177
cmticef23cb132015-04-10 15:13:00 -0700178 def VerifyAndFinishInitialization(self, device):
179 """Verify files/processes exist and flags are correct."""
180 if not self.board:
181 if self.remote:
182 self.board = cros_build_lib.GetBoard(device_board=device.board,
Eashan Bhatt2f01e422019-07-25 10:31:04 -0700183 override_board=self.board,
184 strict=True)
cmticef23cb132015-04-10 15:13:00 -0700185 else:
186 raise GdbCannotDetectBoardError('Cannot determine which board to use. '
187 'Please specify the with --board flag.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700188 self.in_chroot = self.IsInChroot()
cmticef23cb132015-04-10 15:13:00 -0700189 self.prompt = '(%s-gdb) ' % self.board
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700190 if self.in_chroot:
Mike Frysingerd8d25cd2021-04-06 11:39:17 -0400191 self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700192 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
193 self.cross_gdb = self.GetCrossGdb()
194 else:
195 self.chrome_path = os.path.realpath(os.path.join(os.path.dirname(
Mike Frysinger80de5012019-08-01 14:10:53 -0400196 os.path.realpath(__file__)), '../../../..'))
Ben Pastene8d754972019-12-04 15:03:23 -0800197 self.sdk_path = path_util.FindCacheDir()
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700198 self.sysroot = self.SimpleChromeSysroot()
199 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700200
201 if self.remote:
202
203 # If given remote process name, find pid & inf_cmd on remote device.
204 if self.remote_process_name or self.pid:
205 self._FindRemoteProcess(device)
206
207 # Verify that sysroot is valid (exists).
208 if not os.path.isdir(self.sysroot):
209 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
210 self.sysroot)
211
212 self.device = device
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700213 if not self.in_chroot:
214 return
215
cmticef23cb132015-04-10 15:13:00 -0700216 sysroot_inf_cmd = ''
217 if self.inf_cmd:
218 sysroot_inf_cmd = os.path.join(self.sysroot,
219 self.inf_cmd.lstrip('/'))
220
221 # Verify that inf_cmd, if given, exists.
222 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
223 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
224 sysroot_inf_cmd)
225
226 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
227 # exists. If not, tell user and give them the option of quitting & getting
228 # the debug info.
229 if sysroot_inf_cmd:
Mike Frysinger45602c72019-09-22 02:15:11 -0400230 stripped_info = cros_build_lib.run(['file', sysroot_inf_cmd],
Allen Webb763da8a2020-02-25 14:55:54 -0800231 capture_output=True,
232 encoding='utf-8').stdout
Mike Frysinger266e4ff2018-07-14 00:41:05 -0400233 if ' not stripped' not in stripped_info:
cmticef23cb132015-04-10 15:13:00 -0700234 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
235 self.inf_cmd.lstrip('/'))
236 debug_file += '.debug'
237 if not os.path.exists(debug_file):
238 equery = 'equery-%s' % self.board
Mike Frysinger45602c72019-09-22 02:15:11 -0400239 package = cros_build_lib.run([equery, '-q', 'b', self.inf_cmd],
Allen Webb763da8a2020-02-25 14:55:54 -0800240 capture_output=True,
241 encoding='utf-8').stdout
Lann Martinffb95162018-08-28 12:02:54 -0600242 # pylint: disable=logging-not-lazy
cmticef23cb132015-04-10 15:13:00 -0700243 logging.info(self._MISSING_DEBUG_INFO_MSG % {
244 'board': self.board,
245 'inf_cmd': self.inf_cmd,
246 'package': package,
247 'debug_file': debug_file})
248 answer = cros_build_lib.BooleanPrompt()
249 if not answer:
250 raise GdbEarlyExitError('Exiting early, at user request.')
251
252 # Set up qemu, if appropriate.
253 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800254 if qemu_arch is None:
255 self.framework = 'ldso'
256 else:
257 self.framework = 'qemu'
258 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800259
cmticef23cb132015-04-10 15:13:00 -0700260 if self.remote:
261 # Verify cgdb flag info.
262 if self.cgdb:
263 if osutils.Which('cgdb') is None:
264 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
265 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800266
cmticef23cb132015-04-10 15:13:00 -0700267 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800268 """Returns the given path with any sysroot prefix removed."""
269 # If the sysroot is /, then the paths are already normalized.
270 if self.sysroot != '/' and path.startswith(self.sysroot):
271 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800272 return path
273
274 @staticmethod
275 def GetNonRootAccount():
276 """Return details about the non-root account we want to use.
277
278 Returns:
279 A tuple of (username, uid, gid, home).
280 """
281 return (
282 os.environ.get('SUDO_USER', 'nobody'),
283 int(os.environ.get('SUDO_UID', '65534')),
284 int(os.environ.get('SUDO_GID', '65534')),
285 # Should we find a better home?
286 '/tmp/portage',
287 )
288
289 @staticmethod
290 @contextlib.contextmanager
291 def LockDb(db):
292 """Lock an account database.
293
294 We use the same algorithm as shadow/user.eclass. This way we don't race
295 and corrupt things in parallel.
296 """
297 lock = '%s.lock' % db
298 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
299
300 # First try forever to grab the lock.
301 retry = lambda e: e.errno == errno.EEXIST
302 # Retry quickly at first, but slow down over time.
303 try:
304 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700305 except Exception as e:
306 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800307
308 # Yield while holding the lock, but try to clean it no matter what.
309 try:
310 os.unlink(tmplock)
311 yield lock
312 finally:
313 os.unlink(lock)
314
315 def SetupUser(self):
316 """Propogate the user name<->id mapping from outside the chroot.
317
318 Some unittests use getpwnam($USER), as does bash. If the account
319 is not registered in the sysroot, they get back errors.
320 """
321 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
322 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
323 SDK_GECOS = 'ChromeOS Developer'
324
325 user, uid, gid, home = self.GetNonRootAccount()
326 if user == 'nobody':
327 return
328
329 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
330 with self.LockDb(passwd_db):
331 data = osutils.ReadFile(passwd_db)
332 accts = data.splitlines()
333 for acct in accts:
334 passwd = acct.split(':')
335 if passwd[0] == user:
336 # Did the sdk make this account?
337 if passwd[4] == SDK_GECOS:
338 # Don't modify it (see below) since we didn't create it.
339 return
340
341 # Did we make this account?
342 if passwd[4] != MAGIC_GECOS:
343 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
344 (passwd_db, user))
345
346 # Maybe we should see if it needs to be updated? Like if they
347 # changed UIDs? But we don't really check that elsewhere ...
348 return
349
350 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
351 'name': user,
352 'uid': uid,
353 'gid': gid,
354 'gecos': MAGIC_GECOS,
355 'homedir': home,
356 'shell': '/bin/bash',
357 }
358 with open(passwd_db, 'a') as f:
359 if data[-1] != '\n':
360 f.write('\n')
361 f.write('%s\n' % acct)
362
cmticef23cb132015-04-10 15:13:00 -0700363 def _FindRemoteProcess(self, device):
364 """Find a named process (or a pid) running on a remote device."""
365 if not self.remote_process_name and not self.pid:
366 return
cmticeb70801a2014-12-11 14:29:34 -0800367
cmticef23cb132015-04-10 15:13:00 -0700368 if self.remote_process_name:
369 # Look for a process with the specified name on the remote device; if
370 # found, get its pid.
371 pname = self.remote_process_name
372 if pname == 'browser':
373 all_chrome_pids = set(device.GetRunningPids(
374 '/opt/google/chrome/chrome'))
cmticef23cb132015-04-10 15:13:00 -0700375 non_main_chrome_pids = set(device.GetRunningPids('type='))
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500376 pids = list(all_chrome_pids - non_main_chrome_pids)
cmticef23cb132015-04-10 15:13:00 -0700377 elif pname == 'renderer' or pname == 'gpu-process':
378 pids = device.GetRunningPids('type=%s'% pname)
379 else:
380 pids = device.GetRunningPids(pname)
381
382 if pids:
383 if len(pids) == 1:
384 self.pid = pids[0]
385 else:
386 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
387 'You must specify the correct pid.'
388 % (pname, repr(pids)))
389 else:
390 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
391 (pname, self.remote))
392
393 # Find full path for process, from pid (and verify pid).
394 command = [
395 'readlink',
396 '-e', '/proc/%s/exe' % self.pid,
397 ]
398 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400399 res = device.run(command, capture_output=True)
cmticef23cb132015-04-10 15:13:00 -0700400 if res.returncode == 0:
401 self.inf_cmd = res.output.rstrip('\n')
402 except cros_build_lib.RunCommandError:
403 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
404 'with pid %s on %s' %
405 (self.pid, self.remote))
406
407 def GetCrossGdb(self):
408 """Find the appropriate cross-version of gdb for the board."""
409 toolchains = toolchain.GetToolchainsForBoard(self.board)
Mike Frysinger818d9632019-08-24 14:43:05 -0400410 tc = list(toolchain.FilterToolchains(toolchains, 'default', True))
cmticef23cb132015-04-10 15:13:00 -0700411 cross_gdb = tc[0] + '-gdb'
412 if not osutils.Which(cross_gdb):
413 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
414 'setup_board?' % cross_gdb)
415 return cross_gdb
416
Raul E Rangel746c45d2018-05-09 09:27:31 -0600417 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700418 """Generate list of commands with which to initialize the gdb session."""
419 gdb_init_commands = []
420
421 if self.remote:
422 sysroot_var = self.sysroot
423 else:
424 sysroot_var = '/'
425
426 gdb_init_commands = [
427 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700428 'set prompt %s' % self.prompt,
429 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700430 if self.in_chroot:
431 gdb_init_commands += [
432 'set solib-absolute-prefix %s' % sysroot_var,
433 'set solib-search-path %s' % sysroot_var,
434 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
435 ]
cmticef23cb132015-04-10 15:13:00 -0700436
Raul E Rangel746c45d2018-05-09 09:27:31 -0600437 if device:
438 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
439
440 ssh_cmd.extend(['--', 'gdbserver'])
441
442 if self.pid:
443 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
444 target_type = 'remote'
445 elif inferior_cmd:
446 ssh_cmd.extend(['-', inferior_cmd])
447 ssh_cmd.extend(self.inf_args)
448 target_type = 'remote'
449 else:
450 ssh_cmd.extend(['--multi', 'stdio'])
451 target_type = 'extended-remote'
452
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600453 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700454
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700455 if self.in_chroot:
456 if inferior_cmd:
457 gdb_init_commands.append(
458 'file %s' % os.path.join(sysroot_var,
459 inferior_cmd.lstrip(os.sep)))
460 else:
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700461 binary = self.GetSimpleChromeBinary()
462 gdb_init_commands += [
463 'set debug-file-directory %s' % os.path.dirname(binary),
464 'file %s' % binary
465 ]
Raul E Rangel746c45d2018-05-09 09:27:31 -0600466
467 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700468 else:
469 if inferior_cmd:
470 gdb_init_commands.append('file %s ' % inferior_cmd)
471 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
472
473 return gdb_init_commands
474
475 def RunRemote(self):
476 """Handle remote debugging, via gdbserver & cross debugger."""
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600477 with remote_access.ChromiumOSDeviceHandler(
478 self.remote,
479 port=self.remote_port,
480 connect_settings=self.ssh_settings,
481 ping=self.ping) as device:
cmticef23cb132015-04-10 15:13:00 -0700482
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600483 self.VerifyAndFinishInitialization(device)
484 gdb_cmd = self.cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700485
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600486 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
487 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
488 gdb_args += self.gdb_args
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600489
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600490 if self.cgdb:
491 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
492 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700493
Mike Frysinger45602c72019-09-22 02:15:11 -0400494 cros_build_lib.run(
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600495 [gdb_cmd] + gdb_args,
496 ignore_sigint=True,
497 print_cmd=True,
498 cwd=self.sysroot)
cmticef23cb132015-04-10 15:13:00 -0700499
500 def Run(self):
501 """Runs the debugger in a proper environment (e.g. qemu)."""
502
503 self.VerifyAndFinishInitialization(None)
504 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800505 if self.framework == 'qemu':
506 self.qemu.Install(self.sysroot)
507 self.qemu.RegisterBinfmt()
508
cmticeb70801a2014-12-11 14:29:34 -0800509 for mount in self._BIND_MOUNT_PATHS:
510 path = os.path.join(self.sysroot, mount)
511 osutils.SafeMakedirs(path)
512 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
513
cmticef23cb132015-04-10 15:13:00 -0700514 gdb_cmd = self._GDB
515 inferior_cmd = self.inf_cmd
516
cmtice932e0aa2015-02-27 11:49:12 -0800517 gdb_argv = self.gdb_args[:]
518 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700519 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800520 # Some programs expect to find data files via $CWD, so doing a chroot
521 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700522 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800523
cmticeb70801a2014-12-11 14:29:34 -0800524 os.chroot(self.sysroot)
525 os.chdir(cwd)
526 # The TERM the user is leveraging might not exist in the sysroot.
527 # Force a sane default that supports standard color sequences.
528 os.environ['TERM'] = 'ansi'
529 # Some progs want this like bash else they get super confused.
530 os.environ['PWD'] = cwd
531 if not self.run_as_root:
532 _, uid, gid, home = self.GetNonRootAccount()
533 os.setgid(gid)
534 os.setuid(uid)
535 os.environ['HOME'] = home
536
cmticef23cb132015-04-10 15:13:00 -0700537 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800538
cmticef23cb132015-04-10 15:13:00 -0700539 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
540 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800541 gdb_args += self.gdb_args
542
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600543 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800544
545
cmtice932e0aa2015-02-27 11:49:12 -0800546def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800547 """Re-execute gdb as root.
548
549 We often need to do things as root, so make sure we're that. Like chroot
550 for proper library environment or do bind mounts.
551
552 Also unshare the mount namespace so as to ensure that doing bind mounts for
553 tests don't leak out to the normal chroot. Also unshare the UTS namespace
554 so changes to `hostname` do not impact the host.
555 """
556 if os.geteuid() != 0:
557 cmd = ['sudo', '-E', '--'] + argv
558 os.execvp(cmd[0], cmd)
559 else:
cmtice932e0aa2015-02-27 11:49:12 -0800560 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
561
562
cmticef23cb132015-04-10 15:13:00 -0700563def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800564 """Look for the name of the inferior (to be debugged) in arg list."""
565
566 program_name = ''
567 new_list = []
568 for item in arg_list:
569 if item[0] == '-':
570 new_list.append(item)
571 elif not program_name:
572 program_name = item
573 else:
574 raise RuntimeError('Found multiple program names: %s %s'
575 % (program_name, item))
576
577 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800578
579
580def main(argv):
581
582 parser = commandline.ArgumentParser(description=__doc__)
583
cmticef23cb132015-04-10 15:13:00 -0700584 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800585 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700586 parser.add_argument('-g', '--gdb_args', action='append', default=[],
587 help='Arguments to gdb itself. If multiple arguments are'
Mike Frysinger80de5012019-08-01 14:10:53 -0400588 " passed, each argument needs a separate '-g' flag.")
cmticef23cb132015-04-10 15:13:00 -0700589 parser.add_argument(
590 '--remote', default=None,
591 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
592 help='Remote device on which to run the binary. Use'
593 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
594 ' already running local virtual machine.')
595 parser.add_argument('--pid', default='',
596 help='Process ID of the (already) running process on the'
597 ' remote device to which to attach.')
598 parser.add_argument('--remote_pid', dest='pid', default='',
599 help='Deprecated alias for --pid.')
600 parser.add_argument('--no-ping', dest='ping', default=True,
601 action='store_false',
602 help='Do not ping remote before attempting to connect.')
603 parser.add_argument('--attach', dest='attach_name', default='',
604 help='Name of existing process to which to attach, on'
605 ' remote device (remote debugging only). "--attach'
606 ' browser" will find the main chrome browser process;'
607 ' "--attach renderer" will find a chrome renderer'
608 ' process; "--attach gpu-process" will find the chrome'
609 ' gpu process.')
610 parser.add_argument('--cgdb', default=False,
611 action='store_true',
612 help='Use cgdb curses interface rather than plain gdb.'
613 'This option is only valid for remote debugging.')
614 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
615 help='Arguments for gdb to pass to the program being'
616 ' debugged. These are positional and must come at the end'
617 ' of the command line. This will not work if attaching'
618 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700619 parser.add_argument('--binary', default='',
620 help='full path to the binary being debuged.'
621 ' This is only useful for simple chrome.'
622 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800623
624 options = parser.parse_args(argv)
625 options.Freeze()
626
cmtice932e0aa2015-02-27 11:49:12 -0800627 gdb_args = []
628 inf_args = []
629 inf_cmd = ''
630
cmticef23cb132015-04-10 15:13:00 -0700631 if options.inf_args:
632 inf_cmd = options.inf_args[0]
633 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800634
cmticef23cb132015-04-10 15:13:00 -0700635 if options.gdb_args:
636 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800637
638 if inf_cmd:
Mike Frysingerd8d25cd2021-04-06 11:39:17 -0400639 fname = os.path.join(
640 build_target_lib.get_default_sysroot_path(options.board),
641 inf_cmd.lstrip('/'))
cmtice932e0aa2015-02-27 11:49:12 -0800642 if not os.path.exists(fname):
643 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700644 else:
645 if inf_args:
646 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800647
cmticef23cb132015-04-10 15:13:00 -0700648 if inf_args and (options.pid or options.attach_name):
649 parser.error('Cannot pass arguments to an already'
650 ' running process (--remote-pid or --attach).')
651
652 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700653 if options.attach_name and options.attach_name == 'browser':
654 inf_cmd = '/opt/google/chrome/chrome'
655 else:
656 if options.cgdb:
657 parser.error('--cgdb option can only be used with remote debugging.')
658 if options.pid:
659 parser.error('Must specify a remote device (--remote) if you want '
660 'to attach to a remote pid.')
661 if options.attach_name:
662 parser.error('Must specify remote device (--remote) when using'
663 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700664 if options.binary:
665 if not os.path.exists(options.binary):
666 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800667
cmticeb70801a2014-12-11 14:29:34 -0800668 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700669 if not options.remote:
670 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800671
cmticef23cb132015-04-10 15:13:00 -0700672 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
673 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700674 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800675
cmticef23cb132015-04-10 15:13:00 -0700676 try:
677 if options.remote:
678 gdb.RunRemote()
679 else:
680 gdb.Run()
681
682 except GdbException as e:
683 if options.debug:
684 raise
685 else:
686 raise cros_build_lib.Die(str(e))