summaryrefslogtreecommitdiffstats
path: root/tests/scripts/gdb.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/scripts/gdb.py')
-rw-r--r--tests/scripts/gdb.py85
1 files changed, 85 insertions, 0 deletions
diff --git a/tests/scripts/gdb.py b/tests/scripts/gdb.py
new file mode 100644
index 0000000..50b5fa9
--- /dev/null
+++ b/tests/scripts/gdb.py
@@ -0,0 +1,85 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2020-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+"""
+This GDB script sets up the debugger to run the program and see if it finishes
+of its own accord or is terminated by a signal (like SIGABRT/SIGSEGV). In the
+latter case, it saves a full backtrace and core file.
+
+These signals are considered part of normal operation and will not trigger the
+above handling:
+- SIGPIPE: normal in a networked environment
+- SIGHUP: normally used to tell a process to shut down
+"""
+
+import os
+import os.path
+
+import gdb
+
+
+def format_program(inferior=None, thread=None):
+ "Format program name and p(t)id"
+
+ if thread:
+ inferior = thread.inferior
+ elif inferior is None:
+ inferior = gdb.selected_inferior()
+
+ try:
+ name = os.path.basename(inferior.progspace.filename)
+ except AttributeError: # inferior has died already
+ name = "unknown"
+
+ if thread:
+ pid = ".".join(tid for tid in thread.ptid if tid)
+ else:
+ pid = inferior.pid
+
+ return "{}.{}".format(name, pid)
+
+
+def stop_handler(event):
+ "Inferior stopped on a signal, record core, backtrace and exit"
+
+ if not isinstance(event, gdb.SignalEvent):
+ # Ignore breakpoints
+ return
+
+ thread = event.inferior_thread
+
+ identifier = format_program(thread=thread)
+ prefix = os.path.expandvars("${TESTDIR}/") + identifier
+
+ if event.stop_signal == "SIGHUP":
+ # TODO: start a timer to catch shutdown issues/deadlocks
+ gdb.execute("continue")
+ return
+
+ gdb.execute('generate-core-file {}.core'.format(prefix))
+
+ with open(prefix + ".backtrace", "w") as bt_file:
+ backtrace = gdb.execute("thread apply all backtrace full",
+ to_string=True)
+ bt_file.write(backtrace)
+
+ gdb.execute("continue")
+
+
+# We or we could allow the runner to disable randomisation
+gdb.execute("set disable-randomization off")
+
+gdb.execute("handle SIGPIPE noprint")
+gdb.execute("handle SIGINT pass")
+gdb.events.stop.connect(stop_handler)
+gdb.execute("run")