summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.toolchain2
-rw-r--r--core/host/main.c2
-rw-r--r--core/host/panic.c3
-rw-r--r--core/host/task.c97
-rw-r--r--include/test_util.h14
-rwxr-xr-xutil/run_host_test5
6 files changed, 116 insertions, 7 deletions
diff --git a/Makefile.toolchain b/Makefile.toolchain
index 4897469a4c..f29059a605 100644
--- a/Makefile.toolchain
+++ b/Makefile.toolchain
@@ -55,5 +55,5 @@ BUILD_CFLAGS= $(LIBFTDI_CFLAGS) $(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN)
HOST_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN)
LDFLAGS=-nostdlib -X
BUILD_LDFLAGS=$(LIBFTDI_LDLIBS)
-HOST_TEST_LDFLAGS=-T core/host/host_exe.lds -lrt -pthread \
+HOST_TEST_LDFLAGS=-T core/host/host_exe.lds -lrt -pthread -rdynamic\
$(if $(TEST_COVERAGE),-fprofile-arcs,)
diff --git a/core/host/main.c b/core/host/main.c
index 2e01267626..2d3622c269 100644
--- a/core/host/main.c
+++ b/core/host/main.c
@@ -30,6 +30,8 @@ int main(int argc, char **argv)
{
__prog_name = argv[0];
+ task_register_tracedump();
+
register_test_end_hook();
flash_pre_init();
diff --git a/core/host/panic.c b/core/host/panic.c
index 468d024552..260329cc39 100644
--- a/core/host/panic.c
+++ b/core/host/panic.c
@@ -8,6 +8,7 @@
#include "common.h"
#include "panic.h"
+#include "test_util.h"
#include "util.h"
void panic_assert_fail(const char *msg, const char *func, const char *fname,
@@ -15,7 +16,7 @@ void panic_assert_fail(const char *msg, const char *func, const char *fname,
{
fprintf(stderr, "ASSERTION FAIL: %s:%d:%s - %s\n",
fname, linenum, func, msg);
- fflush(stderr);
+ task_dump_trace();
puts("Fail!"); /* Inform test runner */
fflush(stdout);
diff --git a/core/host/task.c b/core/host/task.c
index bb4b073997..156aeeed62 100644
--- a/core/host/task.c
+++ b/core/host/task.c
@@ -5,20 +5,43 @@
/* Task scheduling / events module for Chrome EC operating system */
+#include <execinfo.h>
+#include <malloc.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "atomic.h"
#include "common.h"
#include "console.h"
+#include "host_test.h"
#include "task.h"
#include "task_id.h"
#include "test_util.h"
#include "timer.h"
+#define SIGNAL_INTERRUPT SIGUSR1
+
+#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
+
struct emu_task_t {
pthread_t thread;
pthread_cond_t resume;
@@ -37,6 +60,7 @@ static pthread_cond_t scheduler_cond;
static pthread_mutex_t run_lock;
static task_id_t running_task_id;
static int task_started;
+static pthread_t main_thread;
static sem_t interrupt_sem;
static pthread_mutex_t interrupt_lock;
@@ -105,7 +129,7 @@ void interrupt_enable(void)
interrupt_disabled = 0;
}
-void _task_execute_isr(int sig)
+static void _task_execute_isr(int sig)
{
in_interrupt = 1;
pending_isr();
@@ -113,10 +137,75 @@ void _task_execute_isr(int sig)
in_interrupt = 0;
}
-void task_register_interrupt(void)
+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;
+
+ if (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_id, task_names[running_task_id]);
+ }
+
+ if (need_dispatch) {
+ pthread_kill(tasks[running_task_id].thread, 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);
+}
+
+static void task_register_interrupt(void)
{
sem_init(&interrupt_sem, 0, 0);
- signal(SIGUSR1, _task_execute_isr);
+ signal(SIGNAL_INTERRUPT, _task_execute_isr);
}
void task_trigger_test_interrupt(void (*isr)(void))
@@ -127,7 +216,7 @@ void task_trigger_test_interrupt(void (*isr)(void))
/* Suspend current task and excute ISR */
pending_isr = isr;
- pthread_kill(tasks[running_task_id].thread, SIGUSR1);
+ pthread_kill(tasks[running_task_id].thread, SIGNAL_INTERRUPT);
/* Wait for ISR to complete */
sem_wait(&interrupt_sem);
diff --git a/include/test_util.h b/include/test_util.h
index 182ad9d233..754230489f 100644
--- a/include/test_util.h
+++ b/include/test_util.h
@@ -27,6 +27,7 @@
do { \
if (!(n)) { \
ccprintf("%d: ASSERTION failed: %s\n", __LINE__, #n); \
+ task_dump_trace(); \
return EC_ERROR_UNKNOWN; \
} \
} while (0)
@@ -38,6 +39,7 @@
if (__ABS(n) >= t) { \
ccprintf("%d: ASSERT_ABS_LESS failed: abs(%d) is " \
"not less than %d\n", __LINE__, n, t); \
+ task_dump_trace(); \
return EC_ERROR_UNKNOWN; \
} \
} while (0)
@@ -50,6 +52,7 @@
ccprintf("%d: ASSERT_ARRAY_EQ failed at " \
"index=%d: %d != %d\n", __LINE__, \
__i, (int)(s)[__i], (int)(d)[__i]); \
+ task_dump_trace(); \
return EC_ERROR_UNKNOWN; \
} \
} while (0)
@@ -127,8 +130,19 @@ void interrupt_generator_udelay(unsigned us);
#ifdef EMU_BUILD
void wait_for_task_started(void);
+
+/*
+ * Register trace dump handler for emulator. Trace dump is printed to stderr
+ * when SIGUSR2 is received.
+ */
+void task_register_tracedump(void);
+
+/* Dump current stack trace */
+void task_dump_trace(void);
#else
static inline void wait_for_task_started(void) { }
+static inline void task_register_tracedump(void) { }
+static inline void task_dump_trace(void) { }
#endif
uint32_t prng(uint32_t seed);
diff --git a/util/run_host_test b/util/run_host_test
index ba59454527..a12c44053f 100755
--- a/util/run_host_test
+++ b/util/run_host_test
@@ -6,6 +6,7 @@
from cStringIO import StringIO
import pexpect
+import signal
import sys
import time
@@ -39,7 +40,9 @@ def RunOnce(test_name, log, timeout_secs):
child.close()
raise
finally:
- child.kill(15)
+ if child.isalive():
+ child.kill(signal.SIGTERM)
+ child.read()
log = StringIO()
tee_log = Tee(log)