提交 45ec1b47 编写于 作者: C Cleber Rosa 提交者: clebergnu

Merge pull request #218 from clebergnu/gdb_fatal_signals_v2

GDB: deal with fatal signals and generate core dumps (v2)
......@@ -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.
先完成此消息的编辑!
想要评论请 注册