blob: 7ec82edf35c00fe54e1d108b91dfda51f358af7d [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
21from chromite.lib import commandline
22from chromite.lib import cros_build_lib
cmticef23cb132015-04-10 15:13:00 -070023from chromite.lib import cros_logging as logging
cmticeb70801a2014-12-11 14:29:34 -080024from 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
cmticef23cb132015-04-10 15:13:00 -070032class GdbException(Exception):
33 """Base exception for this module."""
34
35
36class GdbBadRemoteDeviceError(GdbException):
37 """Raised when remote device does not exist or is not responding."""
38
39
40class GdbMissingSysrootError(GdbException):
41 """Raised when path to sysroot cannot be found in chroot."""
42
43
44class GdbMissingInferiorError(GdbException):
45 """Raised when the binary to be debugged cannot be found."""
46
47
48class GdbMissingDebuggerError(GdbException):
49 """Raised when cannot find correct version of debugger."""
50
51
52class GdbCannotFindRemoteProcessError(GdbException):
53 """Raised when cannot find requested executing process on remote device."""
54
55
56class GdbUnableToStartGdbserverError(GdbException):
57 """Raised when error occurs trying to start gdbserver on remote device."""
58
59
60class GdbTooManyPidsError(GdbException):
61 """Raised when more than one matching pid is found running on device."""
62
63
64class GdbEarlyExitError(GdbException):
65 """Raised when user requests to exit early."""
66
67
68class GdbCannotDetectBoardError(GdbException):
69 """Raised when board isn't specified and can't be automatically determined."""
cmticeb70801a2014-12-11 14:29:34 -080070
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070071class GdbSimpleChromeBinaryError(GdbException):
72 """Raised when none or multiple chrome binaries are under out_${board} dir."""
cmtice932e0aa2015-02-27 11:49:12 -080073
cmticeb70801a2014-12-11 14:29:34 -080074class BoardSpecificGdb(object):
75 """Framework for running gdb."""
76
77 _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
cmticef23cb132015-04-10 15:13:00 -070078 _GDB = '/usr/bin/gdb'
Raul E Rangel746c45d2018-05-09 09:27:31 -060079 _EXTRA_SSH_SETTINGS = {
80 'CheckHostIP': 'no',
81 'BatchMode': 'yes',
82 'LogLevel': 'QUIET'
83 }
cmticef23cb132015-04-10 15:13:00 -070084 _MISSING_DEBUG_INFO_MSG = """
85%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
86 The debug symbols for that package may not be installed. To install the debug
87 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080088
cmticef23cb132015-04-10 15:13:00 -070089 cros_install_debug_syms --board=%(board)s %(package)s
90
91To install the debug symbols for all available packages, run:
92
93 cros_install_debug_syms --board=%(board)s --all"""
94
95 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070096 remote_process_name, cgdb_flag, ping, binary):
cmticeb70801a2014-12-11 14:29:34 -080097 self.board = board
cmticef23cb132015-04-10 15:13:00 -070098 self.sysroot = None
99 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -0800100 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -0700101 self.run_as_root = False
102 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800103 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -0700104 self.remote = remote.hostname if remote else None
105 self.pid = pid
106 self.remote_process_name = remote_process_name
107 # Port used for sending ssh commands to DUT.
108 self.remote_port = remote.port if remote else None
109 # Port for communicating between gdb & gdbserver.
110 self.gdbserver_port = remote_access.GetUnusedPort()
111 self.ssh_settings = remote_access.CompileSSHConnectSettings(
112 **self._EXTRA_SSH_SETTINGS)
113 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800114 self.framework = 'auto'
115 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700116 self.device = None
117 self.cross_gdb = None
118 self.ping = ping
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700119 self.binary = binary
120 self.in_chroot = None
121 self.chrome_path = None
122 self.sdk_path = None
123
124 def IsInChroot(self):
125 """Decide whether we are in chroot or chrome-sdk."""
Mike Frysinger80de5012019-08-01 14:10:53 -0400126 return os.path.exists('/mnt/host/source/chromite/')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700127
128 def SimpleChromeGdb(self):
129 """Get the name of the cross gdb based on board name."""
130 bin_path = self.board + '+' + os.environ['SDK_VERSION'] + '+' + \
131 'target_toolchain'
132 bin_path = os.path.join(self.sdk_path, bin_path, 'bin')
133 for f in os.listdir(bin_path):
134 if f.endswith('gdb'):
135 return os.path.join(bin_path, f)
136 raise GdbMissingDebuggerError('Cannot find cros gdb for %s.'
137 % self.board)
138
139 def SimpleChromeSysroot(self):
140 """Get the sysroot in simple chrome."""
141 sysroot = self.board + '+' + os.environ['SDK_VERSION'] + \
142 '+' + 'sysroot_chromeos-base_chromeos-chrome.tar.xz'
143 sysroot = os.path.join(self.sdk_path, sysroot)
144 if not os.path.isdir(sysroot):
Jack Rosenthal9e40ae12019-12-02 10:18:10 -0700145 raise GdbMissingSysrootError('Cannot find sysroot for %s at %s'
146 % (self.board, sysroot))
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.'
169 % binary_name, output_dir)
170 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:
185 self.sysroot = cros_build_lib.GetSysroot(board=self.board)
186 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 Pastenec228d492018-07-02 13:53:58 -0700191 self.sdk_path = os.path.join(
192 path_util.FindCacheDir(), 'chrome-sdk/tarballs/')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700193 self.sysroot = self.SimpleChromeSysroot()
194 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700195
196 if self.remote:
197
198 # If given remote process name, find pid & inf_cmd on remote device.
199 if self.remote_process_name or self.pid:
200 self._FindRemoteProcess(device)
201
202 # Verify that sysroot is valid (exists).
203 if not os.path.isdir(self.sysroot):
204 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
205 self.sysroot)
206
207 self.device = device
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700208 if not self.in_chroot:
209 return
210
cmticef23cb132015-04-10 15:13:00 -0700211 sysroot_inf_cmd = ''
212 if self.inf_cmd:
213 sysroot_inf_cmd = os.path.join(self.sysroot,
214 self.inf_cmd.lstrip('/'))
215
216 # Verify that inf_cmd, if given, exists.
217 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
218 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
219 sysroot_inf_cmd)
220
221 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
222 # exists. If not, tell user and give them the option of quitting & getting
223 # the debug info.
224 if sysroot_inf_cmd:
Mike Frysinger45602c72019-09-22 02:15:11 -0400225 stripped_info = cros_build_lib.run(['file', sysroot_inf_cmd],
226 capture_output=True).output
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],
234 capture_output=True).output
Lann Martinffb95162018-08-28 12:02:54 -0600235 # pylint: disable=logging-not-lazy
cmticef23cb132015-04-10 15:13:00 -0700236 logging.info(self._MISSING_DEBUG_INFO_MSG % {
237 'board': self.board,
238 'inf_cmd': self.inf_cmd,
239 'package': package,
240 'debug_file': debug_file})
241 answer = cros_build_lib.BooleanPrompt()
242 if not answer:
243 raise GdbEarlyExitError('Exiting early, at user request.')
244
245 # Set up qemu, if appropriate.
246 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800247 if qemu_arch is None:
248 self.framework = 'ldso'
249 else:
250 self.framework = 'qemu'
251 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800252
cmticef23cb132015-04-10 15:13:00 -0700253 if self.remote:
254 # Verify cgdb flag info.
255 if self.cgdb:
256 if osutils.Which('cgdb') is None:
257 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
258 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800259
cmticef23cb132015-04-10 15:13:00 -0700260 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800261 """Returns the given path with any sysroot prefix removed."""
262 # If the sysroot is /, then the paths are already normalized.
263 if self.sysroot != '/' and path.startswith(self.sysroot):
264 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800265 return path
266
267 @staticmethod
268 def GetNonRootAccount():
269 """Return details about the non-root account we want to use.
270
271 Returns:
272 A tuple of (username, uid, gid, home).
273 """
274 return (
275 os.environ.get('SUDO_USER', 'nobody'),
276 int(os.environ.get('SUDO_UID', '65534')),
277 int(os.environ.get('SUDO_GID', '65534')),
278 # Should we find a better home?
279 '/tmp/portage',
280 )
281
282 @staticmethod
283 @contextlib.contextmanager
284 def LockDb(db):
285 """Lock an account database.
286
287 We use the same algorithm as shadow/user.eclass. This way we don't race
288 and corrupt things in parallel.
289 """
290 lock = '%s.lock' % db
291 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
292
293 # First try forever to grab the lock.
294 retry = lambda e: e.errno == errno.EEXIST
295 # Retry quickly at first, but slow down over time.
296 try:
297 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700298 except Exception as e:
299 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800300
301 # Yield while holding the lock, but try to clean it no matter what.
302 try:
303 os.unlink(tmplock)
304 yield lock
305 finally:
306 os.unlink(lock)
307
308 def SetupUser(self):
309 """Propogate the user name<->id mapping from outside the chroot.
310
311 Some unittests use getpwnam($USER), as does bash. If the account
312 is not registered in the sysroot, they get back errors.
313 """
314 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
315 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
316 SDK_GECOS = 'ChromeOS Developer'
317
318 user, uid, gid, home = self.GetNonRootAccount()
319 if user == 'nobody':
320 return
321
322 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
323 with self.LockDb(passwd_db):
324 data = osutils.ReadFile(passwd_db)
325 accts = data.splitlines()
326 for acct in accts:
327 passwd = acct.split(':')
328 if passwd[0] == user:
329 # Did the sdk make this account?
330 if passwd[4] == SDK_GECOS:
331 # Don't modify it (see below) since we didn't create it.
332 return
333
334 # Did we make this account?
335 if passwd[4] != MAGIC_GECOS:
336 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
337 (passwd_db, user))
338
339 # Maybe we should see if it needs to be updated? Like if they
340 # changed UIDs? But we don't really check that elsewhere ...
341 return
342
343 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
344 'name': user,
345 'uid': uid,
346 'gid': gid,
347 'gecos': MAGIC_GECOS,
348 'homedir': home,
349 'shell': '/bin/bash',
350 }
351 with open(passwd_db, 'a') as f:
352 if data[-1] != '\n':
353 f.write('\n')
354 f.write('%s\n' % acct)
355
cmticef23cb132015-04-10 15:13:00 -0700356 def _FindRemoteProcess(self, device):
357 """Find a named process (or a pid) running on a remote device."""
358 if not self.remote_process_name and not self.pid:
359 return
cmticeb70801a2014-12-11 14:29:34 -0800360
cmticef23cb132015-04-10 15:13:00 -0700361 if self.remote_process_name:
362 # Look for a process with the specified name on the remote device; if
363 # found, get its pid.
364 pname = self.remote_process_name
365 if pname == 'browser':
366 all_chrome_pids = set(device.GetRunningPids(
367 '/opt/google/chrome/chrome'))
cmticef23cb132015-04-10 15:13:00 -0700368 non_main_chrome_pids = set(device.GetRunningPids('type='))
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500369 pids = list(all_chrome_pids - non_main_chrome_pids)
cmticef23cb132015-04-10 15:13:00 -0700370 elif pname == 'renderer' or pname == 'gpu-process':
371 pids = device.GetRunningPids('type=%s'% pname)
372 else:
373 pids = device.GetRunningPids(pname)
374
375 if pids:
376 if len(pids) == 1:
377 self.pid = pids[0]
378 else:
379 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
380 'You must specify the correct pid.'
381 % (pname, repr(pids)))
382 else:
383 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
384 (pname, self.remote))
385
386 # Find full path for process, from pid (and verify pid).
387 command = [
388 'readlink',
389 '-e', '/proc/%s/exe' % self.pid,
390 ]
391 try:
392 res = device.RunCommand(command, capture_output=True)
393 if res.returncode == 0:
394 self.inf_cmd = res.output.rstrip('\n')
395 except cros_build_lib.RunCommandError:
396 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
397 'with pid %s on %s' %
398 (self.pid, self.remote))
399
400 def GetCrossGdb(self):
401 """Find the appropriate cross-version of gdb for the board."""
402 toolchains = toolchain.GetToolchainsForBoard(self.board)
Mike Frysinger818d9632019-08-24 14:43:05 -0400403 tc = list(toolchain.FilterToolchains(toolchains, 'default', True))
cmticef23cb132015-04-10 15:13:00 -0700404 cross_gdb = tc[0] + '-gdb'
405 if not osutils.Which(cross_gdb):
406 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
407 'setup_board?' % cross_gdb)
408 return cross_gdb
409
Raul E Rangel746c45d2018-05-09 09:27:31 -0600410 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700411 """Generate list of commands with which to initialize the gdb session."""
412 gdb_init_commands = []
413
414 if self.remote:
415 sysroot_var = self.sysroot
416 else:
417 sysroot_var = '/'
418
419 gdb_init_commands = [
420 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700421 'set prompt %s' % self.prompt,
422 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700423 if self.in_chroot:
424 gdb_init_commands += [
425 'set solib-absolute-prefix %s' % sysroot_var,
426 'set solib-search-path %s' % sysroot_var,
427 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
428 ]
cmticef23cb132015-04-10 15:13:00 -0700429
Raul E Rangel746c45d2018-05-09 09:27:31 -0600430 if device:
431 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
432
433 ssh_cmd.extend(['--', 'gdbserver'])
434
435 if self.pid:
436 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
437 target_type = 'remote'
438 elif inferior_cmd:
439 ssh_cmd.extend(['-', inferior_cmd])
440 ssh_cmd.extend(self.inf_args)
441 target_type = 'remote'
442 else:
443 ssh_cmd.extend(['--multi', 'stdio'])
444 target_type = 'extended-remote'
445
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600446 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700447
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700448 if self.in_chroot:
449 if inferior_cmd:
450 gdb_init_commands.append(
451 'file %s' % os.path.join(sysroot_var,
452 inferior_cmd.lstrip(os.sep)))
453 else:
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700454 binary = self.GetSimpleChromeBinary()
455 gdb_init_commands += [
456 'set debug-file-directory %s' % os.path.dirname(binary),
457 'file %s' % binary
458 ]
Raul E Rangel746c45d2018-05-09 09:27:31 -0600459
460 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700461 else:
462 if inferior_cmd:
463 gdb_init_commands.append('file %s ' % inferior_cmd)
464 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
465
466 return gdb_init_commands
467
468 def RunRemote(self):
469 """Handle remote debugging, via gdbserver & cross debugger."""
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600470 with remote_access.ChromiumOSDeviceHandler(
471 self.remote,
472 port=self.remote_port,
473 connect_settings=self.ssh_settings,
474 ping=self.ping) as device:
cmticef23cb132015-04-10 15:13:00 -0700475
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600476 self.VerifyAndFinishInitialization(device)
477 gdb_cmd = self.cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700478
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600479 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
480 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
481 gdb_args += self.gdb_args
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600482
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600483 if self.cgdb:
484 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
485 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700486
Mike Frysinger45602c72019-09-22 02:15:11 -0400487 cros_build_lib.run(
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600488 [gdb_cmd] + gdb_args,
489 ignore_sigint=True,
490 print_cmd=True,
491 cwd=self.sysroot)
cmticef23cb132015-04-10 15:13:00 -0700492
493 def Run(self):
494 """Runs the debugger in a proper environment (e.g. qemu)."""
495
496 self.VerifyAndFinishInitialization(None)
497 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800498 if self.framework == 'qemu':
499 self.qemu.Install(self.sysroot)
500 self.qemu.RegisterBinfmt()
501
cmticeb70801a2014-12-11 14:29:34 -0800502 for mount in self._BIND_MOUNT_PATHS:
503 path = os.path.join(self.sysroot, mount)
504 osutils.SafeMakedirs(path)
505 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
506
cmticef23cb132015-04-10 15:13:00 -0700507 gdb_cmd = self._GDB
508 inferior_cmd = self.inf_cmd
509
cmtice932e0aa2015-02-27 11:49:12 -0800510 gdb_argv = self.gdb_args[:]
511 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700512 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800513 # Some programs expect to find data files via $CWD, so doing a chroot
514 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700515 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800516
cmticeb70801a2014-12-11 14:29:34 -0800517 os.chroot(self.sysroot)
518 os.chdir(cwd)
519 # The TERM the user is leveraging might not exist in the sysroot.
520 # Force a sane default that supports standard color sequences.
521 os.environ['TERM'] = 'ansi'
522 # Some progs want this like bash else they get super confused.
523 os.environ['PWD'] = cwd
524 if not self.run_as_root:
525 _, uid, gid, home = self.GetNonRootAccount()
526 os.setgid(gid)
527 os.setuid(uid)
528 os.environ['HOME'] = home
529
cmticef23cb132015-04-10 15:13:00 -0700530 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800531
cmticef23cb132015-04-10 15:13:00 -0700532 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
533 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800534 gdb_args += self.gdb_args
535
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600536 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800537
538
cmtice932e0aa2015-02-27 11:49:12 -0800539def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800540 """Re-execute gdb as root.
541
542 We often need to do things as root, so make sure we're that. Like chroot
543 for proper library environment or do bind mounts.
544
545 Also unshare the mount namespace so as to ensure that doing bind mounts for
546 tests don't leak out to the normal chroot. Also unshare the UTS namespace
547 so changes to `hostname` do not impact the host.
548 """
549 if os.geteuid() != 0:
550 cmd = ['sudo', '-E', '--'] + argv
551 os.execvp(cmd[0], cmd)
552 else:
cmtice932e0aa2015-02-27 11:49:12 -0800553 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
554
555
cmticef23cb132015-04-10 15:13:00 -0700556def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800557 """Look for the name of the inferior (to be debugged) in arg list."""
558
559 program_name = ''
560 new_list = []
561 for item in arg_list:
562 if item[0] == '-':
563 new_list.append(item)
564 elif not program_name:
565 program_name = item
566 else:
567 raise RuntimeError('Found multiple program names: %s %s'
568 % (program_name, item))
569
570 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800571
572
573def main(argv):
574
575 parser = commandline.ArgumentParser(description=__doc__)
576
cmticef23cb132015-04-10 15:13:00 -0700577 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800578 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700579 parser.add_argument('-g', '--gdb_args', action='append', default=[],
580 help='Arguments to gdb itself. If multiple arguments are'
Mike Frysinger80de5012019-08-01 14:10:53 -0400581 " passed, each argument needs a separate '-g' flag.")
cmticef23cb132015-04-10 15:13:00 -0700582 parser.add_argument(
583 '--remote', default=None,
584 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
585 help='Remote device on which to run the binary. Use'
586 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
587 ' already running local virtual machine.')
588 parser.add_argument('--pid', default='',
589 help='Process ID of the (already) running process on the'
590 ' remote device to which to attach.')
591 parser.add_argument('--remote_pid', dest='pid', default='',
592 help='Deprecated alias for --pid.')
593 parser.add_argument('--no-ping', dest='ping', default=True,
594 action='store_false',
595 help='Do not ping remote before attempting to connect.')
596 parser.add_argument('--attach', dest='attach_name', default='',
597 help='Name of existing process to which to attach, on'
598 ' remote device (remote debugging only). "--attach'
599 ' browser" will find the main chrome browser process;'
600 ' "--attach renderer" will find a chrome renderer'
601 ' process; "--attach gpu-process" will find the chrome'
602 ' gpu process.')
603 parser.add_argument('--cgdb', default=False,
604 action='store_true',
605 help='Use cgdb curses interface rather than plain gdb.'
606 'This option is only valid for remote debugging.')
607 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
608 help='Arguments for gdb to pass to the program being'
609 ' debugged. These are positional and must come at the end'
610 ' of the command line. This will not work if attaching'
611 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700612 parser.add_argument('--binary', default='',
613 help='full path to the binary being debuged.'
614 ' This is only useful for simple chrome.'
615 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800616
617 options = parser.parse_args(argv)
618 options.Freeze()
619
cmtice932e0aa2015-02-27 11:49:12 -0800620 gdb_args = []
621 inf_args = []
622 inf_cmd = ''
623
cmticef23cb132015-04-10 15:13:00 -0700624 if options.inf_args:
625 inf_cmd = options.inf_args[0]
626 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800627
cmticef23cb132015-04-10 15:13:00 -0700628 if options.gdb_args:
629 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800630
631 if inf_cmd:
632 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
633 inf_cmd.lstrip('/'))
634 if not os.path.exists(fname):
635 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700636 else:
637 if inf_args:
638 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800639
cmticef23cb132015-04-10 15:13:00 -0700640 if inf_args and (options.pid or options.attach_name):
641 parser.error('Cannot pass arguments to an already'
642 ' running process (--remote-pid or --attach).')
643
644 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700645 if options.attach_name and options.attach_name == 'browser':
646 inf_cmd = '/opt/google/chrome/chrome'
647 else:
648 if options.cgdb:
649 parser.error('--cgdb option can only be used with remote debugging.')
650 if options.pid:
651 parser.error('Must specify a remote device (--remote) if you want '
652 'to attach to a remote pid.')
653 if options.attach_name:
654 parser.error('Must specify remote device (--remote) when using'
655 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700656 if options.binary:
657 if not os.path.exists(options.binary):
658 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800659
cmticeb70801a2014-12-11 14:29:34 -0800660 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700661 if not options.remote:
662 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800663
cmticef23cb132015-04-10 15:13:00 -0700664 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
665 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700666 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800667
cmticef23cb132015-04-10 15:13:00 -0700668 try:
669 if options.remote:
670 gdb.RunRemote()
671 else:
672 gdb.Run()
673
674 except GdbException as e:
675 if options.debug:
676 raise
677 else:
678 raise cros_build_lib.Die(str(e))