blob: 32e3275d536b8f22a3c252c948ff4be93a78232a [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],
230 capture_output=True).output
Mike Frysinger266e4ff2018-07-14 00:41:05 -0400231 if ' not stripped' not in stripped_info:
cmticef23cb132015-04-10 15:13:00 -0700232 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
233 self.inf_cmd.lstrip('/'))
234 debug_file += '.debug'
235 if not os.path.exists(debug_file):
236 equery = 'equery-%s' % self.board
Mike Frysinger45602c72019-09-22 02:15:11 -0400237 package = cros_build_lib.run([equery, '-q', 'b', self.inf_cmd],
238 capture_output=True).output
Lann Martinffb95162018-08-28 12:02:54 -0600239 # pylint: disable=logging-not-lazy
cmticef23cb132015-04-10 15:13:00 -0700240 logging.info(self._MISSING_DEBUG_INFO_MSG % {
241 'board': self.board,
242 'inf_cmd': self.inf_cmd,
243 'package': package,
244 'debug_file': debug_file})
245 answer = cros_build_lib.BooleanPrompt()
246 if not answer:
247 raise GdbEarlyExitError('Exiting early, at user request.')
248
249 # Set up qemu, if appropriate.
250 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800251 if qemu_arch is None:
252 self.framework = 'ldso'
253 else:
254 self.framework = 'qemu'
255 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800256
cmticef23cb132015-04-10 15:13:00 -0700257 if self.remote:
258 # Verify cgdb flag info.
259 if self.cgdb:
260 if osutils.Which('cgdb') is None:
261 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
262 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800263
cmticef23cb132015-04-10 15:13:00 -0700264 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800265 """Returns the given path with any sysroot prefix removed."""
266 # If the sysroot is /, then the paths are already normalized.
267 if self.sysroot != '/' and path.startswith(self.sysroot):
268 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800269 return path
270
271 @staticmethod
272 def GetNonRootAccount():
273 """Return details about the non-root account we want to use.
274
275 Returns:
276 A tuple of (username, uid, gid, home).
277 """
278 return (
279 os.environ.get('SUDO_USER', 'nobody'),
280 int(os.environ.get('SUDO_UID', '65534')),
281 int(os.environ.get('SUDO_GID', '65534')),
282 # Should we find a better home?
283 '/tmp/portage',
284 )
285
286 @staticmethod
287 @contextlib.contextmanager
288 def LockDb(db):
289 """Lock an account database.
290
291 We use the same algorithm as shadow/user.eclass. This way we don't race
292 and corrupt things in parallel.
293 """
294 lock = '%s.lock' % db
295 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
296
297 # First try forever to grab the lock.
298 retry = lambda e: e.errno == errno.EEXIST
299 # Retry quickly at first, but slow down over time.
300 try:
301 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700302 except Exception as e:
303 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800304
305 # Yield while holding the lock, but try to clean it no matter what.
306 try:
307 os.unlink(tmplock)
308 yield lock
309 finally:
310 os.unlink(lock)
311
312 def SetupUser(self):
313 """Propogate the user name<->id mapping from outside the chroot.
314
315 Some unittests use getpwnam($USER), as does bash. If the account
316 is not registered in the sysroot, they get back errors.
317 """
318 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
319 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
320 SDK_GECOS = 'ChromeOS Developer'
321
322 user, uid, gid, home = self.GetNonRootAccount()
323 if user == 'nobody':
324 return
325
326 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
327 with self.LockDb(passwd_db):
328 data = osutils.ReadFile(passwd_db)
329 accts = data.splitlines()
330 for acct in accts:
331 passwd = acct.split(':')
332 if passwd[0] == user:
333 # Did the sdk make this account?
334 if passwd[4] == SDK_GECOS:
335 # Don't modify it (see below) since we didn't create it.
336 return
337
338 # Did we make this account?
339 if passwd[4] != MAGIC_GECOS:
340 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
341 (passwd_db, user))
342
343 # Maybe we should see if it needs to be updated? Like if they
344 # changed UIDs? But we don't really check that elsewhere ...
345 return
346
347 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
348 'name': user,
349 'uid': uid,
350 'gid': gid,
351 'gecos': MAGIC_GECOS,
352 'homedir': home,
353 'shell': '/bin/bash',
354 }
355 with open(passwd_db, 'a') as f:
356 if data[-1] != '\n':
357 f.write('\n')
358 f.write('%s\n' % acct)
359
cmticef23cb132015-04-10 15:13:00 -0700360 def _FindRemoteProcess(self, device):
361 """Find a named process (or a pid) running on a remote device."""
362 if not self.remote_process_name and not self.pid:
363 return
cmticeb70801a2014-12-11 14:29:34 -0800364
cmticef23cb132015-04-10 15:13:00 -0700365 if self.remote_process_name:
366 # Look for a process with the specified name on the remote device; if
367 # found, get its pid.
368 pname = self.remote_process_name
369 if pname == 'browser':
370 all_chrome_pids = set(device.GetRunningPids(
371 '/opt/google/chrome/chrome'))
cmticef23cb132015-04-10 15:13:00 -0700372 non_main_chrome_pids = set(device.GetRunningPids('type='))
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500373 pids = list(all_chrome_pids - non_main_chrome_pids)
cmticef23cb132015-04-10 15:13:00 -0700374 elif pname == 'renderer' or pname == 'gpu-process':
375 pids = device.GetRunningPids('type=%s'% pname)
376 else:
377 pids = device.GetRunningPids(pname)
378
379 if pids:
380 if len(pids) == 1:
381 self.pid = pids[0]
382 else:
383 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
384 'You must specify the correct pid.'
385 % (pname, repr(pids)))
386 else:
387 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
388 (pname, self.remote))
389
390 # Find full path for process, from pid (and verify pid).
391 command = [
392 'readlink',
393 '-e', '/proc/%s/exe' % self.pid,
394 ]
395 try:
396 res = device.RunCommand(command, capture_output=True)
397 if res.returncode == 0:
398 self.inf_cmd = res.output.rstrip('\n')
399 except cros_build_lib.RunCommandError:
400 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
401 'with pid %s on %s' %
402 (self.pid, self.remote))
403
404 def GetCrossGdb(self):
405 """Find the appropriate cross-version of gdb for the board."""
406 toolchains = toolchain.GetToolchainsForBoard(self.board)
Mike Frysinger818d9632019-08-24 14:43:05 -0400407 tc = list(toolchain.FilterToolchains(toolchains, 'default', True))
cmticef23cb132015-04-10 15:13:00 -0700408 cross_gdb = tc[0] + '-gdb'
409 if not osutils.Which(cross_gdb):
410 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
411 'setup_board?' % cross_gdb)
412 return cross_gdb
413
Raul E Rangel746c45d2018-05-09 09:27:31 -0600414 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700415 """Generate list of commands with which to initialize the gdb session."""
416 gdb_init_commands = []
417
418 if self.remote:
419 sysroot_var = self.sysroot
420 else:
421 sysroot_var = '/'
422
423 gdb_init_commands = [
424 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700425 'set prompt %s' % self.prompt,
426 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700427 if self.in_chroot:
428 gdb_init_commands += [
429 'set solib-absolute-prefix %s' % sysroot_var,
430 'set solib-search-path %s' % sysroot_var,
431 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
432 ]
cmticef23cb132015-04-10 15:13:00 -0700433
Raul E Rangel746c45d2018-05-09 09:27:31 -0600434 if device:
435 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
436
437 ssh_cmd.extend(['--', 'gdbserver'])
438
439 if self.pid:
440 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
441 target_type = 'remote'
442 elif inferior_cmd:
443 ssh_cmd.extend(['-', inferior_cmd])
444 ssh_cmd.extend(self.inf_args)
445 target_type = 'remote'
446 else:
447 ssh_cmd.extend(['--multi', 'stdio'])
448 target_type = 'extended-remote'
449
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600450 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700451
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700452 if self.in_chroot:
453 if inferior_cmd:
454 gdb_init_commands.append(
455 'file %s' % os.path.join(sysroot_var,
456 inferior_cmd.lstrip(os.sep)))
457 else:
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700458 binary = self.GetSimpleChromeBinary()
459 gdb_init_commands += [
460 'set debug-file-directory %s' % os.path.dirname(binary),
461 'file %s' % binary
462 ]
Raul E Rangel746c45d2018-05-09 09:27:31 -0600463
464 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700465 else:
466 if inferior_cmd:
467 gdb_init_commands.append('file %s ' % inferior_cmd)
468 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
469
470 return gdb_init_commands
471
472 def RunRemote(self):
473 """Handle remote debugging, via gdbserver & cross debugger."""
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600474 with remote_access.ChromiumOSDeviceHandler(
475 self.remote,
476 port=self.remote_port,
477 connect_settings=self.ssh_settings,
478 ping=self.ping) as device:
cmticef23cb132015-04-10 15:13:00 -0700479
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600480 self.VerifyAndFinishInitialization(device)
481 gdb_cmd = self.cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700482
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600483 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
484 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
485 gdb_args += self.gdb_args
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600486
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600487 if self.cgdb:
488 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
489 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700490
Mike Frysinger45602c72019-09-22 02:15:11 -0400491 cros_build_lib.run(
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600492 [gdb_cmd] + gdb_args,
493 ignore_sigint=True,
494 print_cmd=True,
495 cwd=self.sysroot)
cmticef23cb132015-04-10 15:13:00 -0700496
497 def Run(self):
498 """Runs the debugger in a proper environment (e.g. qemu)."""
499
500 self.VerifyAndFinishInitialization(None)
501 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800502 if self.framework == 'qemu':
503 self.qemu.Install(self.sysroot)
504 self.qemu.RegisterBinfmt()
505
cmticeb70801a2014-12-11 14:29:34 -0800506 for mount in self._BIND_MOUNT_PATHS:
507 path = os.path.join(self.sysroot, mount)
508 osutils.SafeMakedirs(path)
509 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
510
cmticef23cb132015-04-10 15:13:00 -0700511 gdb_cmd = self._GDB
512 inferior_cmd = self.inf_cmd
513
cmtice932e0aa2015-02-27 11:49:12 -0800514 gdb_argv = self.gdb_args[:]
515 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700516 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800517 # Some programs expect to find data files via $CWD, so doing a chroot
518 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700519 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800520
cmticeb70801a2014-12-11 14:29:34 -0800521 os.chroot(self.sysroot)
522 os.chdir(cwd)
523 # The TERM the user is leveraging might not exist in the sysroot.
524 # Force a sane default that supports standard color sequences.
525 os.environ['TERM'] = 'ansi'
526 # Some progs want this like bash else they get super confused.
527 os.environ['PWD'] = cwd
528 if not self.run_as_root:
529 _, uid, gid, home = self.GetNonRootAccount()
530 os.setgid(gid)
531 os.setuid(uid)
532 os.environ['HOME'] = home
533
cmticef23cb132015-04-10 15:13:00 -0700534 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800535
cmticef23cb132015-04-10 15:13:00 -0700536 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
537 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800538 gdb_args += self.gdb_args
539
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600540 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800541
542
cmtice932e0aa2015-02-27 11:49:12 -0800543def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800544 """Re-execute gdb as root.
545
546 We often need to do things as root, so make sure we're that. Like chroot
547 for proper library environment or do bind mounts.
548
549 Also unshare the mount namespace so as to ensure that doing bind mounts for
550 tests don't leak out to the normal chroot. Also unshare the UTS namespace
551 so changes to `hostname` do not impact the host.
552 """
553 if os.geteuid() != 0:
554 cmd = ['sudo', '-E', '--'] + argv
555 os.execvp(cmd[0], cmd)
556 else:
cmtice932e0aa2015-02-27 11:49:12 -0800557 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
558
559
cmticef23cb132015-04-10 15:13:00 -0700560def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800561 """Look for the name of the inferior (to be debugged) in arg list."""
562
563 program_name = ''
564 new_list = []
565 for item in arg_list:
566 if item[0] == '-':
567 new_list.append(item)
568 elif not program_name:
569 program_name = item
570 else:
571 raise RuntimeError('Found multiple program names: %s %s'
572 % (program_name, item))
573
574 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800575
576
577def main(argv):
578
579 parser = commandline.ArgumentParser(description=__doc__)
580
cmticef23cb132015-04-10 15:13:00 -0700581 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800582 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700583 parser.add_argument('-g', '--gdb_args', action='append', default=[],
584 help='Arguments to gdb itself. If multiple arguments are'
Mike Frysinger80de5012019-08-01 14:10:53 -0400585 " passed, each argument needs a separate '-g' flag.")
cmticef23cb132015-04-10 15:13:00 -0700586 parser.add_argument(
587 '--remote', default=None,
588 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
589 help='Remote device on which to run the binary. Use'
590 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
591 ' already running local virtual machine.')
592 parser.add_argument('--pid', default='',
593 help='Process ID of the (already) running process on the'
594 ' remote device to which to attach.')
595 parser.add_argument('--remote_pid', dest='pid', default='',
596 help='Deprecated alias for --pid.')
597 parser.add_argument('--no-ping', dest='ping', default=True,
598 action='store_false',
599 help='Do not ping remote before attempting to connect.')
600 parser.add_argument('--attach', dest='attach_name', default='',
601 help='Name of existing process to which to attach, on'
602 ' remote device (remote debugging only). "--attach'
603 ' browser" will find the main chrome browser process;'
604 ' "--attach renderer" will find a chrome renderer'
605 ' process; "--attach gpu-process" will find the chrome'
606 ' gpu process.')
607 parser.add_argument('--cgdb', default=False,
608 action='store_true',
609 help='Use cgdb curses interface rather than plain gdb.'
610 'This option is only valid for remote debugging.')
611 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
612 help='Arguments for gdb to pass to the program being'
613 ' debugged. These are positional and must come at the end'
614 ' of the command line. This will not work if attaching'
615 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700616 parser.add_argument('--binary', default='',
617 help='full path to the binary being debuged.'
618 ' This is only useful for simple chrome.'
619 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800620
621 options = parser.parse_args(argv)
622 options.Freeze()
623
cmtice932e0aa2015-02-27 11:49:12 -0800624 gdb_args = []
625 inf_args = []
626 inf_cmd = ''
627
cmticef23cb132015-04-10 15:13:00 -0700628 if options.inf_args:
629 inf_cmd = options.inf_args[0]
630 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800631
cmticef23cb132015-04-10 15:13:00 -0700632 if options.gdb_args:
633 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800634
635 if inf_cmd:
636 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
637 inf_cmd.lstrip('/'))
638 if not os.path.exists(fname):
639 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700640 else:
641 if inf_args:
642 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800643
cmticef23cb132015-04-10 15:13:00 -0700644 if inf_args and (options.pid or options.attach_name):
645 parser.error('Cannot pass arguments to an already'
646 ' running process (--remote-pid or --attach).')
647
648 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700649 if options.attach_name and options.attach_name == 'browser':
650 inf_cmd = '/opt/google/chrome/chrome'
651 else:
652 if options.cgdb:
653 parser.error('--cgdb option can only be used with remote debugging.')
654 if options.pid:
655 parser.error('Must specify a remote device (--remote) if you want '
656 'to attach to a remote pid.')
657 if options.attach_name:
658 parser.error('Must specify remote device (--remote) when using'
659 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700660 if options.binary:
661 if not os.path.exists(options.binary):
662 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800663
cmticeb70801a2014-12-11 14:29:34 -0800664 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700665 if not options.remote:
666 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800667
cmticef23cb132015-04-10 15:13:00 -0700668 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
669 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700670 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800671
cmticef23cb132015-04-10 15:13:00 -0700672 try:
673 if options.remote:
674 gdb.RunRemote()
675 else:
676 gdb.Run()
677
678 except GdbException as e:
679 if options.debug:
680 raise
681 else:
682 raise cros_build_lib.Die(str(e))