blob: 413154d4f230bc4ddec7feadb733902c0d883de3 [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
cmticeb70801a2014-12-11 14:29:34 -080022from chromite.lib import commandline
Ben Pastene8d754972019-12-04 15:03:23 -080023from chromite.lib import constants
cmticeb70801a2014-12-11 14:29:34 -080024from chromite.lib import cros_build_lib
cmticef23cb132015-04-10 15:13:00 -070025from chromite.lib import cros_logging as logging
cmticeb70801a2014-12-11 14:29:34 -080026from chromite.lib import namespaces
27from chromite.lib import osutils
Ben Pastenec228d492018-07-02 13:53:58 -070028from chromite.lib import path_util
cmtice932e0aa2015-02-27 11:49:12 -080029from chromite.lib import qemu
cmticef23cb132015-04-10 15:13:00 -070030from chromite.lib import remote_access
cmticeb70801a2014-12-11 14:29:34 -080031from chromite.lib import retry_util
cmticef23cb132015-04-10 15:13:00 -070032from chromite.lib import toolchain
cmticeb70801a2014-12-11 14:29:34 -080033
Mike Frysinger1c76d4c2020-02-08 23:35:29 -050034
35assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
36
37
cmticef23cb132015-04-10 15:13:00 -070038class GdbException(Exception):
39 """Base exception for this module."""
40
41
42class GdbBadRemoteDeviceError(GdbException):
43 """Raised when remote device does not exist or is not responding."""
44
45
46class GdbMissingSysrootError(GdbException):
47 """Raised when path to sysroot cannot be found in chroot."""
48
49
50class GdbMissingInferiorError(GdbException):
51 """Raised when the binary to be debugged cannot be found."""
52
53
54class GdbMissingDebuggerError(GdbException):
55 """Raised when cannot find correct version of debugger."""
56
57
58class GdbCannotFindRemoteProcessError(GdbException):
59 """Raised when cannot find requested executing process on remote device."""
60
61
62class GdbUnableToStartGdbserverError(GdbException):
63 """Raised when error occurs trying to start gdbserver on remote device."""
64
65
66class GdbTooManyPidsError(GdbException):
67 """Raised when more than one matching pid is found running on device."""
68
69
70class GdbEarlyExitError(GdbException):
71 """Raised when user requests to exit early."""
72
73
74class GdbCannotDetectBoardError(GdbException):
75 """Raised when board isn't specified and can't be automatically determined."""
cmticeb70801a2014-12-11 14:29:34 -080076
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070077class GdbSimpleChromeBinaryError(GdbException):
78 """Raised when none or multiple chrome binaries are under out_${board} dir."""
cmtice932e0aa2015-02-27 11:49:12 -080079
cmticeb70801a2014-12-11 14:29:34 -080080class BoardSpecificGdb(object):
81 """Framework for running gdb."""
82
83 _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
cmticef23cb132015-04-10 15:13:00 -070084 _GDB = '/usr/bin/gdb'
Raul E Rangel746c45d2018-05-09 09:27:31 -060085 _EXTRA_SSH_SETTINGS = {
86 'CheckHostIP': 'no',
87 'BatchMode': 'yes',
88 'LogLevel': 'QUIET'
89 }
cmticef23cb132015-04-10 15:13:00 -070090 _MISSING_DEBUG_INFO_MSG = """
91%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
92 The debug symbols for that package may not be installed. To install the debug
93 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080094
cmticef23cb132015-04-10 15:13:00 -070095 cros_install_debug_syms --board=%(board)s %(package)s
96
97To install the debug symbols for all available packages, run:
98
99 cros_install_debug_syms --board=%(board)s --all"""
100
101 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700102 remote_process_name, cgdb_flag, ping, binary):
cmticeb70801a2014-12-11 14:29:34 -0800103 self.board = board
cmticef23cb132015-04-10 15:13:00 -0700104 self.sysroot = None
105 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -0800106 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -0700107 self.run_as_root = False
108 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800109 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -0700110 self.remote = remote.hostname if remote else None
111 self.pid = pid
112 self.remote_process_name = remote_process_name
113 # Port used for sending ssh commands to DUT.
114 self.remote_port = remote.port if remote else None
115 # Port for communicating between gdb & gdbserver.
116 self.gdbserver_port = remote_access.GetUnusedPort()
117 self.ssh_settings = remote_access.CompileSSHConnectSettings(
118 **self._EXTRA_SSH_SETTINGS)
119 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800120 self.framework = 'auto'
121 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700122 self.device = None
123 self.cross_gdb = None
124 self.ping = ping
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700125 self.binary = binary
126 self.in_chroot = None
127 self.chrome_path = None
128 self.sdk_path = None
129
130 def IsInChroot(self):
131 """Decide whether we are in chroot or chrome-sdk."""
Mike Frysinger80de5012019-08-01 14:10:53 -0400132 return os.path.exists('/mnt/host/source/chromite/')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700133
134 def SimpleChromeGdb(self):
135 """Get the name of the cross gdb based on board name."""
Ben Pastene8d754972019-12-04 15:03:23 -0800136 bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
137 cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
138 self.sdk_path, self.board)
139 bin_path = os.path.join(bin_path, 'bin')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700140 for f in os.listdir(bin_path):
141 if f.endswith('gdb'):
142 return os.path.join(bin_path, f)
Ben Pastene8d754972019-12-04 15:03:23 -0800143 raise GdbMissingDebuggerError('Cannot find cross gdb for %s.' % self.board)
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700144
145 def SimpleChromeSysroot(self):
146 """Get the sysroot in simple chrome."""
Ben Pastene8d754972019-12-04 15:03:23 -0800147 sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
148 constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board)
149 if not sysroot:
Jack Rosenthal9e40ae12019-12-02 10:18:10 -0700150 raise GdbMissingSysrootError('Cannot find sysroot for %s at %s'
Ben Pastene8d754972019-12-04 15:03:23 -0800151 % (self.board, self.sdk_path))
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700152 return sysroot
153
154 def GetSimpleChromeBinary(self):
155 """Get path to the binary in simple chrome."""
156 if self.binary:
157 return self.binary
158
159 output_dir = os.path.join(self.chrome_path, 'src',
160 'out_{}'.format(self.board))
161 target_binary = None
162 binary_name = os.path.basename(self.inf_cmd)
163 for root, _, files in os.walk(output_dir):
164 for f in files:
165 if f == binary_name:
Mike Frysinger82b059e2018-07-14 00:46:18 -0400166 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700167 target_binary = os.path.join(root, f)
168 else:
169 raise GdbSimpleChromeBinaryError(
170 'There are multiple %s under %s. Please specify the path to '
171 'the binary via --binary'% binary_name, output_dir)
Mike Frysinger82b059e2018-07-14 00:46:18 -0400172 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700173 raise GdbSimpleChromeBinaryError('There is no %s under %s.'
Ben Pastene8d754972019-12-04 15:03:23 -0800174 % (binary_name, output_dir))
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700175 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800176
cmticef23cb132015-04-10 15:13:00 -0700177 def VerifyAndFinishInitialization(self, device):
178 """Verify files/processes exist and flags are correct."""
179 if not self.board:
180 if self.remote:
181 self.board = cros_build_lib.GetBoard(device_board=device.board,
Eashan Bhatt2f01e422019-07-25 10:31:04 -0700182 override_board=self.board,
183 strict=True)
cmticef23cb132015-04-10 15:13:00 -0700184 else:
185 raise GdbCannotDetectBoardError('Cannot determine which board to use. '
186 'Please specify the with --board flag.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700187 self.in_chroot = self.IsInChroot()
cmticef23cb132015-04-10 15:13:00 -0700188 self.prompt = '(%s-gdb) ' % self.board
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700189 if self.in_chroot:
190 self.sysroot = cros_build_lib.GetSysroot(board=self.board)
191 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
192 self.cross_gdb = self.GetCrossGdb()
193 else:
194 self.chrome_path = os.path.realpath(os.path.join(os.path.dirname(
Mike Frysinger80de5012019-08-01 14:10:53 -0400195 os.path.realpath(__file__)), '../../../..'))
Ben Pastene8d754972019-12-04 15:03:23 -0800196 self.sdk_path = path_util.FindCacheDir()
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700197 self.sysroot = self.SimpleChromeSysroot()
198 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700199
200 if self.remote:
201
202 # If given remote process name, find pid & inf_cmd on remote device.
203 if self.remote_process_name or self.pid:
204 self._FindRemoteProcess(device)
205
206 # Verify that sysroot is valid (exists).
207 if not os.path.isdir(self.sysroot):
208 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
209 self.sysroot)
210
211 self.device = device
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700212 if not self.in_chroot:
213 return
214
cmticef23cb132015-04-10 15:13:00 -0700215 sysroot_inf_cmd = ''
216 if self.inf_cmd:
217 sysroot_inf_cmd = os.path.join(self.sysroot,
218 self.inf_cmd.lstrip('/'))
219
220 # Verify that inf_cmd, if given, exists.
221 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
222 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
223 sysroot_inf_cmd)
224
225 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
226 # exists. If not, tell user and give them the option of quitting & getting
227 # the debug info.
228 if sysroot_inf_cmd:
Mike Frysinger45602c72019-09-22 02:15:11 -0400229 stripped_info = cros_build_lib.run(['file', sysroot_inf_cmd],
Allen Webb763da8a2020-02-25 14:55:54 -0800230 capture_output=True,
231 encoding='utf-8').stdout
Mike Frysinger266e4ff2018-07-14 00:41:05 -0400232 if ' not stripped' not in stripped_info:
cmticef23cb132015-04-10 15:13:00 -0700233 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
234 self.inf_cmd.lstrip('/'))
235 debug_file += '.debug'
236 if not os.path.exists(debug_file):
237 equery = 'equery-%s' % self.board
Mike Frysinger45602c72019-09-22 02:15:11 -0400238 package = cros_build_lib.run([equery, '-q', 'b', self.inf_cmd],
Allen Webb763da8a2020-02-25 14:55:54 -0800239 capture_output=True,
240 encoding='utf-8').stdout
Lann Martinffb95162018-08-28 12:02:54 -0600241 # pylint: disable=logging-not-lazy
cmticef23cb132015-04-10 15:13:00 -0700242 logging.info(self._MISSING_DEBUG_INFO_MSG % {
243 'board': self.board,
244 'inf_cmd': self.inf_cmd,
245 'package': package,
246 'debug_file': debug_file})
247 answer = cros_build_lib.BooleanPrompt()
248 if not answer:
249 raise GdbEarlyExitError('Exiting early, at user request.')
250
251 # Set up qemu, if appropriate.
252 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800253 if qemu_arch is None:
254 self.framework = 'ldso'
255 else:
256 self.framework = 'qemu'
257 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800258
cmticef23cb132015-04-10 15:13:00 -0700259 if self.remote:
260 # Verify cgdb flag info.
261 if self.cgdb:
262 if osutils.Which('cgdb') is None:
263 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
264 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800265
cmticef23cb132015-04-10 15:13:00 -0700266 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800267 """Returns the given path with any sysroot prefix removed."""
268 # If the sysroot is /, then the paths are already normalized.
269 if self.sysroot != '/' and path.startswith(self.sysroot):
270 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800271 return path
272
273 @staticmethod
274 def GetNonRootAccount():
275 """Return details about the non-root account we want to use.
276
277 Returns:
278 A tuple of (username, uid, gid, home).
279 """
280 return (
281 os.environ.get('SUDO_USER', 'nobody'),
282 int(os.environ.get('SUDO_UID', '65534')),
283 int(os.environ.get('SUDO_GID', '65534')),
284 # Should we find a better home?
285 '/tmp/portage',
286 )
287
288 @staticmethod
289 @contextlib.contextmanager
290 def LockDb(db):
291 """Lock an account database.
292
293 We use the same algorithm as shadow/user.eclass. This way we don't race
294 and corrupt things in parallel.
295 """
296 lock = '%s.lock' % db
297 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
298
299 # First try forever to grab the lock.
300 retry = lambda e: e.errno == errno.EEXIST
301 # Retry quickly at first, but slow down over time.
302 try:
303 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700304 except Exception as e:
305 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800306
307 # Yield while holding the lock, but try to clean it no matter what.
308 try:
309 os.unlink(tmplock)
310 yield lock
311 finally:
312 os.unlink(lock)
313
314 def SetupUser(self):
315 """Propogate the user name<->id mapping from outside the chroot.
316
317 Some unittests use getpwnam($USER), as does bash. If the account
318 is not registered in the sysroot, they get back errors.
319 """
320 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
321 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
322 SDK_GECOS = 'ChromeOS Developer'
323
324 user, uid, gid, home = self.GetNonRootAccount()
325 if user == 'nobody':
326 return
327
328 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
329 with self.LockDb(passwd_db):
330 data = osutils.ReadFile(passwd_db)
331 accts = data.splitlines()
332 for acct in accts:
333 passwd = acct.split(':')
334 if passwd[0] == user:
335 # Did the sdk make this account?
336 if passwd[4] == SDK_GECOS:
337 # Don't modify it (see below) since we didn't create it.
338 return
339
340 # Did we make this account?
341 if passwd[4] != MAGIC_GECOS:
342 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
343 (passwd_db, user))
344
345 # Maybe we should see if it needs to be updated? Like if they
346 # changed UIDs? But we don't really check that elsewhere ...
347 return
348
349 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
350 'name': user,
351 'uid': uid,
352 'gid': gid,
353 'gecos': MAGIC_GECOS,
354 'homedir': home,
355 'shell': '/bin/bash',
356 }
357 with open(passwd_db, 'a') as f:
358 if data[-1] != '\n':
359 f.write('\n')
360 f.write('%s\n' % acct)
361
cmticef23cb132015-04-10 15:13:00 -0700362 def _FindRemoteProcess(self, device):
363 """Find a named process (or a pid) running on a remote device."""
364 if not self.remote_process_name and not self.pid:
365 return
cmticeb70801a2014-12-11 14:29:34 -0800366
cmticef23cb132015-04-10 15:13:00 -0700367 if self.remote_process_name:
368 # Look for a process with the specified name on the remote device; if
369 # found, get its pid.
370 pname = self.remote_process_name
371 if pname == 'browser':
372 all_chrome_pids = set(device.GetRunningPids(
373 '/opt/google/chrome/chrome'))
cmticef23cb132015-04-10 15:13:00 -0700374 non_main_chrome_pids = set(device.GetRunningPids('type='))
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500375 pids = list(all_chrome_pids - non_main_chrome_pids)
cmticef23cb132015-04-10 15:13:00 -0700376 elif pname == 'renderer' or pname == 'gpu-process':
377 pids = device.GetRunningPids('type=%s'% pname)
378 else:
379 pids = device.GetRunningPids(pname)
380
381 if pids:
382 if len(pids) == 1:
383 self.pid = pids[0]
384 else:
385 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
386 'You must specify the correct pid.'
387 % (pname, repr(pids)))
388 else:
389 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
390 (pname, self.remote))
391
392 # Find full path for process, from pid (and verify pid).
393 command = [
394 'readlink',
395 '-e', '/proc/%s/exe' % self.pid,
396 ]
397 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400398 res = device.run(command, capture_output=True)
cmticef23cb132015-04-10 15:13:00 -0700399 if res.returncode == 0:
400 self.inf_cmd = res.output.rstrip('\n')
401 except cros_build_lib.RunCommandError:
402 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
403 'with pid %s on %s' %
404 (self.pid, self.remote))
405
406 def GetCrossGdb(self):
407 """Find the appropriate cross-version of gdb for the board."""
408 toolchains = toolchain.GetToolchainsForBoard(self.board)
Mike Frysinger818d9632019-08-24 14:43:05 -0400409 tc = list(toolchain.FilterToolchains(toolchains, 'default', True))
cmticef23cb132015-04-10 15:13:00 -0700410 cross_gdb = tc[0] + '-gdb'
411 if not osutils.Which(cross_gdb):
412 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
413 'setup_board?' % cross_gdb)
414 return cross_gdb
415
Raul E Rangel746c45d2018-05-09 09:27:31 -0600416 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700417 """Generate list of commands with which to initialize the gdb session."""
418 gdb_init_commands = []
419
420 if self.remote:
421 sysroot_var = self.sysroot
422 else:
423 sysroot_var = '/'
424
425 gdb_init_commands = [
426 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700427 'set prompt %s' % self.prompt,
428 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700429 if self.in_chroot:
430 gdb_init_commands += [
431 'set solib-absolute-prefix %s' % sysroot_var,
432 'set solib-search-path %s' % sysroot_var,
433 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
434 ]
cmticef23cb132015-04-10 15:13:00 -0700435
Raul E Rangel746c45d2018-05-09 09:27:31 -0600436 if device:
437 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
438
439 ssh_cmd.extend(['--', 'gdbserver'])
440
441 if self.pid:
442 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
443 target_type = 'remote'
444 elif inferior_cmd:
445 ssh_cmd.extend(['-', inferior_cmd])
446 ssh_cmd.extend(self.inf_args)
447 target_type = 'remote'
448 else:
449 ssh_cmd.extend(['--multi', 'stdio'])
450 target_type = 'extended-remote'
451
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600452 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700453
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700454 if self.in_chroot:
455 if inferior_cmd:
456 gdb_init_commands.append(
457 'file %s' % os.path.join(sysroot_var,
458 inferior_cmd.lstrip(os.sep)))
459 else:
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700460 binary = self.GetSimpleChromeBinary()
461 gdb_init_commands += [
462 'set debug-file-directory %s' % os.path.dirname(binary),
463 'file %s' % binary
464 ]
Raul E Rangel746c45d2018-05-09 09:27:31 -0600465
466 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700467 else:
468 if inferior_cmd:
469 gdb_init_commands.append('file %s ' % inferior_cmd)
470 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
471
472 return gdb_init_commands
473
474 def RunRemote(self):
475 """Handle remote debugging, via gdbserver & cross debugger."""
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600476 with remote_access.ChromiumOSDeviceHandler(
477 self.remote,
478 port=self.remote_port,
479 connect_settings=self.ssh_settings,
480 ping=self.ping) as device:
cmticef23cb132015-04-10 15:13:00 -0700481
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600482 self.VerifyAndFinishInitialization(device)
483 gdb_cmd = self.cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700484
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600485 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
486 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
487 gdb_args += self.gdb_args
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600488
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600489 if self.cgdb:
490 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
491 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700492
Mike Frysinger45602c72019-09-22 02:15:11 -0400493 cros_build_lib.run(
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600494 [gdb_cmd] + gdb_args,
495 ignore_sigint=True,
496 print_cmd=True,
497 cwd=self.sysroot)
cmticef23cb132015-04-10 15:13:00 -0700498
499 def Run(self):
500 """Runs the debugger in a proper environment (e.g. qemu)."""
501
502 self.VerifyAndFinishInitialization(None)
503 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800504 if self.framework == 'qemu':
505 self.qemu.Install(self.sysroot)
506 self.qemu.RegisterBinfmt()
507
cmticeb70801a2014-12-11 14:29:34 -0800508 for mount in self._BIND_MOUNT_PATHS:
509 path = os.path.join(self.sysroot, mount)
510 osutils.SafeMakedirs(path)
511 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
512
cmticef23cb132015-04-10 15:13:00 -0700513 gdb_cmd = self._GDB
514 inferior_cmd = self.inf_cmd
515
cmtice932e0aa2015-02-27 11:49:12 -0800516 gdb_argv = self.gdb_args[:]
517 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700518 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800519 # Some programs expect to find data files via $CWD, so doing a chroot
520 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700521 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800522
cmticeb70801a2014-12-11 14:29:34 -0800523 os.chroot(self.sysroot)
524 os.chdir(cwd)
525 # The TERM the user is leveraging might not exist in the sysroot.
526 # Force a sane default that supports standard color sequences.
527 os.environ['TERM'] = 'ansi'
528 # Some progs want this like bash else they get super confused.
529 os.environ['PWD'] = cwd
530 if not self.run_as_root:
531 _, uid, gid, home = self.GetNonRootAccount()
532 os.setgid(gid)
533 os.setuid(uid)
534 os.environ['HOME'] = home
535
cmticef23cb132015-04-10 15:13:00 -0700536 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800537
cmticef23cb132015-04-10 15:13:00 -0700538 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
539 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800540 gdb_args += self.gdb_args
541
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600542 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800543
544
cmtice932e0aa2015-02-27 11:49:12 -0800545def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800546 """Re-execute gdb as root.
547
548 We often need to do things as root, so make sure we're that. Like chroot
549 for proper library environment or do bind mounts.
550
551 Also unshare the mount namespace so as to ensure that doing bind mounts for
552 tests don't leak out to the normal chroot. Also unshare the UTS namespace
553 so changes to `hostname` do not impact the host.
554 """
555 if os.geteuid() != 0:
556 cmd = ['sudo', '-E', '--'] + argv
557 os.execvp(cmd[0], cmd)
558 else:
cmtice932e0aa2015-02-27 11:49:12 -0800559 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
560
561
cmticef23cb132015-04-10 15:13:00 -0700562def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800563 """Look for the name of the inferior (to be debugged) in arg list."""
564
565 program_name = ''
566 new_list = []
567 for item in arg_list:
568 if item[0] == '-':
569 new_list.append(item)
570 elif not program_name:
571 program_name = item
572 else:
573 raise RuntimeError('Found multiple program names: %s %s'
574 % (program_name, item))
575
576 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800577
578
579def main(argv):
580
581 parser = commandline.ArgumentParser(description=__doc__)
582
cmticef23cb132015-04-10 15:13:00 -0700583 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800584 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700585 parser.add_argument('-g', '--gdb_args', action='append', default=[],
586 help='Arguments to gdb itself. If multiple arguments are'
Mike Frysinger80de5012019-08-01 14:10:53 -0400587 " passed, each argument needs a separate '-g' flag.")
cmticef23cb132015-04-10 15:13:00 -0700588 parser.add_argument(
589 '--remote', default=None,
590 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
591 help='Remote device on which to run the binary. Use'
592 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
593 ' already running local virtual machine.')
594 parser.add_argument('--pid', default='',
595 help='Process ID of the (already) running process on the'
596 ' remote device to which to attach.')
597 parser.add_argument('--remote_pid', dest='pid', default='',
598 help='Deprecated alias for --pid.')
599 parser.add_argument('--no-ping', dest='ping', default=True,
600 action='store_false',
601 help='Do not ping remote before attempting to connect.')
602 parser.add_argument('--attach', dest='attach_name', default='',
603 help='Name of existing process to which to attach, on'
604 ' remote device (remote debugging only). "--attach'
605 ' browser" will find the main chrome browser process;'
606 ' "--attach renderer" will find a chrome renderer'
607 ' process; "--attach gpu-process" will find the chrome'
608 ' gpu process.')
609 parser.add_argument('--cgdb', default=False,
610 action='store_true',
611 help='Use cgdb curses interface rather than plain gdb.'
612 'This option is only valid for remote debugging.')
613 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
614 help='Arguments for gdb to pass to the program being'
615 ' debugged. These are positional and must come at the end'
616 ' of the command line. This will not work if attaching'
617 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700618 parser.add_argument('--binary', default='',
619 help='full path to the binary being debuged.'
620 ' This is only useful for simple chrome.'
621 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800622
623 options = parser.parse_args(argv)
624 options.Freeze()
625
cmtice932e0aa2015-02-27 11:49:12 -0800626 gdb_args = []
627 inf_args = []
628 inf_cmd = ''
629
cmticef23cb132015-04-10 15:13:00 -0700630 if options.inf_args:
631 inf_cmd = options.inf_args[0]
632 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800633
cmticef23cb132015-04-10 15:13:00 -0700634 if options.gdb_args:
635 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800636
637 if inf_cmd:
638 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
639 inf_cmd.lstrip('/'))
640 if not os.path.exists(fname):
641 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700642 else:
643 if inf_args:
644 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800645
cmticef23cb132015-04-10 15:13:00 -0700646 if inf_args and (options.pid or options.attach_name):
647 parser.error('Cannot pass arguments to an already'
648 ' running process (--remote-pid or --attach).')
649
650 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700651 if options.attach_name and options.attach_name == 'browser':
652 inf_cmd = '/opt/google/chrome/chrome'
653 else:
654 if options.cgdb:
655 parser.error('--cgdb option can only be used with remote debugging.')
656 if options.pid:
657 parser.error('Must specify a remote device (--remote) if you want '
658 'to attach to a remote pid.')
659 if options.attach_name:
660 parser.error('Must specify remote device (--remote) when using'
661 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700662 if options.binary:
663 if not os.path.exists(options.binary):
664 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800665
cmticeb70801a2014-12-11 14:29:34 -0800666 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700667 if not options.remote:
668 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800669
cmticef23cb132015-04-10 15:13:00 -0700670 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
671 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700672 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800673
cmticef23cb132015-04-10 15:13:00 -0700674 try:
675 if options.remote:
676 gdb.RunRemote()
677 else:
678 gdb.Run()
679
680 except GdbException as e:
681 if options.debug:
682 raise
683 else:
684 raise cros_build_lib.Die(str(e))