diff options
Diffstat (limited to 'core/host/stack_trace.c')
-rw-r--r-- | core/host/stack_trace.c | 97 |
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); +} |