提交 3519dea8 编写于 作者: C Cleber Rosa

GDB: deal with fatal signals and generate core dumps

This adds two related features:

1) dealing with fatal signals such as SIGSEGV
2) generating core dumps automatically (disabled by default) if requested

So now the behaviour on fatal signals (SISEGV and SIGABRT) for now cause
the test to be skipped. Other ways to deal with this proved to be complex
and not really effective.
Signed-off-by: NCleber Rosa <crosa@redhat.com>
上级 832fee3d
......@@ -88,6 +88,41 @@ def is_break_hit(parsed_mi_msg):
(parsed_mi_msg.result.reason == "breakpoint-hit"))
def is_sigsegv(parsed_mi_msg):
return (hasattr(parsed_mi_msg, 'class_') and
(parsed_mi_msg.class_ == 'stopped') and
hasattr(parsed_mi_msg, 'result') and
hasattr(parsed_mi_msg.result, 'signal_name') and
(parsed_mi_msg.result.reason == "SIGSEGV"))
def is_sigabrt_stopped(parsed_mi_msg):
return (hasattr(parsed_mi_msg, 'class_') and
(parsed_mi_msg.class_ == 'stopped') and
hasattr(parsed_mi_msg, 'record_type') and
(parsed_mi_msg.record_type == 'result') and
(parsed_mi_msg.result.reason == 'signal-received') and
(parsed_mi_msg.result.signal_name == 'SIGABRT'))
def is_sigabrt_console(parsed_mi_msg):
return (hasattr(parsed_mi_msg, 'record_type') and
(parsed_mi_msg.record_type == 'stream') and
hasattr(parsed_mi_msg, 'type') and
(parsed_mi_msg.type == 'console') and
hasattr(parsed_mi_msg, 'value') and
parsed_mi_msg.value == 'SIGABRT, Aborted.\n')
def is_sigabrt(parsed_mi_msg):
return (is_sigabrt_stopped(parsed_mi_msg) or
is_sigabrt_console(parsed_mi_msg))
def is_fatal_signal(parsed_mi_msg):
return is_sigsegv(parsed_mi_msg) or is_sigabrt(parsed_mi_msg)
class CommandResult(object):
"""
......
......@@ -29,13 +29,19 @@ class GDB(plugin.Plugin):
def configure(self, parser):
self.parser = parser
runner = self.parser.runner
runner.add_argument('--gdb-run-bin', action='append',
default=[], metavar='BINARY_PATH',
help=('Set a breakpoint on a given binary to be '
'run inside the GNU debugger. Format should '
'be "<binary>[:breakpoint]". Breakpoint '
'defaults to "main"'))
gdb_grp = self.parser.runner.add_argument_group('GNU Debugger support')
gdb_grp.add_argument('--gdb-run-bin', action='append',
default=[], metavar='BINARY_PATH',
help=('Set a breakpoint on a given binary to be '
'run inside the GNU debugger. Format should '
'be "<binary>[:breakpoint]". Breakpoint '
'defaults to "main"'))
gdb_grp.add_argument('--gdb-enable-core', action='store_true',
default=False,
help=('Automatically generate a core dump when the'
' inferior process received a fatal signal '
'such as SIGSEGV or SIGABRT'))
self.configured = True
......@@ -43,5 +49,7 @@ class GDB(plugin.Plugin):
try:
for binary in app_args.gdb_run_bin:
runtime.GDB_RUN_BINARY_NAMES_EXPR.append(binary)
if app_args.gdb_enable_core:
runtime.GDB_ENABLE_CORE = True
except AttributeError:
pass
......@@ -21,9 +21,9 @@ Module that contains runtime configuration
#: using the given expression
GDB_RUN_BINARY_NAMES_EXPR = []
#: Wether to disable the automatic generation of core dumps for applications
#: Wether to enable the automatic generation of core dumps for applications
#: that are run inside the GNU debugger
GDB_DISABLE_CORE = False
GDB_ENABLE_CORE = False
#: Sometimes it's useful for the framework and API to know about the test that
#: is currently running, if one exists
......
......@@ -24,6 +24,7 @@ import subprocess
import time
import stat
import shlex
import shutil
import threading
from avocado import gdb
......@@ -453,6 +454,22 @@ class GDBSubProcess(object):
breakpoints.append(gdb.GDB.DEFAULT_BREAK)
return breakpoints
def create_and_wait_on_resume_fifo(self, path):
'''
Creates a FIFO file and waits until it's written to
:param path: the path that the file will be created
:type path: str
:returns: first character that was written to the fifo
:rtype: str
'''
os.mkfifo(path)
f = open(path, 'r')
c = f.read(1)
f.close()
os.unlink(path)
return c
def generate_gdb_connect_cmds(self):
current_test = runtime.CURRENT_TEST
if current_test is not None:
......@@ -475,10 +492,11 @@ class GDBSubProcess(object):
binary_name = os.path.basename(self.binary)
fifo_name = "%s.gdb.cont.fifo" % os.path.basename(binary_name)
fifo_path = os.path.join(runtime.CURRENT_TEST.workdir, fifo_name)
fifo_path = os.path.join(current_test.outputdir, fifo_name)
script_name = '%s.gdb.sh' % binary_name
script_path = os.path.join(current_test.outputdir, script_name)
script = open(script_path, 'w')
script.write("#!/bin/sh\n")
script.write("%s -x %s\n" % (gdb.GDB.GDB_PATH, cmds))
......@@ -487,6 +505,17 @@ class GDBSubProcess(object):
os.chmod(script_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
return (script_path, fifo_path)
def generate_core(self):
core_name = "%s.core" % os.path.basename(self.binary)
core_path = os.path.join(runtime.CURRENT_TEST.outputdir, core_name)
gcore_cmd = 'gcore %s' % core_path
r = self.gdb.cli_cmd(gcore_cmd)
if not r.result.class_ == 'done':
raise gdb.UnexpectedResponseError
# also copy the binary as it's needed with the core
shutil.copy(self.binary, runtime.CURRENT_TEST.outputdir)
return core_path
def handle_break_hit(self, response):
self.gdb.disconnect()
script_path, fifo_path = self.generate_gdb_connect_sh()
......@@ -501,12 +530,27 @@ class GDBSubProcess(object):
runtime.CURRENT_TEST.report_state()
runtime.CURRENT_TEST.paused_msg = ''
os.mkfifo(fifo_path)
f = open(fifo_path, 'r')
c = f.read(1)
f.close()
os.unlink(fifo_path)
return c
return self.create_and_wait_on_resume_fifo(fifo_path)
def handle_fatal_signal(self, response):
script_path, fifo_path = self.generate_gdb_connect_sh()
msg = ("\n\nTEST PAUSED because inferior process received a FATAL SIGNAL. "
"To DEBUG your application run:\n%s\n\n" % script_path)
if runtime.GDB_ENABLE_CORE:
core = self.generate_core()
msg += ("\nAs requested, a core dump has been generated "
"automatically at the following location:\n%s\n") % core
self.gdb.disconnect()
runtime.CURRENT_TEST.paused = True
runtime.CURRENT_TEST.paused_msg = msg
runtime.CURRENT_TEST.report_state()
runtime.CURRENT_TEST.paused_msg = ''
return self.create_and_wait_on_resume_fifo(fifo_path)
def _is_thread_stopped(self):
result = False
......@@ -550,6 +594,7 @@ class GDBSubProcess(object):
try:
msg = messages.pop(0)
parsed_msg = gdb.parse_mi(msg)
if gdb.is_exit(parsed_msg):
self.result.exit_status = self._get_exit_status(parsed_msg)
result = True
......@@ -573,6 +618,13 @@ class GDBSubProcess(object):
'dependable results.', self.binary)
raise GDBInferiorProcessExitedError
elif gdb.is_fatal_signal(parsed_msg):
# waits on fifo read() until end of debug session is notified
r = self.handle_fatal_signal(parsed_msg)
log.warn('Because "%s" received a fatal signal, this test '
'is going to be skipped.', self.binary)
raise GDBInferiorProcessExitedError
except IndexError:
continue
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册