/*
* Copyright (c) 2008 Jiri Svoboda
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup debug
* @{
*/
/** @file
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syscall.h>
#include <ipc/ipc.h>
#include <fibril.h>
#include <errno.h>
#include <udebug.h>
#include <async.h>
#include <string.h>
#include "cmd.h"
#include "main.h"
void thread_debug_start(unsigned thread_hash);
#define INBUF_SIZE 64
char in_buf[INBUF_SIZE];
#define MAX_ARGC 10
int cmd_argc;
char *cmd_argv[MAX_ARGC + 1]; /* need one spare field for cmd_split() */
#define THBUF_SIZE 64
thash_t thread_hash_buf[THBUF_SIZE];
unsigned n_threads;
int next_thread_id;
int app_phone;
volatile bool abort_debug;
thash_t thash;
volatile int paused;
void read_line(char *buffer, int n)
{
char c;
int i;
i = 0;
while (i < n - 1) {
if (c == '\n') break;
if (c == '\b') {
if (i > 0) {
--i;
}
continue;
}
buffer[i++] = c;
}
buffer[i] = '\0';
}
void command_split(char *cmd_str)
{
char *p = cmd_str;
if (*p == '\0') {
cmd_argc = 0;
return;
}
cmd_argc = 1;
cmd_argv[0] = p;
while (*p != '\0') {
if (*p == ' ') {
cmd_argv[cmd_argc++] = p + 1;
*p = '\0';
}
++p;
}
}
void command_run(void)
{
int i;
int cmp_len;
int len;
int idx_found;
int num_found;
cmp_len = 1;
while (cmp_len <= len + 1) {
num_found = 0;
i = 0;
while (cmd_table[i].name != NULL) {
if (strncmp(cmd_table
[i
].
name, cmd_argv
[0], cmp_len
) == 0) {
idx_found = i;
++num_found;
}
++i;
}
if (num_found < 2) break;
--cmp_len;
}
if (num_found == 0) {
printf("Unknown command. Try one of:\n");
cmd_help(0, NULL);
return;
}
if (cmd_argc - 1 != cmd_table[idx_found].argc) {
printf("Command '%s' expects %d arguments\n",
cmd_table[idx_found].name, cmd_table[idx_found].argc);
return;
}
(*cmd_table[idx_found].proc)(cmd_argc, cmd_argv);
}
int task_connect(int taskid)
{
int rc;
printf("ipc_connect_kbox(%d)... ", taskid
);
rc = ipc_connect_kbox(taskid);
app_phone = rc;
if (rc < 0) return rc;
rc = udebug_begin(app_phone);
if (rc < 0) return rc;
printf("udebug_set_evmask(0x%x)... ", UDEBUG_EM_ALL
);
rc = udebug_set_evmask(app_phone, UDEBUG_EM_ALL);
if (rc < 0) return rc;
return 0;
}
int get_thread_list(void)
{
int rc;
int tb_copied;
int tb_needed;
int i;
printf("send IPC_M_DEBUG_THREAD_READ message\n");
rc = udebug_thread_read(app_phone, (unsigned)thread_hash_buf,
THBUF_SIZE*sizeof(unsigned), &tb_copied, &tb_needed);
if (rc < 0) return rc;
n_threads = tb_copied / sizeof(unsigned);
for (i=0; i<n_threads; i++) {
printf(" %u", thread_hash_buf
[i
]);
}
printf("\ntotal of %u threads\n", tb_needed
/sizeof(unsigned));
return 0;
}
void event_thread_b(unsigned hash)
{
async_serialize_start();
printf("new thread, hash 0x%x\n", hash
);
async_serialize_end();
thread_debug_start(hash);
}
void debug_loop(void *thread_hash_arg)
{
int rc;
unsigned ev_type;
unsigned thread_hash;
unsigned thread_id;
unsigned val0, val1;
thread_hash = (unsigned)thread_hash_arg;
thread_id = next_thread_id++;
printf("debug_loop(%d)\n", thread_id
);
while (!abort_debug) {
/* Run thread until an event occurs */
rc = udebug_go(app_phone, thread_hash,
&ev_type, &val0, &val1);
// printf("rc = %d, ev_type=%d\n", rc, ev_type);
if (ev_type == UDEBUG_EVENT_FINISHED) {
printf("thread %u debugging finished\n", thread_id
);
break;
}
if (rc >= 0) {
switch (ev_type) {
case UDEBUG_EVENT_STOP:
printf("waiting for resume\n");
while (paused) {
usleep(1000000);
fibril_yield();
}
break;
case UDEBUG_EVENT_THREAD_B:
event_thread_b(val0);
break;
case UDEBUG_EVENT_THREAD_E:
printf("thread 0x%x exited\n", val0
);
abort_debug = true;
break;
default:
printf("unknown event type %d\n", ev_type
);
break;
}
}
}
printf("debug_loop(%d) exiting\n", thread_id
);
}
void thread_debug_start(unsigned thread_hash)
{
fid_t fid;
thash = thread_hash;
fid = fibril_create(debug_loop, (void *)thread_hash);
if (fid == 0) {
printf("Warning: Failed creating fibril\n");
}
fibril_add_ready(fid);
}
void debug_active_task(void)
{
int taskid;
int i;
int rc;
int c;
printf("Breakpoint Debugger\n");
printf("Press 'c' to connect\n");
taskid = 14;
rc = task_connect(taskid);
if (rc < 0) {
printf("Failed to connect to task %d\n", taskid
);
return;
}
printf("Connected to task %d\n", taskid
);
rc = get_thread_list();
if (rc < 0) {
printf("Failed to get thread list (error %d)\n", rc
);
return;
}
abort_debug = false;
for (i = 0; i < n_threads; i++) {
thread_debug_start(thread_hash_buf[i]);
}
while (!quit) {
read_line(in_buf, INBUF_SIZE);
command_split(in_buf);
if (cmd_argc == 0) continue;
command_run();
}
printf("terminate debugging session...\n");
abort_debug = true;
udebug_end(app_phone);
ipc_hangup(app_phone);
return;
}
static void main_init(void)
{
next_thread_id = 1;
paused = 0;
}
int main(void)
{
main_init();
while (1) {
debug_active_task();
}
}
/** @}
*/