|
|
0e0bbf |
From 91cdbeb45d11cfd1dd62afe50f80a7c5d4342f5b Mon Sep 17 00:00:00 2001
|
|
|
0e0bbf |
From: Martin Milata <mmilata@redhat.com>
|
|
|
0e0bbf |
Date: Mon, 24 Nov 2014 14:36:14 +0100
|
|
|
0e0bbf |
Subject: [SATYR PATCH] unwind: support unwinding from core hook
|
|
|
0e0bbf |
|
|
|
0e0bbf |
In-memory unwinding of dying processes, related to abrt/abrt#gh829. The
|
|
|
0e0bbf |
implementation mostly mirrors code in src/stack.c of elfutils repo
|
|
|
0e0bbf |
(jankratochvil/corepattern branch).
|
|
|
0e0bbf |
|
|
|
0e0bbf |
Related to abrt/abrt#829.
|
|
|
0e0bbf |
|
|
|
0e0bbf |
Signed-off-by: Martin Milata <mmilata@redhat.com>
|
|
|
0e0bbf |
---
|
|
|
0e0bbf |
include/abrt.h | 8 +++
|
|
|
0e0bbf |
include/core/unwind.h | 18 ++++++
|
|
|
0e0bbf |
lib/abrt.c | 35 ++++++++++++
|
|
|
0e0bbf |
lib/core_unwind.c | 14 +++++
|
|
|
0e0bbf |
lib/core_unwind_elfutils.c | 136 +++++++++++++++++++++++++++++++++++++++++++++
|
|
|
0e0bbf |
5 files changed, 211 insertions(+)
|
|
|
0e0bbf |
|
|
|
0e0bbf |
diff --git a/include/abrt.h b/include/abrt.h
|
|
|
0e0bbf |
index 0a8f5ac..5116446 100644
|
|
|
0e0bbf |
--- a/include/abrt.h
|
|
|
0e0bbf |
+++ b/include/abrt.h
|
|
|
0e0bbf |
@@ -26,6 +26,7 @@ extern "C" {
|
|
|
0e0bbf |
|
|
|
0e0bbf |
#include "report_type.h"
|
|
|
0e0bbf |
#include <stdbool.h>
|
|
|
0e0bbf |
+#include <sys/types.h>
|
|
|
0e0bbf |
|
|
|
0e0bbf |
bool
|
|
|
0e0bbf |
sr_abrt_print_report_from_dir(const char *directory,
|
|
|
0e0bbf |
@@ -43,6 +44,13 @@ sr_abrt_create_core_stacktrace_from_gdb(const char *directory,
|
|
|
0e0bbf |
bool hash_fingerprints,
|
|
|
0e0bbf |
char **error_message);
|
|
|
0e0bbf |
|
|
|
0e0bbf |
+bool
|
|
|
0e0bbf |
+sr_abrt_create_core_stacktrace_from_core_hook(const char *directory,
|
|
|
0e0bbf |
+ pid_t thread_id,
|
|
|
0e0bbf |
+ const char *executable,
|
|
|
0e0bbf |
+ int signum,
|
|
|
0e0bbf |
+ char **error_message);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
struct sr_rpm_package *
|
|
|
0e0bbf |
sr_abrt_parse_dso_list(const char *text);
|
|
|
0e0bbf |
|
|
|
0e0bbf |
diff --git a/include/core/unwind.h b/include/core/unwind.h
|
|
|
0e0bbf |
index 8ab9d52..b128c76 100644
|
|
|
0e0bbf |
--- a/include/core/unwind.h
|
|
|
0e0bbf |
+++ b/include/core/unwind.h
|
|
|
0e0bbf |
@@ -24,6 +24,8 @@
|
|
|
0e0bbf |
extern "C" {
|
|
|
0e0bbf |
#endif
|
|
|
0e0bbf |
|
|
|
0e0bbf |
+#include <sys/types.h>
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
struct sr_core_stacktrace;
|
|
|
0e0bbf |
struct sr_gdb_stacktrace;
|
|
|
0e0bbf |
|
|
|
0e0bbf |
@@ -38,6 +40,22 @@ sr_core_stacktrace_from_gdb(const char *gdb_output,
|
|
|
0e0bbf |
const char *executable_filename,
|
|
|
0e0bbf |
char **error_message);
|
|
|
0e0bbf |
|
|
|
0e0bbf |
+/* This function can be used to unwind stack of live ("dying") process, invoked
|
|
|
0e0bbf |
+ * from the core dump hook (/proc/sys/kernel/core_pattern).
|
|
|
0e0bbf |
+ *
|
|
|
0e0bbf |
+ * Beware:
|
|
|
0e0bbf |
+ *
|
|
|
0e0bbf |
+ * - It can only unwind one thread of the process, the thread that caused the
|
|
|
0e0bbf |
+ * terminating signal to be sent. You must supply that thread's tid.
|
|
|
0e0bbf |
+ * - The function calls close() on stdin, meaning that in the core handler you
|
|
|
0e0bbf |
+ * cannot access the core image after calling this function.
|
|
|
0e0bbf |
+ */
|
|
|
0e0bbf |
+struct sr_core_stacktrace *
|
|
|
0e0bbf |
+sr_core_stacktrace_from_core_hook(pid_t thread_id,
|
|
|
0e0bbf |
+ const char *executable_filename,
|
|
|
0e0bbf |
+ int signum,
|
|
|
0e0bbf |
+ char **error_message);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
#ifdef __cplusplus
|
|
|
0e0bbf |
}
|
|
|
0e0bbf |
#endif
|
|
|
0e0bbf |
diff --git a/lib/abrt.c b/lib/abrt.c
|
|
|
0e0bbf |
index 39bc45d..585fdfb 100644
|
|
|
0e0bbf |
--- a/lib/abrt.c
|
|
|
0e0bbf |
+++ b/lib/abrt.c
|
|
|
0e0bbf |
@@ -138,6 +138,41 @@ sr_abrt_create_core_stacktrace(const char *directory,
|
|
|
0e0bbf |
error_message);
|
|
|
0e0bbf |
}
|
|
|
0e0bbf |
|
|
|
0e0bbf |
+bool
|
|
|
0e0bbf |
+sr_abrt_create_core_stacktrace_from_core_hook(const char *directory,
|
|
|
0e0bbf |
+ pid_t thread_id,
|
|
|
0e0bbf |
+ const char *executable,
|
|
|
0e0bbf |
+ int signum,
|
|
|
0e0bbf |
+ char **error_message)
|
|
|
0e0bbf |
+{
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ struct sr_core_stacktrace *core_stacktrace;
|
|
|
0e0bbf |
+ core_stacktrace = sr_core_stacktrace_from_core_hook(thread_id, executable,
|
|
|
0e0bbf |
+ signum, error_message);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (!core_stacktrace)
|
|
|
0e0bbf |
+ return false;
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ fulfill_missing_values(core_stacktrace);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ char *json = sr_core_stacktrace_to_json(core_stacktrace);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ // Add newline to the end of core stacktrace file to make text
|
|
|
0e0bbf |
+ // editors happy.
|
|
|
0e0bbf |
+ json = sr_realloc(json, strlen(json) + 2);
|
|
|
0e0bbf |
+ strcat(json, "\n");
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ char *core_backtrace_filename = sr_build_path(directory, "core_backtrace", NULL);
|
|
|
0e0bbf |
+ bool success = sr_string_to_file(core_backtrace_filename,
|
|
|
0e0bbf |
+ json,
|
|
|
0e0bbf |
+ error_message);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ free(core_backtrace_filename);
|
|
|
0e0bbf |
+ free(json);
|
|
|
0e0bbf |
+ sr_core_stacktrace_free(core_stacktrace);
|
|
|
0e0bbf |
+ return success;
|
|
|
0e0bbf |
+}
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
struct sr_rpm_package *
|
|
|
0e0bbf |
sr_abrt_parse_dso_list(const char *text)
|
|
|
0e0bbf |
{
|
|
|
0e0bbf |
diff --git a/lib/core_unwind.c b/lib/core_unwind.c
|
|
|
0e0bbf |
index 8b7cc22..7ea66da 100644
|
|
|
0e0bbf |
--- a/lib/core_unwind.c
|
|
|
0e0bbf |
+++ b/lib/core_unwind.c
|
|
|
0e0bbf |
@@ -52,6 +52,20 @@ sr_parse_coredump(const char *coredump_filename,
|
|
|
0e0bbf |
|
|
|
0e0bbf |
#endif /* !defined WITH_LIBDWFL && !defined WITH_LIBUNWIND */
|
|
|
0e0bbf |
|
|
|
0e0bbf |
+#if !defined WITH_LIBDWFL
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+struct sr_core_stacktrace *
|
|
|
0e0bbf |
+sr_core_stacktrace_from_core_hook(pid_t thread_id,
|
|
|
0e0bbf |
+ const char *executable_filename,
|
|
|
0e0bbf |
+ int signum,
|
|
|
0e0bbf |
+ char **error_message);
|
|
|
0e0bbf |
+{
|
|
|
0e0bbf |
+ *error_message = sr_asprintf("satyr is built without live process unwind support");
|
|
|
0e0bbf |
+ return NULL;
|
|
|
0e0bbf |
+}
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+#endif /* !defined WITH_LIBDWFL */
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
/* FIXME: is there another way to pass the executable name to the find_elf
|
|
|
0e0bbf |
* callback? */
|
|
|
0e0bbf |
const char *executable_file = NULL;
|
|
|
0e0bbf |
diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c
|
|
|
0e0bbf |
index bbd4813..bea9b5f 100644
|
|
|
0e0bbf |
--- a/lib/core_unwind_elfutils.c
|
|
|
0e0bbf |
+++ b/lib/core_unwind_elfutils.c
|
|
|
0e0bbf |
@@ -28,6 +28,10 @@
|
|
|
0e0bbf |
|
|
|
0e0bbf |
#include <stdio.h>
|
|
|
0e0bbf |
#include <string.h>
|
|
|
0e0bbf |
+#include <unistd.h>
|
|
|
0e0bbf |
+#include <errno.h>
|
|
|
0e0bbf |
+#include <sys/ptrace.h>
|
|
|
0e0bbf |
+#include <sys/wait.h>
|
|
|
0e0bbf |
|
|
|
0e0bbf |
#define FRAME_LIMIT 256
|
|
|
0e0bbf |
|
|
|
0e0bbf |
@@ -202,4 +206,136 @@ fail:
|
|
|
0e0bbf |
return stacktrace;
|
|
|
0e0bbf |
}
|
|
|
0e0bbf |
|
|
|
0e0bbf |
+struct sr_core_stacktrace *
|
|
|
0e0bbf |
+sr_core_stacktrace_from_core_hook(pid_t tid,
|
|
|
0e0bbf |
+ const char *executable,
|
|
|
0e0bbf |
+ int signum,
|
|
|
0e0bbf |
+ char **error_msg)
|
|
|
0e0bbf |
+{
|
|
|
0e0bbf |
+ struct sr_core_stacktrace *stacktrace = NULL;
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ /* Initialize error_msg to 'no error'. */
|
|
|
0e0bbf |
+ if (error_msg)
|
|
|
0e0bbf |
+ *error_msg = NULL;
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ const Dwfl_Callbacks proc_cb =
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ .find_elf = dwfl_linux_proc_find_elf,
|
|
|
0e0bbf |
+ .find_debuginfo = find_debuginfo_none,
|
|
|
0e0bbf |
+ };
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ Dwfl *dwfl = dwfl_begin(&proc_cb);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (dwfl_linux_proc_report(dwfl, tid) != 0)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error_dwfl("dwfl_linux_proc_report");
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (dwfl_report_end(dwfl, NULL, NULL) != 0)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error_dwfl("dwfl_report_end");
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (ptrace(PTRACE_SEIZE, tid, NULL, (void *)(uintptr_t)PTRACE_O_TRACEEXIT) != 0)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("PTRACE_SEIZE (tid %u) failed: %s", (unsigned)tid, strerror(errno));
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (close(STDIN_FILENO) != 0)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("Failed to close stdin: %s", strerror(errno));
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ int status;
|
|
|
0e0bbf |
+ pid_t got = waitpid(tid, &status, 0);
|
|
|
0e0bbf |
+ if (got == -1)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("waitpid failed: %s", strerror(errno));
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (got != tid)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("waitpid returned %u but %u was expected", (unsigned)got, (unsigned)tid);
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (!WIFSTOPPED(status))
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("waitpid returned 0x%x but WIFSTOPPED was expected", status);
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if ((status >> 8) != (SIGTRAP | (PTRACE_EVENT_EXIT << 8)))
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("waitpid returned 0x%x but (status >> 8) == "
|
|
|
0e0bbf |
+ "(SIGTRAP | (PTRACE_EVENT_EXIT << 8)) was expected", status);
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (dwfl_linux_proc_attach(dwfl, tid, true) != 0)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error_dwfl("dwfl_linux_proc_attach");
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ stacktrace = sr_core_stacktrace_new();
|
|
|
0e0bbf |
+ if (!stacktrace)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("Failed to initialize stacktrace memory");
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ stacktrace->threads = sr_core_thread_new();
|
|
|
0e0bbf |
+ if (!stacktrace->threads)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("Failed to initialize thread memory");
|
|
|
0e0bbf |
+ sr_core_stacktrace_free(stacktrace);
|
|
|
0e0bbf |
+ stacktrace = 0;
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+ stacktrace->threads->id = tid;
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ struct frame_callback_arg frame_arg =
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ .frames_tail = &(stacktrace->threads->frames),
|
|
|
0e0bbf |
+ .error_msg = NULL,
|
|
|
0e0bbf |
+ .nframes = 0
|
|
|
0e0bbf |
+ };
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ int ret = dwfl_getthread_frames(dwfl, tid, frame_callback, &frame_arg);
|
|
|
0e0bbf |
+ if (ret != 0 && ret != CB_STOP_UNWIND)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ if (ret == -1)
|
|
|
0e0bbf |
+ set_error_dwfl("dwfl_getthread_frames");
|
|
|
0e0bbf |
+ else if (ret == DWARF_CB_ABORT)
|
|
|
0e0bbf |
+ {
|
|
|
0e0bbf |
+ set_error("%s", frame_arg.error_msg);
|
|
|
0e0bbf |
+ free(frame_arg.error_msg);
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+ else
|
|
|
0e0bbf |
+ set_error("Unknown error in dwfl_getthreads");
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ sr_core_stacktrace_free(stacktrace);
|
|
|
0e0bbf |
+ stacktrace = NULL;
|
|
|
0e0bbf |
+ goto fail;
|
|
|
0e0bbf |
+ }
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ truncate_long_thread(stacktrace->threads, &frame_arg);
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+ if (executable)
|
|
|
0e0bbf |
+ stacktrace->executable = sr_strdup(executable);
|
|
|
0e0bbf |
+ if (signum > 0)
|
|
|
0e0bbf |
+ stacktrace->signal = (uint16_t)signum;
|
|
|
0e0bbf |
+ stacktrace->crash_thread = stacktrace->threads;
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
+fail:
|
|
|
0e0bbf |
+ dwfl_end(dwfl);
|
|
|
0e0bbf |
+ return stacktrace;
|
|
|
0e0bbf |
+}
|
|
|
0e0bbf |
+
|
|
|
0e0bbf |
#endif /* WITH_LIBDWFL */
|
|
|
0e0bbf |
--
|
|
|
0e0bbf |
2.4.3
|
|
|
0e0bbf |
|