emulator: Move trace dump to a separate module

The implementation of trace dump has little to do with task scheduling,
so we should move it to a separate module for cleaner code. This
requires exposing some emulator-specific task info, as defined in
host_task.h.

BUG=chrome-os-partner:19235
TEST=Pass all tests
BRANCH=None

Change-Id: Iba9bc0794a4e1dd4ddb92b98345162b398fa6a8d
Signed-off-by: Vic (Chun-Ju) Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/183238
This commit is contained in:
Vic (Chun-Ju) Yang
2014-01-21 17:18:33 +08:00
committed by chrome-internal-fetch
parent a02d7fa148
commit 7c686dd68b
10 changed files with 174 additions and 97 deletions

View File

@@ -8,4 +8,4 @@
CFLAGS_CPU=-fno-builtin
core-y=main.o task.o timer.o panic.o disabled.o
core-y=main.o task.o timer.o panic.o disabled.o stack_trace.o

26
core/host/host_task.h Normal file
View File

@@ -0,0 +1,26 @@
/* 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.
*/
/* Emulator task scheduling module */
#ifndef __CROS_EC_HOST_TASK_H
#define __CROS_EC_HOST_TASK_H
#include <pthread.h>
#include "task.h"
/**
* Returns the thread corresponding to the task.
*/
pthread_t task_get_thread(task_id_t tskid);
/**
* Returns the ID of the active task, regardless of current thread
* context.
*/
task_id_t task_get_running(void);
#endif /* __CROS_EC_HOST_TASK_H */

View File

@@ -9,6 +9,7 @@
#include "flash.h"
#include "hooks.h"
#include "keyboard_scan.h"
#include "stack_trace.h"
#include "system.h"
#include "task.h"
#include "test_util.h"

View File

@@ -8,6 +8,7 @@
#include "common.h"
#include "panic.h"
#include "stack_trace.h"
#include "test_util.h"
#include "util.h"

97
core/host/stack_trace.c Normal file
View File

@@ -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);
}

View File

@@ -5,7 +5,6 @@
/* Task scheduling / events module for Chrome EC operating system */
#include <execinfo.h>
#include <malloc.h>
#include <pthread.h>
#include <semaphore.h>
@@ -18,7 +17,7 @@
#include "atomic.h"
#include "common.h"
#include "console.h"
#include "host_test.h"
#include "host_task.h"
#include "task.h"
#include "task_id.h"
#include "test_util.h"
@@ -26,22 +25,6 @@
#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;
@@ -60,7 +43,6 @@ 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;
@@ -141,71 +123,6 @@ static void _task_execute_isr(int sig)
in_interrupt = 0;
}
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_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_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);
@@ -242,6 +159,16 @@ void interrupt_generator_udelay(unsigned us)
generator_sleeping = 0;
}
const char *task_get_name(task_id_t tskid)
{
return task_names[tskid];
}
pthread_t task_get_thread(task_id_t tskid)
{
return tasks[tskid].thread;
}
uint32_t task_set_event(task_id_t tskid, uint32_t event, int wait)
{
tasks[tskid].event = event;
@@ -307,6 +234,11 @@ task_id_t task_get_current(void)
return my_task_id;
}
task_id_t task_get_running(void)
{
return running_task_id;
}
void wait_for_task_started(void)
{
int i, ok;

25
include/stack_trace.h Normal file
View File

@@ -0,0 +1,25 @@
/* 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.
*/
/* Trace dump module */
#ifndef __CROS_EC_TRACE_H
#define __CROS_EC_TRACE_H
#ifdef EMU_BUILD
/*
* 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 task_register_tracedump(void) { }
static inline void task_dump_trace(void) { }
#endif
#endif /* __CROS_EC_TRACE_H */

View File

@@ -105,6 +105,11 @@ uint32_t task_wait_event(int timeout_us);
*/
void task_print_list(void);
/**
* Returns the name of the task.
*/
const char *task_get_name(task_id_t tskid);
#ifdef CONFIG_TASK_PROFILING
/**
* Start tracking an interrupt.

View File

@@ -10,6 +10,7 @@
#include "common.h"
#include "console.h"
#include "stack_trace.h"
#define RUN_TEST(n) \
do { \
@@ -130,19 +131,8 @@ 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);

View File

@@ -8,8 +8,8 @@
#include "common.h"
#include "console.h"
#include "test_util.h"
#include "task.h"
#include "test_util.h"
#include "timer.h"
#include "util.h"