blob: b1edc1366ae0dac8cdcbd50e84b05c291cb1398d [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."""
126 return os.path.exists("/mnt/host/source/chromite/")
127
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):
145 raise GdbMissingSysrootError('Cannot find sysroot for %s at.'
146 ' %s' % self.board, sysroot)
147 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(
190 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:
225 stripped_info = cros_build_lib.RunCommand(['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
233 package = cros_build_lib.RunCommand([equery, '-q', 'b',
234 self.inf_cmd],
235 capture_output=True).output
Lann Martinffb95162018-08-28 12:02:54 -0600236 # pylint: disable=logging-not-lazy
cmticef23cb132015-04-10 15:13:00 -0700237 logging.info(self._MISSING_DEBUG_INFO_MSG % {
238 'board': self.board,
239 'inf_cmd': self.inf_cmd,
240 'package': package,
241 'debug_file': debug_file})
242 answer = cros_build_lib.BooleanPrompt()
243 if not answer:
244 raise GdbEarlyExitError('Exiting early, at user request.')
245
246 # Set up qemu, if appropriate.
247 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800248 if qemu_arch is None:
249 self.framework = 'ldso'
250 else:
251 self.framework = 'qemu'
252 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800253
cmticef23cb132015-04-10 15:13:00 -0700254 if self.remote:
255 # Verify cgdb flag info.
256 if self.cgdb:
257 if osutils.Which('cgdb') is None:
258 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
259 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800260
cmticef23cb132015-04-10 15:13:00 -0700261 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800262 """Returns the given path with any sysroot prefix removed."""
263 # If the sysroot is /, then the paths are already normalized.
264 if self.sysroot != '/' and path.startswith(self.sysroot):
265 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800266 return path
267
268 @staticmethod
269 def GetNonRootAccount():
270 """Return details about the non-root account we want to use.
271
272 Returns:
273 A tuple of (username, uid, gid, home).
274 """
275 return (
276 os.environ.get('SUDO_USER', 'nobody'),
277 int(os.environ.get('SUDO_UID', '65534')),
278 int(os.environ.get('SUDO_GID', '65534')),
279 # Should we find a better home?
280 '/tmp/portage',
281 )
282
283 @staticmethod
284 @contextlib.contextmanager
285 def LockDb(db):
286 """Lock an account database.
287
288 We use the same algorithm as shadow/user.eclass. This way we don't race
289 and corrupt things in parallel.
290 """
291 lock = '%s.lock' % db
292 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
293
294 # First try forever to grab the lock.
295 retry = lambda e: e.errno == errno.EEXIST
296 # Retry quickly at first, but slow down over time.
297 try:
298 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700299 except Exception as e:
300 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800301
302 # Yield while holding the lock, but try to clean it no matter what.
303 try:
304 os.unlink(tmplock)
305 yield lock
306 finally:
307 os.unlink(lock)
308
309 def SetupUser(self):
310 """Propogate the user name<->id mapping from outside the chroot.
311
312 Some unittests use getpwnam($USER), as does bash. If the account
313 is not registered in the sysroot, they get back errors.
314 """
315 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
316 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
317 SDK_GECOS = 'ChromeOS Developer'
318
319 user, uid, gid, home = self.GetNonRootAccount()
320 if user == 'nobody':
321 return
322
323 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
324 with self.LockDb(passwd_db):
325 data = osutils.ReadFile(passwd_db)
326 accts = data.splitlines()
327 for acct in accts:
328 passwd = acct.split(':')
329 if passwd[0] == user:
330 # Did the sdk make this account?
331 if passwd[4] == SDK_GECOS:
332 # Don't modify it (see below) since we didn't create it.
333 return
334
335 # Did we make this account?
336 if passwd[4] != MAGIC_GECOS:
337 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
338 (passwd_db, user))
339
340 # Maybe we should see if it needs to be updated? Like if they
341 # changed UIDs? But we don't really check that elsewhere ...
342 return
343
344 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
345 'name': user,
346 'uid': uid,
347 'gid': gid,
348 'gecos': MAGIC_GECOS,
349 'homedir': home,
350 'shell': '/bin/bash',
351 }
352 with open(passwd_db, 'a') as f:
353 if data[-1] != '\n':
354 f.write('\n')
355 f.write('%s\n' % acct)
356
cmticef23cb132015-04-10 15:13:00 -0700357 def _FindRemoteProcess(self, device):
358 """Find a named process (or a pid) running on a remote device."""
359 if not self.remote_process_name and not self.pid:
360 return
cmticeb70801a2014-12-11 14:29:34 -0800361
cmticef23cb132015-04-10 15:13:00 -0700362 if self.remote_process_name:
363 # Look for a process with the specified name on the remote device; if
364 # found, get its pid.
365 pname = self.remote_process_name
366 if pname == 'browser':
367 all_chrome_pids = set(device.GetRunningPids(
368 '/opt/google/chrome/chrome'))
cmticef23cb132015-04-10 15:13:00 -0700369 non_main_chrome_pids = set(device.GetRunningPids('type='))
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500370 pids = list(all_chrome_pids - non_main_chrome_pids)
cmticef23cb132015-04-10 15:13:00 -0700371 elif pname == 'renderer' or pname == 'gpu-process':
372 pids = device.GetRunningPids('type=%s'% pname)
373 else:
374 pids = device.GetRunningPids(pname)
375
376 if pids:
377 if len(pids) == 1:
378 self.pid = pids[0]
379 else:
380 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
381 'You must specify the correct pid.'
382 % (pname, repr(pids)))
383 else:
384 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
385 (pname, self.remote))
386
387 # Find full path for process, from pid (and verify pid).
388 command = [
389 'readlink',
390 '-e', '/proc/%s/exe' % self.pid,
391 ]
392 try:
393 res = device.RunCommand(command, capture_output=True)
394 if res.returncode == 0:
395 self.inf_cmd = res.output.rstrip('\n')
396 except cros_build_lib.RunCommandError:
397 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
398 'with pid %s on %s' %
399 (self.pid, self.remote))
400
401 def GetCrossGdb(self):
402 """Find the appropriate cross-version of gdb for the board."""
403 toolchains = toolchain.GetToolchainsForBoard(self.board)
404 tc = toolchain.FilterToolchains(toolchains, 'default', True).keys()
405 cross_gdb = tc[0] + '-gdb'
406 if not osutils.Which(cross_gdb):
407 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
408 'setup_board?' % cross_gdb)
409 return cross_gdb
410
Raul E Rangel746c45d2018-05-09 09:27:31 -0600411 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700412 """Generate list of commands with which to initialize the gdb session."""
413 gdb_init_commands = []
414
415 if self.remote:
416 sysroot_var = self.sysroot
417 else:
418 sysroot_var = '/'
419
420 gdb_init_commands = [
421 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700422 'set prompt %s' % self.prompt,
423 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700424 if self.in_chroot:
425 gdb_init_commands += [
426 'set solib-absolute-prefix %s' % sysroot_var,
427 'set solib-search-path %s' % sysroot_var,
428 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
429 ]
cmticef23cb132015-04-10 15:13:00 -0700430
Raul E Rangel746c45d2018-05-09 09:27:31 -0600431 if device:
432 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
433
434 ssh_cmd.extend(['--', 'gdbserver'])
435
436 if self.pid:
437 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
438 target_type = 'remote'
439 elif inferior_cmd:
440 ssh_cmd.extend(['-', inferior_cmd])
441 ssh_cmd.extend(self.inf_args)
442 target_type = 'remote'
443 else:
444 ssh_cmd.extend(['--multi', 'stdio'])
445 target_type = 'extended-remote'
446
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600447 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700448
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700449 if self.in_chroot:
450 if inferior_cmd:
451 gdb_init_commands.append(
452 'file %s' % os.path.join(sysroot_var,
453 inferior_cmd.lstrip(os.sep)))
454 else:
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700455 binary = self.GetSimpleChromeBinary()
456 gdb_init_commands += [
457 'set debug-file-directory %s' % os.path.dirname(binary),
458 'file %s' % binary
459 ]
Raul E Rangel746c45d2018-05-09 09:27:31 -0600460
461 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700462 else:
463 if inferior_cmd:
464 gdb_init_commands.append('file %s ' % inferior_cmd)
465 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
466
467 return gdb_init_commands
468
469 def RunRemote(self):
470 """Handle remote debugging, via gdbserver & cross debugger."""
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600471 with remote_access.ChromiumOSDeviceHandler(
472 self.remote,
473 port=self.remote_port,
474 connect_settings=self.ssh_settings,
475 ping=self.ping) as device:
cmticef23cb132015-04-10 15:13:00 -0700476
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600477 self.VerifyAndFinishInitialization(device)
478 gdb_cmd = self.cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700479
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600480 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
481 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
482 gdb_args += self.gdb_args
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600483
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600484 if self.cgdb:
485 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
486 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700487
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600488 cros_build_lib.RunCommand(
489 [gdb_cmd] + gdb_args,
490 ignore_sigint=True,
491 print_cmd=True,
492 cwd=self.sysroot)
cmticef23cb132015-04-10 15:13:00 -0700493
494 def Run(self):
495 """Runs the debugger in a proper environment (e.g. qemu)."""
496
497 self.VerifyAndFinishInitialization(None)
498 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800499 if self.framework == 'qemu':
500 self.qemu.Install(self.sysroot)
501 self.qemu.RegisterBinfmt()
502
cmticeb70801a2014-12-11 14:29:34 -0800503 for mount in self._BIND_MOUNT_PATHS:
504 path = os.path.join(self.sysroot, mount)
505 osutils.SafeMakedirs(path)
506 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
507
cmticef23cb132015-04-10 15:13:00 -0700508 gdb_cmd = self._GDB
509 inferior_cmd = self.inf_cmd
510
cmtice932e0aa2015-02-27 11:49:12 -0800511 gdb_argv = self.gdb_args[:]
512 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700513 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800514 # Some programs expect to find data files via $CWD, so doing a chroot
515 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700516 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800517
cmticeb70801a2014-12-11 14:29:34 -0800518 os.chroot(self.sysroot)
519 os.chdir(cwd)
520 # The TERM the user is leveraging might not exist in the sysroot.
521 # Force a sane default that supports standard color sequences.
522 os.environ['TERM'] = 'ansi'
523 # Some progs want this like bash else they get super confused.
524 os.environ['PWD'] = cwd
525 if not self.run_as_root:
526 _, uid, gid, home = self.GetNonRootAccount()
527 os.setgid(gid)
528 os.setuid(uid)
529 os.environ['HOME'] = home
530
cmticef23cb132015-04-10 15:13:00 -0700531 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800532
cmticef23cb132015-04-10 15:13:00 -0700533 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
534 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800535 gdb_args += self.gdb_args
536
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600537 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800538
539
cmtice932e0aa2015-02-27 11:49:12 -0800540def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800541 """Re-execute gdb as root.
542
543 We often need to do things as root, so make sure we're that. Like chroot
544 for proper library environment or do bind mounts.
545
546 Also unshare the mount namespace so as to ensure that doing bind mounts for
547 tests don't leak out to the normal chroot. Also unshare the UTS namespace
548 so changes to `hostname` do not impact the host.
549 """
550 if os.geteuid() != 0:
551 cmd = ['sudo', '-E', '--'] + argv
552 os.execvp(cmd[0], cmd)
553 else:
cmtice932e0aa2015-02-27 11:49:12 -0800554 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
555
556
cmticef23cb132015-04-10 15:13:00 -0700557def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800558 """Look for the name of the inferior (to be debugged) in arg list."""
559
560 program_name = ''
561 new_list = []
562 for item in arg_list:
563 if item[0] == '-':
564 new_list.append(item)
565 elif not program_name:
566 program_name = item
567 else:
568 raise RuntimeError('Found multiple program names: %s %s'
569 % (program_name, item))
570
571 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800572
573
574def main(argv):
575
576 parser = commandline.ArgumentParser(description=__doc__)
577
cmticef23cb132015-04-10 15:13:00 -0700578 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800579 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700580 parser.add_argument('-g', '--gdb_args', action='append', default=[],
581 help='Arguments to gdb itself. If multiple arguments are'
582 ' passed, each argument needs a separate \'-g\' flag.')
583 parser.add_argument(
584 '--remote', default=None,
585 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
586 help='Remote device on which to run the binary. Use'
587 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
588 ' already running local virtual machine.')
589 parser.add_argument('--pid', default='',
590 help='Process ID of the (already) running process on the'
591 ' remote device to which to attach.')
592 parser.add_argument('--remote_pid', dest='pid', default='',
593 help='Deprecated alias for --pid.')
594 parser.add_argument('--no-ping', dest='ping', default=True,
595 action='store_false',
596 help='Do not ping remote before attempting to connect.')
597 parser.add_argument('--attach', dest='attach_name', default='',
598 help='Name of existing process to which to attach, on'
599 ' remote device (remote debugging only). "--attach'
600 ' browser" will find the main chrome browser process;'
601 ' "--attach renderer" will find a chrome renderer'
602 ' process; "--attach gpu-process" will find the chrome'
603 ' gpu process.')
604 parser.add_argument('--cgdb', default=False,
605 action='store_true',
606 help='Use cgdb curses interface rather than plain gdb.'
607 'This option is only valid for remote debugging.')
608 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
609 help='Arguments for gdb to pass to the program being'
610 ' debugged. These are positional and must come at the end'
611 ' of the command line. This will not work if attaching'
612 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700613 parser.add_argument('--binary', default='',
614 help='full path to the binary being debuged.'
615 ' This is only useful for simple chrome.'
616 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800617
618 options = parser.parse_args(argv)
619 options.Freeze()
620
cmtice932e0aa2015-02-27 11:49:12 -0800621 gdb_args = []
622 inf_args = []
623 inf_cmd = ''
624
cmticef23cb132015-04-10 15:13:00 -0700625 if options.inf_args:
626 inf_cmd = options.inf_args[0]
627 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800628
cmticef23cb132015-04-10 15:13:00 -0700629 if options.gdb_args:
630 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800631
632 if inf_cmd:
633 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
634 inf_cmd.lstrip('/'))
635 if not os.path.exists(fname):
636 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700637 else:
638 if inf_args:
639 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800640
cmticef23cb132015-04-10 15:13:00 -0700641 if inf_args and (options.pid or options.attach_name):
642 parser.error('Cannot pass arguments to an already'
643 ' running process (--remote-pid or --attach).')
644
645 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700646 if options.attach_name and options.attach_name == 'browser':
647 inf_cmd = '/opt/google/chrome/chrome'
648 else:
649 if options.cgdb:
650 parser.error('--cgdb option can only be used with remote debugging.')
651 if options.pid:
652 parser.error('Must specify a remote device (--remote) if you want '
653 'to attach to a remote pid.')
654 if options.attach_name:
655 parser.error('Must specify remote device (--remote) when using'
656 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700657 if options.binary:
658 if not os.path.exists(options.binary):
659 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800660
cmticeb70801a2014-12-11 14:29:34 -0800661 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700662 if not options.remote:
663 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800664
cmticef23cb132015-04-10 15:13:00 -0700665 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
666 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700667 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800668
cmticef23cb132015-04-10 15:13:00 -0700669 try:
670 if options.remote:
671 gdb.RunRemote()
672 else:
673 gdb.Run()
674
675 except GdbException as e:
676 if options.debug:
677 raise
678 else:
679 raise cros_build_lib.Die(str(e))