Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 3470 → Rev 3471

/branches/tracing/uspace/app/trace/trace.c
41,6 → 41,8
#include <errno.h>
#include <udebug.h>
#include <async.h>
#include <task.h>
#include <loader/loader.h>
 
// Temporary: service and method names
#include "proto.h"
51,10 → 53,11
#include "syscalls.h"
#include "ipcp.h"
#include "errors.h"
#include "trace.h"
 
#define THBUF_SIZE 64
unsigned thread_hash_buf[THBUF_SIZE];
unsigned n_threads;
uintptr_t thread_hash_buf[THBUF_SIZE];
int n_threads;
 
int next_thread_id;
 
61,32 → 64,88
int phoneid;
int abort_trace;
 
unsigned thash;
uintptr_t thash;
volatile int paused;
 
void thread_trace_start(unsigned thread_hash);
void thread_trace_start(uintptr_t thread_hash);
 
static proto_t *proto_console;
static task_id_t task_id;
static loader_t *task_ldr;
 
static int task_connect(task_id_t task_id)
/** Combination of events/data to print. */
display_mask_t display_mask;
 
static int program_run_fibril(void *arg);
 
static void program_run(void)
{
fid_t fid;
 
fid = fibril_create(program_run_fibril, NULL);
if (fid == 0) {
printf("Error creating fibril\n");
exit(1);
}
 
fibril_add_ready(fid);
}
 
static int program_run_fibril(void *arg)
{
int rc;
 
printf("ipc_connect_task(%lld)... ", task_id);
/*
* This must be done in background as it will block until
* we let the task reply to this call.
*/
rc = loader_run(task_ldr);
if (rc != 0) {
printf("Error running program\n");
exit(1);
}
 
free(task_ldr);
task_ldr = NULL;
 
printf("program_run_fibril exiting\n");
return 0;
}
 
 
static int connect_task(task_id_t task_id)
{
int rc;
 
rc = ipc_connect_kbox(task_id);
printf("-> %d\n", rc);
 
if (rc == ENOTSUP) {
printf("You do not have userspace debugging support "
"compiled in the kernel.\n");
printf("Compile kernel with 'Support for userspace debuggers' "
"(CONFIG_UDEBUG) enabled.\n");
return rc;
}
 
if (rc < 0) {
printf("Error connecting\n");
printf("ipc_connect_task(%lld) -> %d ", task_id, rc);
return rc;
}
 
phoneid = rc;
if (rc < 0) return rc;
 
printf("udebug_begin()... ");
rc = udebug_begin(phoneid);
printf("-> %d\n", rc);
if (rc < 0) return rc;
if (rc < 0) {
printf("udebug_begin() -> %d\n", rc);
return rc;
}
 
printf("udebug_set_evmask(0x%x)... ", UDEBUG_EM_ALL);
rc = udebug_set_evmask(phoneid, UDEBUG_EM_ALL);
printf("-> %d\n", rc);
if (rc < 0) return rc;
if (rc < 0) {
printf("udebug_set_evmask(0x%x) -> %d\n ", UDEBUG_EM_ALL, rc);
return rc;
}
 
return 0;
}
98,66 → 157,101
size_t tb_needed;
int i;
 
printf("send IPC_M_DEBUG_THREAD_READ message\n");
rc = udebug_thread_read(phoneid, thread_hash_buf,
THBUF_SIZE*sizeof(unsigned), &tb_copied, &tb_needed);
printf("-> %d\n", rc);
if (rc < 0) return rc;
if (rc < 0) {
printf("udebug_thread_read() -> %d\n", rc);
return rc;
}
 
n_threads = tb_copied / sizeof(unsigned);
n_threads = tb_copied / sizeof(uintptr_t);
 
printf("thread IDs:");
for (i=0; i<n_threads; i++) {
printf(" %u", thread_hash_buf[i]);
printf("Threads:");
for (i = 0; i < n_threads; i++) {
printf(" [%d] (hash 0x%lx)", 1+i, thread_hash_buf[i]);
}
printf("\ntotal of %u threads\n", tb_needed/sizeof(unsigned));
printf("\ntotal of %u threads\n", tb_needed / sizeof(uintptr_t));
 
return 0;
}
 
static void print_sc_retval(int retval, rv_type_t rv_type)
void val_print(sysarg_t val, val_type_t v_type)
{
printf (" -> ");
if (rv_type == RV_INTEGER) {
printf("%d", retval);
} else if (rv_type == RV_HASH) {
printf("0x%08x", retval);
} else if (rv_type == RV_ERRNO) {
if (retval >= -15 && retval <= 0) {
printf("%d %s (%s)", retval,
err_desc[retval].name,
err_desc[retval].desc);
switch (v_type) {
case V_VOID:
printf("<void>");
break;
 
case V_INTEGER:
printf("%ld", val);
break;
 
case V_HASH:
case V_PTR:
printf("0x%08lx", val);
break;
 
case V_ERRNO:
if (val >= -15 && val <= 0) {
printf("%ld %s (%s)", val,
err_desc[-val].name,
err_desc[-val].desc);
} else {
printf("%d", retval);
printf("%ld", val);
}
} else if (rv_type == RV_INT_ERRNO) {
if (retval >= -15 && retval < 0) {
printf("%d %s (%s)", retval,
err_desc[retval].name,
err_desc[retval].desc);
break;
case V_INT_ERRNO:
if (val >= -15 && val < 0) {
printf("%ld %s (%s)", val,
err_desc[-val].name,
err_desc[-val].desc);
} else {
printf("%d", retval);
printf("%ld", val);
}
break;
 
case V_CHAR:
if (val >= 0x20 && val < 0x7f) {
printf("'%c'", val);
} else {
switch (val) {
case '\a': printf("'\\a'"); break;
case '\b': printf("'\\b'"); break;
case '\n': printf("'\\n'"); break;
case '\r': printf("'\\r'"); break;
case '\t': printf("'\\t'"); break;
case '\\': printf("'\\\\'"); break;
default: printf("'\\x%02lX'", val); break;
}
}
break;
}
}
 
 
static void print_sc_retval(sysarg_t retval, val_type_t val_type)
{
printf(" -> ");
val_print(retval, val_type);
putchar('\n');
}
 
static void print_sc_args(unsigned *sc_args, int n)
static void print_sc_args(sysarg_t *sc_args, int n)
{
int i;
 
putchar('(');
if (n > 0) printf("%d", sc_args[0]);
for (i=1; i<n; i++) {
printf(", %d", sc_args[i]);
if (n > 0) printf("%ld", sc_args[0]);
for (i = 1; i < n; i++) {
printf(", %ld", sc_args[i]);
}
putchar(')');
}
 
static void sc_ipc_call_async_fast(unsigned *sc_args, int sc_rc)
static void sc_ipc_call_async_fast(sysarg_t *sc_args, sysarg_t sc_rc)
{
ipc_call_t call;
int phoneid;
ipcarg_t phoneid;
if (sc_rc == IPC_CALLRET_FATAL || sc_rc == IPC_CALLRET_TEMPORARY)
return;
174,7 → 268,7
ipcp_call_out(phoneid, &call, sc_rc);
}
 
static void sc_ipc_call_async_slow(unsigned *sc_args, int sc_rc)
static void sc_ipc_call_async_slow(sysarg_t *sc_args, sysarg_t sc_rc)
{
ipc_call_t call;
int rc;
190,7 → 284,7
}
}
 
static void sc_ipc_call_sync_fast(unsigned *sc_args)
static void sc_ipc_call_sync_fast(sysarg_t *sc_args)
{
ipc_call_t question, reply;
int rc;
218,7 → 312,7
ipcp_call_sync(phoneidx, &question, &reply);
}
 
static void sc_ipc_call_sync_slow(unsigned *sc_args)
static void sc_ipc_call_sync_slow(sysarg_t *sc_args)
{
ipc_call_t question, reply;
int rc;
236,7 → 330,7
ipcp_call_sync(sc_args[0], &question, &reply);
}
 
static void sc_ipc_wait(unsigned *sc_args, int sc_rc)
static void sc_ipc_wait(sysarg_t *sc_args, int sc_rc)
{
ipc_call_t call;
int rc;
253,9 → 347,10
}
}
 
static void event_syscall_b(unsigned thread_id, unsigned thread_hash, unsigned sc_id, int sc_rc)
static void event_syscall_b(unsigned thread_id, uintptr_t thread_hash,
unsigned sc_id, sysarg_t sc_rc)
{
unsigned sc_args[6];
sysarg_t sc_args[6];
int rc;
 
/* Read syscall arguments */
271,16 → 366,19
return;
}
 
/* Print syscall name, id and arguments */
printf("%s", syscall_desc[sc_id].name);
print_sc_args(sc_args, syscall_desc[sc_id].n_args);
if ((display_mask & DM_SYSCALL) != 0) {
/* Print syscall name and arguments */
printf("%s", syscall_desc[sc_id].name);
print_sc_args(sc_args, syscall_desc[sc_id].n_args);
}
 
async_serialize_end();
}
 
static void event_syscall_e(unsigned thread_id, unsigned thread_hash, unsigned sc_id, int sc_rc)
static void event_syscall_e(unsigned thread_id, uintptr_t thread_hash,
unsigned sc_id, sysarg_t sc_rc)
{
unsigned sc_args[6];
sysarg_t sc_args[6];
int rv_type;
int rc;
 
297,8 → 395,11
return;
}
 
rv_type = syscall_desc[sc_id].rv_type;
print_sc_retval(sc_rc, rv_type);
if ((display_mask & DM_SYSCALL) != 0) {
/* Print syscall return value */
rv_type = syscall_desc[sc_id].rv_type;
print_sc_retval(sc_rc, rv_type);
}
 
switch (sc_id) {
case SYS_IPC_CALL_ASYNC_FAST:
323,10 → 424,10
async_serialize_end();
}
 
static void event_thread_b(unsigned hash)
static void event_thread_b(uintptr_t hash)
{
async_serialize_start();
printf("new thread, hash 0x%x\n", hash);
printf("New thread, hash 0x%lx\n", hash);
async_serialize_end();
 
thread_trace_start(hash);
336,14 → 437,14
{
int rc;
unsigned ev_type;
unsigned thread_hash;
uintptr_t thread_hash;
unsigned thread_id;
unsigned val0, val1;
sysarg_t val0, val1;
 
thread_hash = (unsigned)thread_hash_arg;
thread_hash = (uintptr_t)thread_hash_arg;
thread_id = next_thread_id++;
 
printf("trace_loop(%d)\n", thread_id);
printf("Start tracing thread [%d] (hash 0x%lx)\n", thread_id, thread_hash);
 
while (!abort_trace) {
 
353,7 → 454,7
 
// printf("rc = %d, ev_type=%d\n", rc, ev_type);
if (ev_type == UDEBUG_EVENT_FINISHED) {
printf("thread %u debugging finished\n", thread_id);
/* Done tracing this thread */
break;
}
 
366,24 → 467,24
event_syscall_e(thread_id, thread_hash, val0, (int)val1);
break;
case UDEBUG_EVENT_STOP:
printf("stop event\n");
printf("waiting for resume\n");
printf("Stop event\n");
printf("Waiting for resume\n");
while (paused) {
usleep(1000000);
fibril_yield();
printf(".");
}
printf("resumed\n");
printf("Resumed\n");
break;
case UDEBUG_EVENT_THREAD_B:
event_thread_b(val0);
break;
case UDEBUG_EVENT_THREAD_E:
printf("thread 0x%x exited\n", val0);
printf("Thread 0x%lx exited\n", val0);
abort_trace = 1;
break;
default:
printf("unknown event type %d\n", ev_type);
printf("Unknown event type %d\n", ev_type);
break;
}
}
390,11 → 491,11
 
}
 
printf("trace_loop(%d) exiting\n", thread_id);
printf("Finished tracing thread [%d]\n", thread_id);
return 0;
}
 
void thread_trace_start(unsigned thread_hash)
void thread_trace_start(uintptr_t thread_hash)
{
fid_t fid;
 
407,25 → 508,61
fibril_add_ready(fid);
}
 
static void trace_active_task(task_id_t task_id)
static loader_t *preload_task(const char *path, char *const argv[],
task_id_t *task_id)
{
int i;
loader_t *ldr;
int rc;
int c;
 
printf("Syscall Tracer\n");
/* Spawn a program loader */
ldr = loader_spawn();
if (ldr == NULL)
return 0;
 
rc = task_connect(task_id);
if (rc < 0) {
printf("Failed to connect to task %lld\n", task_id);
return;
}
/* Get task ID. */
rc = loader_get_task_id(ldr, task_id);
if (rc != EOK)
goto error;
 
printf("Connected to task %lld\n", task_id);
/* Send program pathname */
rc = loader_set_pathname(ldr, path);
if (rc != EOK)
goto error;
 
/* Send arguments */
rc = loader_set_args(ldr, argv);
if (rc != EOK)
goto error;
 
/* Load the program. */
rc = loader_load_program(ldr);
if (rc != EOK)
goto error;
 
/* Success */
return ldr;
 
/* Error exit */
error:
loader_abort(ldr);
free(ldr);
return NULL;
}
 
static void trace_task(task_id_t task_id)
{
int i;
int rc;
int c;
 
ipcp_init();
ipcp_connection_set(1, 0, proto_console);
 
/*
* User apps now typically have console on phone 3.
* (Phones 1 and 2 are used by the loader).
*/
ipcp_connection_set(3, 0, proto_console);
 
rc = get_thread_list();
if (rc < 0) {
printf("Failed to get thread list (error %d)\n", rc);
451,7 → 588,7
}
}
 
printf("terminate debugging session...\n");
printf("\nTerminate debugging session...\n");
abort_trace = 1;
udebug_end(phoneid);
ipc_hangup(phoneid);
458,7 → 595,7
 
ipcp_cleanup();
 
printf("done\n");
printf("Done\n");
return;
}
 
467,6 → 604,22
proto_t *p;
oper_t *o;
 
val_type_t arg_def[OPER_MAX_ARGS] = {
V_INTEGER,
V_INTEGER,
V_INTEGER,
V_INTEGER,
V_INTEGER
};
 
val_type_t resp_def[OPER_MAX_ARGS] = {
V_INTEGER,
V_INTEGER,
V_INTEGER,
V_INTEGER,
V_INTEGER
};
 
next_thread_id = 1;
paused = 0;
 
473,38 → 626,45
proto_init();
 
p = proto_new("vfs");
o = oper_new("read");
o = oper_new("read", 1, arg_def, V_ERRNO, 1, resp_def);
proto_add_oper(p, VFS_READ, o);
o = oper_new("write");
o = oper_new("write", 1, arg_def, V_ERRNO, 1, resp_def);
proto_add_oper(p, VFS_WRITE, o);
o = oper_new("truncate");
o = oper_new("truncate", 5, arg_def, V_ERRNO, 0, resp_def);
proto_add_oper(p, VFS_TRUNCATE, o);
o = oper_new("mount");
o = oper_new("mount", 2, arg_def, V_ERRNO, 0, resp_def);
proto_add_oper(p, VFS_MOUNT, o);
o = oper_new("unmount");
proto_add_oper(p, VFS_UNMOUNT, o);
/* o = oper_new("unmount", 0, arg_def);
proto_add_oper(p, VFS_UNMOUNT, o);*/
 
proto_register(SERVICE_VFS, p);
 
p = proto_new("console");
o = oper_new("getchar");
resp_def[0] = V_CHAR;
o = oper_new("getchar", 0, arg_def, V_INTEGER, 2, resp_def);
proto_add_oper(p, CONSOLE_GETCHAR, o);
o = oper_new("putchar");
 
arg_def[0] = V_CHAR;
o = oper_new("putchar", 1, arg_def, V_VOID, 0, resp_def);
proto_add_oper(p, CONSOLE_PUTCHAR, o);
o = oper_new("clear");
o = oper_new("clear", 0, arg_def, V_VOID, 0, resp_def);
proto_add_oper(p, CONSOLE_CLEAR, o);
o = oper_new("goto");
 
arg_def[0] = V_INTEGER; arg_def[1] = V_INTEGER;
o = oper_new("goto", 2, arg_def, V_VOID, 0, resp_def);
proto_add_oper(p, CONSOLE_GOTO, o);
o = oper_new("getsize");
 
resp_def[0] = V_INTEGER; resp_def[1] = V_INTEGER;
o = oper_new("getsize", 0, arg_def, V_INTEGER, 2, resp_def);
proto_add_oper(p, CONSOLE_GETSIZE, o);
o = oper_new("flush");
o = oper_new("flush", 0, arg_def, V_VOID, 0, resp_def);
proto_add_oper(p, CONSOLE_FLUSH, o);
o = oper_new("set_style");
 
arg_def[0] = V_INTEGER; arg_def[1] = V_INTEGER;
o = oper_new("set_style", 2, arg_def, V_INTEGER, 0, resp_def);
proto_add_oper(p, CONSOLE_SET_STYLE, o);
o = oper_new("cursor_visibility");
o = oper_new("cursor_visibility", 1, arg_def, V_VOID, 0, resp_def);
proto_add_oper(p, CONSOLE_CURSOR_VISIBILITY, o);
o = oper_new("flush");
proto_add_oper(p, CONSOLE_FLUSH, o);
 
proto_console = p;
proto_register(SERVICE_CONSOLE, p);
512,30 → 672,133
 
static void print_syntax()
{
printf("syntax: trace <task_id>\n");
printf("Syntax:\n");
printf("\ttrace [+<events>] <executable> [<arg1> [...]]\n");
printf("or\ttrace [+<events>] -t <task_id>\n");
printf("Events: (default is +tp)\n");
printf("\n");
printf("\tt ... Thread creation and termination\n");
printf("\ts ... System calls\n");
printf("\ti ... Low-level IPC\n");
printf("\tp ... Protocol level\n");
printf("\n");
printf("Examples:\n");
printf("\ttrace +s /app/tetris\n");
printf("\ttrace +tsip -t 12\n");
}
 
int main(int argc, char *argv[])
static display_mask_t parse_display_mask(char *text)
{
task_id_t task_id;
display_mask_t dm;
char *c;
 
c = text;
 
while (*c) {
switch (*c) {
case 't': dm = dm | DM_THREAD; break;
case 's': dm = dm | DM_SYSCALL; break;
case 'i': dm = dm | DM_IPC; break;
case 'p': dm = dm | DM_SYSTEM | DM_USER; break;
default:
printf("Unexpected event type '%c'\n", *c);
exit(1);
}
 
++c;
}
 
return dm;
}
 
static int parse_args(int argc, char *argv[])
{
char *arg;
char *err_p;
 
if (argc != 2) {
printf("Mising argument\n");
task_id = 0;
 
--argc; ++argv;
 
while (argc > 0) {
arg = *argv;
if (arg[0] == '+') {
display_mask = parse_display_mask(&arg[1]);
} else if (arg[0] == '-') {
if (arg[1] == 't') {
/* Trace an already running task */
--argc; ++argv;
task_id = strtol(*argv, &err_p, 10);
task_ldr = NULL;
if (*err_p) {
printf("Task ID syntax error\n");
print_syntax();
return -1;
}
} else {
printf("Uknown option '%s'\n", arg[0]);
print_syntax();
return -1;
}
} else {
break;
}
 
--argc; ++argv;
}
 
if (task_id != 0) {
if (argc == 0) return 0;
printf("Extra arguments\n");
print_syntax();
return 1;
return -1;
}
 
task_id = strtol(argv[1], &err_p, 10);
if (argc < 1) {
printf("Missing argument\n");
print_syntax();
return -1;
}
 
if (*err_p) {
printf("Task ID syntax error\n");
print_syntax();
/* Preload the specified program file. */
printf("Spawning '%s' with arguments:\n", *argv);
{
char **cp = argv;
while (*cp) printf("'%s'\n", *cp++);
}
task_ldr = preload_task(*argv, argv, &task_id);
 
return 0;
}
 
int main(int argc, char *argv[])
{
int rc;
 
printf("System Call / IPC Tracer\n");
 
display_mask = DM_THREAD | DM_SYSTEM | DM_USER;
 
if (parse_args(argc, argv) < 0)
return 1;
 
main_init();
 
rc = connect_task(task_id);
if (rc < 0) {
printf("Failed connecting to task %lld\n", task_id);
return 1;
}
 
main_init();
trace_active_task(task_id);
printf("Connected to task %lld\n", task_id);
 
if (task_ldr != NULL) {
program_run();
}
 
trace_task(task_id);
 
return 0;
}
 
/** @}