blob: 29731f10176a65a20ad13780f766b215f3a4f86d [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
cmtice932e0aa2015-02-27 11:49:12 -080026from chromite.lib import qemu
cmticef23cb132015-04-10 15:13:00 -070027from chromite.lib import remote_access
cmticeb70801a2014-12-11 14:29:34 -080028from chromite.lib import retry_util
cmticef23cb132015-04-10 15:13:00 -070029from chromite.lib import toolchain
cmticeb70801a2014-12-11 14:29:34 -080030
cmticef23cb132015-04-10 15:13:00 -070031class GdbException(Exception):
32 """Base exception for this module."""
33
34
35class GdbBadRemoteDeviceError(GdbException):
36 """Raised when remote device does not exist or is not responding."""
37
38
39class GdbMissingSysrootError(GdbException):
40 """Raised when path to sysroot cannot be found in chroot."""
41
42
43class GdbMissingInferiorError(GdbException):
44 """Raised when the binary to be debugged cannot be found."""
45
46
47class GdbMissingDebuggerError(GdbException):
48 """Raised when cannot find correct version of debugger."""
49
50
51class GdbCannotFindRemoteProcessError(GdbException):
52 """Raised when cannot find requested executing process on remote device."""
53
54
55class GdbUnableToStartGdbserverError(GdbException):
56 """Raised when error occurs trying to start gdbserver on remote device."""
57
58
59class GdbTooManyPidsError(GdbException):
60 """Raised when more than one matching pid is found running on device."""
61
62
63class GdbEarlyExitError(GdbException):
64 """Raised when user requests to exit early."""
65
66
67class GdbCannotDetectBoardError(GdbException):
68 """Raised when board isn't specified and can't be automatically determined."""
cmticeb70801a2014-12-11 14:29:34 -080069
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070070class GdbSimpleChromeBinaryError(GdbException):
71 """Raised when none or multiple chrome binaries are under out_${board} dir."""
cmtice932e0aa2015-02-27 11:49:12 -080072
cmticeb70801a2014-12-11 14:29:34 -080073class BoardSpecificGdb(object):
74 """Framework for running gdb."""
75
76 _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
cmticef23cb132015-04-10 15:13:00 -070077 _GDB = '/usr/bin/gdb'
Raul E Rangel746c45d2018-05-09 09:27:31 -060078 _EXTRA_SSH_SETTINGS = {
79 'CheckHostIP': 'no',
80 'BatchMode': 'yes',
81 'LogLevel': 'QUIET'
82 }
cmticef23cb132015-04-10 15:13:00 -070083 _MISSING_DEBUG_INFO_MSG = """
84%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
85 The debug symbols for that package may not be installed. To install the debug
86 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080087
cmticef23cb132015-04-10 15:13:00 -070088 cros_install_debug_syms --board=%(board)s %(package)s
89
90To install the debug symbols for all available packages, run:
91
92 cros_install_debug_syms --board=%(board)s --all"""
93
94 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070095 remote_process_name, cgdb_flag, ping, binary):
cmticeb70801a2014-12-11 14:29:34 -080096 self.board = board
cmticef23cb132015-04-10 15:13:00 -070097 self.sysroot = None
98 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -080099 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -0700100 self.run_as_root = False
101 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800102 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -0700103 self.remote = remote.hostname if remote else None
104 self.pid = pid
105 self.remote_process_name = remote_process_name
106 # Port used for sending ssh commands to DUT.
107 self.remote_port = remote.port if remote else None
108 # Port for communicating between gdb & gdbserver.
109 self.gdbserver_port = remote_access.GetUnusedPort()
110 self.ssh_settings = remote_access.CompileSSHConnectSettings(
111 **self._EXTRA_SSH_SETTINGS)
112 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800113 self.framework = 'auto'
114 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700115 self.device = None
116 self.cross_gdb = None
117 self.ping = ping
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700118 self.binary = binary
119 self.in_chroot = None
120 self.chrome_path = None
121 self.sdk_path = None
122
123 def IsInChroot(self):
124 """Decide whether we are in chroot or chrome-sdk."""
125 return os.path.exists("/mnt/host/source/chromite/")
126
127 def SimpleChromeGdb(self):
128 """Get the name of the cross gdb based on board name."""
129 bin_path = self.board + '+' + os.environ['SDK_VERSION'] + '+' + \
130 'target_toolchain'
131 bin_path = os.path.join(self.sdk_path, bin_path, 'bin')
132 for f in os.listdir(bin_path):
133 if f.endswith('gdb'):
134 return os.path.join(bin_path, f)
135 raise GdbMissingDebuggerError('Cannot find cros gdb for %s.'
136 % self.board)
137
138 def SimpleChromeSysroot(self):
139 """Get the sysroot in simple chrome."""
140 sysroot = self.board + '+' + os.environ['SDK_VERSION'] + \
141 '+' + 'sysroot_chromeos-base_chromeos-chrome.tar.xz'
142 sysroot = os.path.join(self.sdk_path, sysroot)
143 if not os.path.isdir(sysroot):
144 raise GdbMissingSysrootError('Cannot find sysroot for %s at.'
145 ' %s' % self.board, sysroot)
146 return sysroot
147
148 def GetSimpleChromeBinary(self):
149 """Get path to the binary in simple chrome."""
150 if self.binary:
151 return self.binary
152
153 output_dir = os.path.join(self.chrome_path, 'src',
154 'out_{}'.format(self.board))
155 target_binary = None
156 binary_name = os.path.basename(self.inf_cmd)
157 for root, _, files in os.walk(output_dir):
158 for f in files:
159 if f == binary_name:
160 if target_binary == None:
161 target_binary = os.path.join(root, f)
162 else:
163 raise GdbSimpleChromeBinaryError(
164 'There are multiple %s under %s. Please specify the path to '
165 'the binary via --binary'% binary_name, output_dir)
166 if target_binary == None:
167 raise GdbSimpleChromeBinaryError('There is no %s under %s.'
168 % binary_name, output_dir)
169 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800170
cmticef23cb132015-04-10 15:13:00 -0700171 def VerifyAndFinishInitialization(self, device):
172 """Verify files/processes exist and flags are correct."""
173 if not self.board:
174 if self.remote:
175 self.board = cros_build_lib.GetBoard(device_board=device.board,
176 override_board=self.board)
177 else:
178 raise GdbCannotDetectBoardError('Cannot determine which board to use. '
179 'Please specify the with --board flag.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700180 self.in_chroot = self.IsInChroot()
cmticef23cb132015-04-10 15:13:00 -0700181 self.prompt = '(%s-gdb) ' % self.board
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700182 if self.in_chroot:
183 self.sysroot = cros_build_lib.GetSysroot(board=self.board)
184 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
185 self.cross_gdb = self.GetCrossGdb()
186 else:
187 self.chrome_path = os.path.realpath(os.path.join(os.path.dirname(
188 os.path.realpath(__file__)), "../../../.."))
189 self.sdk_path = os.path.join(self.chrome_path,
190 '.cros_cache/chrome-sdk/tarballs/')
191 self.sysroot = self.SimpleChromeSysroot()
192 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700193
194 if self.remote:
195
196 # If given remote process name, find pid & inf_cmd on remote device.
197 if self.remote_process_name or self.pid:
198 self._FindRemoteProcess(device)
199
200 # Verify that sysroot is valid (exists).
201 if not os.path.isdir(self.sysroot):
202 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
203 self.sysroot)
204
205 self.device = device
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700206 if not self.in_chroot:
207 return
208
cmticef23cb132015-04-10 15:13:00 -0700209 sysroot_inf_cmd = ''
210 if self.inf_cmd:
211 sysroot_inf_cmd = os.path.join(self.sysroot,
212 self.inf_cmd.lstrip('/'))
213
214 # Verify that inf_cmd, if given, exists.
215 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
216 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
217 sysroot_inf_cmd)
218
219 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
220 # exists. If not, tell user and give them the option of quitting & getting
221 # the debug info.
222 if sysroot_inf_cmd:
223 stripped_info = cros_build_lib.RunCommand(['file', sysroot_inf_cmd],
224 capture_output=True).output
225 if not ' not stripped' in stripped_info:
226 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
227 self.inf_cmd.lstrip('/'))
228 debug_file += '.debug'
229 if not os.path.exists(debug_file):
230 equery = 'equery-%s' % self.board
231 package = cros_build_lib.RunCommand([equery, '-q', 'b',
232 self.inf_cmd],
233 capture_output=True).output
234 logging.info(self._MISSING_DEBUG_INFO_MSG % {
235 'board': self.board,
236 'inf_cmd': self.inf_cmd,
237 'package': package,
238 'debug_file': debug_file})
239 answer = cros_build_lib.BooleanPrompt()
240 if not answer:
241 raise GdbEarlyExitError('Exiting early, at user request.')
242
243 # Set up qemu, if appropriate.
244 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800245 if qemu_arch is None:
246 self.framework = 'ldso'
247 else:
248 self.framework = 'qemu'
249 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800250
cmticef23cb132015-04-10 15:13:00 -0700251 if self.remote:
252 # Verify cgdb flag info.
253 if self.cgdb:
254 if osutils.Which('cgdb') is None:
255 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
256 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800257
cmticef23cb132015-04-10 15:13:00 -0700258 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800259 """Returns the given path with any sysroot prefix removed."""
260 # If the sysroot is /, then the paths are already normalized.
261 if self.sysroot != '/' and path.startswith(self.sysroot):
262 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800263 return path
264
265 @staticmethod
266 def GetNonRootAccount():
267 """Return details about the non-root account we want to use.
268
269 Returns:
270 A tuple of (username, uid, gid, home).
271 """
272 return (
273 os.environ.get('SUDO_USER', 'nobody'),
274 int(os.environ.get('SUDO_UID', '65534')),
275 int(os.environ.get('SUDO_GID', '65534')),
276 # Should we find a better home?
277 '/tmp/portage',
278 )
279
280 @staticmethod
281 @contextlib.contextmanager
282 def LockDb(db):
283 """Lock an account database.
284
285 We use the same algorithm as shadow/user.eclass. This way we don't race
286 and corrupt things in parallel.
287 """
288 lock = '%s.lock' % db
289 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
290
291 # First try forever to grab the lock.
292 retry = lambda e: e.errno == errno.EEXIST
293 # Retry quickly at first, but slow down over time.
294 try:
295 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700296 except Exception as e:
297 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800298
299 # Yield while holding the lock, but try to clean it no matter what.
300 try:
301 os.unlink(tmplock)
302 yield lock
303 finally:
304 os.unlink(lock)
305
306 def SetupUser(self):
307 """Propogate the user name<->id mapping from outside the chroot.
308
309 Some unittests use getpwnam($USER), as does bash. If the account
310 is not registered in the sysroot, they get back errors.
311 """
312 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
313 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
314 SDK_GECOS = 'ChromeOS Developer'
315
316 user, uid, gid, home = self.GetNonRootAccount()
317 if user == 'nobody':
318 return
319
320 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
321 with self.LockDb(passwd_db):
322 data = osutils.ReadFile(passwd_db)
323 accts = data.splitlines()
324 for acct in accts:
325 passwd = acct.split(':')
326 if passwd[0] == user:
327 # Did the sdk make this account?
328 if passwd[4] == SDK_GECOS:
329 # Don't modify it (see below) since we didn't create it.
330 return
331
332 # Did we make this account?
333 if passwd[4] != MAGIC_GECOS:
334 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
335 (passwd_db, user))
336
337 # Maybe we should see if it needs to be updated? Like if they
338 # changed UIDs? But we don't really check that elsewhere ...
339 return
340
341 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
342 'name': user,
343 'uid': uid,
344 'gid': gid,
345 'gecos': MAGIC_GECOS,
346 'homedir': home,
347 'shell': '/bin/bash',
348 }
349 with open(passwd_db, 'a') as f:
350 if data[-1] != '\n':
351 f.write('\n')
352 f.write('%s\n' % acct)
353
cmticef23cb132015-04-10 15:13:00 -0700354 def _FindRemoteProcess(self, device):
355 """Find a named process (or a pid) running on a remote device."""
356 if not self.remote_process_name and not self.pid:
357 return
cmticeb70801a2014-12-11 14:29:34 -0800358
cmticef23cb132015-04-10 15:13:00 -0700359 if self.remote_process_name:
360 # Look for a process with the specified name on the remote device; if
361 # found, get its pid.
362 pname = self.remote_process_name
363 if pname == 'browser':
364 all_chrome_pids = set(device.GetRunningPids(
365 '/opt/google/chrome/chrome'))
366 sandbox_pids = set(device.GetRunningPids(
367 '/opt/google/chrome/chrome-sandbox'))
368 non_main_chrome_pids = set(device.GetRunningPids('type='))
369 pids = list(all_chrome_pids - sandbox_pids - non_main_chrome_pids)
370 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)
403 tc = toolchain.FilterToolchains(toolchains, 'default', True).keys()
404 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
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600487 cros_build_lib.RunCommand(
488 [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'
581 ' passed, each argument needs a separate \'-g\' flag.')
582 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))