Skip to content

Commit

Permalink
GDB: add --gdb-prerun-commands option to GDB plugin
Browse files Browse the repository at this point in the history
This feature allows custom GDB commands to be executed before the
actual binary is run. What it can do then, is only limited by the
user's goals.

This is intended to allow complex applications, such as QEMU,
to be debugged inside Avocado tests. Configuring GDB for QEMU,
for example, involves passing signals to the application that
would otherwise result in an event that would stop the debugger.

Signed-off-by: Cleber Rosa <[email protected]>
  • Loading branch information
clebergnu committed Nov 23, 2014
1 parent 67887a0 commit e9181e1
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 0 deletions.
14 changes: 14 additions & 0 deletions avocado/plugins/gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ def configure(self, parser):
'be "<binary>[:breakpoint]". Breakpoint '
'defaults to "main"'))

gdb_grp.add_argument('--gdb-prerun-commands', action='append',
default=[], metavar='BINARY_PATH:COMMANDS_PATH',
help=('After loading a binary in binary in GDB, '
'but before actually running it, execute '
'the given GDB commands in the given file.'
'BINARY_PATH is optional and if ommited '
'will apply to all binaries'))

gdb_grp.add_argument('--gdb-enable-core', action='store_true',
default=False,
help=('Automatically generate a core dump when the'
Expand All @@ -49,6 +57,12 @@ def activate(self, app_args):
try:
for binary in app_args.gdb_run_bin:
runtime.GDB_RUN_BINARY_NAMES_EXPR.append(binary)
for commands in app_args.gdb_prerun_commands:
if ':' in commands:
binary, commands_path = commands.split(':', 1)
runtime.GDB_PRERUN_COMMANDS['binary'] = commands_path
else:
runtime.GDB_PRERUN_COMMANDS[''] = commands
if app_args.gdb_enable_core:
runtime.GDB_ENABLE_CORE = True
except AttributeError:
Expand Down
4 changes: 4 additions & 0 deletions avocado/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#: using the given expression
GDB_RUN_BINARY_NAMES_EXPR = []

#: After loading a binary in binary in GDB, but before actually running it,
#: execute the given GDB commands present in the given file, one per line
GDB_PRERUN_COMMANDS = {}

#: Wether to enable the automatic generation of core dumps for applications
#: that are run inside the GNU debugger
GDB_ENABLE_CORE = False
Expand Down
17 changes: 17 additions & 0 deletions avocado/utils/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,10 +746,27 @@ def wait_for_exit(self):

return result

def _run_pre_commands(self):
'''
Run commands if user passed a commands file with --gdb-prerun-commands
'''
binary_name = os.path.basename(self.binary)
# The commands file can be specific to a given binary or universal,
# start checking for specific ones first
prerun_commands_path = runtime.GDB_PRERUN_COMMANDS.get(
binary_name,
runtime.GDB_PRERUN_COMMANDS.get('', None))

if prerun_commands_path is not None:
prerun_commands = open(prerun_commands_path).readlines()
for command in prerun_commands:
self.gdb.cmd(command)

def run(self, timeout=None):
for b in self._get_breakpoints():
self.gdb.set_break(b, ignore_error=True)

self._run_pre_commands()
result = self.gdb.run(self.args[1:])

# Collect gdbserver stdout and stderr file information for debugging
Expand Down
3 changes: 3 additions & 0 deletions examples/gdb-prerun-scripts/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This directory contains examples of GDB "prerun" command files. These are
intended to be examples of how to use the "--gdb-prerun-commands" option
of the Avocado GDB plugin.
2 changes: 2 additions & 0 deletions examples/gdb-prerun-scripts/pass-sigusr1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
signal SIGUSR1 pass
signal SIGUSR1 nostop
14 changes: 14 additions & 0 deletions man/avocado.rst
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,20 @@ while you are debugging it, avocado has no way to know about its status.
Avocado will automatically send a `continue` command to the debugger
when you disconnect from and exit gdb.

If you're debugging a special application and need to setup GDB in custom
ways by running GDB commands, you can do that with the `--gdb-prerun-commands`
option::

$ avocado run --gdb-run-bin=foo:bar --gdb-prerun-commands=/tmp/disable-signals footest

In this example, `/tmp/disable-signals` is a simple text file containing two lines::

signal SIGUSR1 pass
signal SIGUSR1 nostop

Each line is a GDB command, so you can have from simple to very complex
debuggin environments configured like that.

RECORDING TEST REFERENCE OUTPUT
===============================

Expand Down
45 changes: 45 additions & 0 deletions selftests/all/functional/avocado/gdb_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2014
# Author: Cleber Rosa <[email protected]>

import os
import sys
import unittest

# simple magic for using scripts within a source tree
basedir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', '..')
basedir = os.path.abspath(basedir)
if os.path.isdir(os.path.join(basedir, 'avocado')):
sys.path.append(basedir)

from avocado import runtime
from avocado.utils import process


class GDBPluginTest(unittest.TestCase):

def test_gdb_prerun_commands(self):
os.chdir(basedir)
cmd_line = './scripts/avocado run --gdb-prerun-commands=/dev/null sleeptest'
process.run(cmd_line)

def test_gdb_multiple_prerun_commands(self):
os.chdir(basedir)
cmd_line = ('./scripts/avocado run --gdb-prerun-commands=/dev/null '
'--gdb-prerun-commands=foo:/dev/null sleeptest')
process.run(cmd_line)

if __name__ == '__main__':
unittest.main()

0 comments on commit e9181e1

Please sign in to comment.