summaryrefslogtreecommitdiff
path: root/core/host/stack_trace.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/host/stack_trace.c')
-rw-r--r--core/host/stack_trace.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/core/host/stack_trace.c b/core/host/stack_trace.c
new file mode 100644
index 0000000000..67ceac5891
--- /dev/null
+++ b/core/host/stack_trace.c
@@ -0,0 +1,97 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <execinfo.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "host_task.h"
+#include "host_test.h"
+#include "timer.h"
+
+#define SIGNAL_TRACE_DUMP SIGTERM
+#define MAX_TRACE 30
+/*
+ * When trace dump is requested from signal handler, skip:
+ * _task_dump_trace_impl
+ * _task_dump_trace_dispath
+ * A function in libc
+ */
+#define SIGNAL_TRACE_OFFSET 3
+/*
+ * When trace dump is requested from task_dump_trace(), skip:
+ * task_dump_trace
+ * _task_dump_trace_impl
+ */
+#define DIRECT_TRACE_OFFSET 2
+
+static pthread_t main_thread;
+
+static void __attribute__((noinline)) _task_dump_trace_impl(int offset)
+{
+ void *trace[MAX_TRACE];
+ size_t sz;
+ char **messages;
+ char buf[256];
+ FILE *file;
+ int i, nb;
+
+ sz = backtrace(trace, MAX_TRACE);
+ messages = backtrace_symbols(trace + offset, sz - offset);
+
+ for (i = 0; i < sz - offset; ++i) {
+ fprintf(stderr, "#%-2d %s\n", i, messages[i]);
+ sprintf(buf, "addr2line %p -e %s",
+ trace[i + offset], __get_prog_name());
+ file = popen(buf, "r");
+ if (file) {
+ nb = fread(buf, 1, sizeof(buf) - 1, file);
+ buf[nb] = '\0';
+ fprintf(stderr, " %s", buf);
+ pclose(file);
+ }
+ }
+ fflush(stderr);
+ free(messages);
+}
+
+void __attribute__((noinline)) task_dump_trace(void)
+{
+ _task_dump_trace_impl(DIRECT_TRACE_OFFSET);
+}
+
+static void __attribute__((noinline)) _task_dump_trace_dispatch(int sig)
+{
+ int need_dispatch = 1;
+ task_id_t running = task_get_running();
+
+ if (!pthread_equal(pthread_self(), main_thread)) {
+ need_dispatch = 0;
+ } else if (!task_start_called()) {
+ fprintf(stderr, "Stack trace of main thread:\n");
+ need_dispatch = 0;
+ } else if (in_interrupt_context()) {
+ fprintf(stderr, "Stack trace of ISR:\n");
+ } else {
+ fprintf(stderr, "Stack trace of task %d (%s):\n",
+ running, task_get_name(running));
+ }
+
+ if (need_dispatch) {
+ pthread_kill(task_get_thread(running), SIGNAL_TRACE_DUMP);
+ } else {
+ _task_dump_trace_impl(SIGNAL_TRACE_OFFSET);
+ udelay(100 * MSEC); /* Leave time for stderr to flush */
+ exit(1);
+ }
+}
+
+void task_register_tracedump(void)
+{
+ /* Trace dumper MUST be registered from main thread */
+ main_thread = pthread_self();
+ signal(SIGNAL_TRACE_DUMP, _task_dump_trace_dispatch);
+}