diff --git a/TODO.txt b/TODO.txt index 833a266..0880960 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,10 +1,13 @@ @fix * fix fixed size 'space' character * fix tabulation +* fix destructor +* fix fx9860 support @update +* hexdump: search commands @feature -* call graph export for laptop * night mode (refacto gui and handle color template) * transform gintracer into a librairy +* handle session (send the session instead of ucontext !) diff --git a/gintrace.g3a b/gintrace.g3a index 93989d3..c13b86d 100644 Binary files a/gintrace.g3a and b/gintrace.g3a differ diff --git a/include/gintrace/gui/input.h b/include/gintrace/gui/input.h index 5b754d0..a4e1bbc 100644 --- a/include/gintrace/gui/input.h +++ b/include/gintrace/gui/input.h @@ -4,10 +4,11 @@ #include #include -/* intput(): Handle user input */ -extern int input_read(char *buffer, size_t size); +/* intput_read(): Handle user input */ +extern int input_read(char *buffer, size_t size, const char *prefix, ...); -/* intput(): display message then wait user event */ +/* intput_write(): display message then wait user event */ extern int input_write(const char *format, ...); - +/* intput(): display message */ +extern int input_write_noint(const char *format, ...); #endif /*__GINTRACE_GUI_INPUT_H__*/ diff --git a/include/gintrace/menu/callgraph.h b/include/gintrace/menu/callgraph.h index c9897ab..f551ee5 100644 --- a/include/gintrace/menu/callgraph.h +++ b/include/gintrace/menu/callgraph.h @@ -30,7 +30,6 @@ struct callgraph { } cursor; struct callnode *root; struct callnode *parent; - int callnode_counter; }; /* extern menu information */ diff --git a/src/gui/input.c b/src/gui/input.c index 481a4cd..289bd1e 100644 --- a/src/gui/input.c +++ b/src/gui/input.c @@ -13,14 +13,16 @@ /* internal structure used to store many information */ struct { struct { - int visible; - } cursor; + uint8_t cursor :1; /* display cursor */ + uint8_t meta :1; /* display meta info */ + uint8_t const :6; + } config; struct { - uint8_t alpha: 1; - uint8_t shift: 1; - uint8_t ctrl: 1; - uint8_t exit: 1; - uint8_t const: 4; + uint8_t alpha :1; + uint8_t shift :1; + uint8_t ctrl :1; + uint8_t exit :1; + uint8_t const :4; } mode; struct { size_t max; @@ -28,41 +30,35 @@ struct { off_t cursor; char *data; } buffer; + struct { + size_t size; + char data[256]; + } prefix; } input_info; //--- // Display management //--- -/* input_display(): Display the input area */ -void input_display(void) +/* input_print(): Print line */ +static void input_print(int *cursor_x, int *cursor_y, const char *str) { - int cursor_x; - int cursor_y; uint16_t tmp; int cursor; + char c; int x; int y; - /* add cursor mark */ - if (input_info.cursor.visible == 1) - input_info.buffer.data[input_info.buffer.cursor] |= 0x80; - - /* display part */ - cursor_x = 0; - cursor_y = GUI_DISP_NB_ROW - 1; - cursor_y -= input_info.buffer.size / GUI_DISP_NB_COLUMN; - drect(0, cursor_y * (FHEIGHT + 1) - 1, DWIDTH, DHEIGHT, C_WHITE); - dhline(cursor_y * (FHEIGHT + 1) - 3, C_BLACK); - dhline(cursor_y * (FHEIGHT + 1) - 2, C_BLACK); - for (size_t i = 0; i < input_info.buffer.size; ++i) { + if (str == NULL) + return; + for (size_t i = 0; str[i] != '\0'; ++i) { /* get the cursor and remove the potential cursor marker */ - cursor = ((input_info.buffer.data[i] & 0x80) != 0); - input_info.buffer.data[i] &= 0x7f; + cursor = ((str[i] & 0x80) != 0); + c = str[i] & 0x7f; /* display part (character + cursor if needed) */ - x = cursor_x * (FWIDTH + 1); - y = cursor_y * (FHEIGHT + 1); - tmp = input_info.buffer.data[i] << 8; + x = (*cursor_x) * (FWIDTH + 1); + y = (*cursor_y) * (FHEIGHT + 1); + tmp = c << 8; dtext(x, y, C_BLACK, (void*)&tmp); if (cursor != 0) { dline(x, y + (FHEIGHT + 1), x + (FWIDTH + 1) - 2, @@ -70,16 +66,53 @@ void input_display(void) } /* update cursor if needed */ - cursor_x += 1; - if (cursor_x >= GUI_DISP_NB_COLUMN) { - cursor_x = 0; - cursor_y += 1; + *cursor_x += 1; + if (*cursor_x >= GUI_DISP_NB_COLUMN) { + *cursor_x = 0; + *cursor_y += 1; } } +} + +/* input_display(): Display the input area */ +static void input_display(void) +{ + int cursor_x; + int cursor_y; + size_t bytes; + + /* add cursor mark */ + if (input_info.config.cursor == 1) + input_info.buffer.data[input_info.buffer.cursor] |= 0x80; + + /* calculate the number of bytes to display */ + /* TODO: remove me, check line discipline */ + bytes = input_info.buffer.size + input_info.prefix.size; + if (input_info.config.meta == 1) + bytes += 4; + + /* calculate the X/Y position */ + cursor_x = 0; + cursor_y = (GUI_DISP_NB_ROW - 1) - (bytes / GUI_DISP_NB_COLUMN); + + /* display information */ + drect(0, cursor_y * (FHEIGHT + 1) - 1, DWIDTH, DHEIGHT, C_WHITE); + dhline(cursor_y * (FHEIGHT + 1) - 3, C_BLACK); + dhline(cursor_y * (FHEIGHT + 1) - 2, C_BLACK); + if (input_info.config.meta == 1) { + char meta[] = "[l]:"; + if (input_info.mode.alpha == 1) + meta[1] = 'L'; + if (input_info.mode.shift == 1) + meta[1] = 'n'; + input_print(&cursor_x, &cursor_y, meta); + } + input_print(&cursor_x, &cursor_y, input_info.prefix.data); + input_print(&cursor_x, &cursor_y, input_info.buffer.data); dupdate(); /* remove cursor mark */ - if (input_info.cursor.visible == 1) + if (input_info.config.cursor == 1) input_info.buffer.data[input_info.buffer.cursor] &= ~0x80; } @@ -210,7 +243,7 @@ static int input_key_buffer_update(key_event_t key_event) } /* input_read(): Handle user input */ -int input_read(char *buffer, size_t size) +int input_read(char *buffer, size_t size, const char *prefix, ...) { key_event_t key; uint16_t *secondary; @@ -226,10 +259,17 @@ int input_read(char *buffer, size_t size) memset(&input_info, 0x00, sizeof(input_info)); memset(buffer, 0x00, size); - /* save terminal information */ + /* setup internal information */ input_info.buffer.data = buffer; input_info.buffer.size = 1; input_info.buffer.max = size; + if (prefix != NULL) { + va_list _args; + va_start(_args, prefix); + input_info.prefix.size = vsnprintf(input_info.prefix.data, + 32, prefix, _args); + va_end(_args); + } /* Gint workaround to freeze the current display */ dgetvram(&main, &secondary); @@ -240,8 +280,10 @@ int input_read(char *buffer, size_t size) } memcpy(secondary, main, 2*396*224); + /* keyboard handling */ - input_info.cursor.visible = 1; + input_info.config.cursor = 1; + input_info.config.meta = 1; while (input_info.mode.exit == 0) { input_display(); key = getkey_opt(GETKEY_REP_ALL | GETKEY_MENU, NULL); @@ -289,8 +331,46 @@ int input_write(const char *format, ...) memcpy(secondary, main, 2*396*224); /* display and wait user event */ - input_info.cursor.visible = 0; + input_info.config.cursor = 0; + input_info.config.meta = 0; input_display(); getkey(); return (0); } +/* TODO: merge with input_write*/ +int input_write_noint(const char *format, ...) +{ + char buffer[512]; + va_list _args; + uint16_t *secondary; + uint16_t *main; + size_t size; + void *tmp; + + va_start(_args, format); + size = vsnprintf(buffer, 512, format, _args); + va_end(_args); + + /* initialize internal data */ + memset(&input_info, 0x00, sizeof(input_info)); + + /* save terminal information */ + input_info.buffer.data = buffer; + input_info.buffer.size = size; + input_info.buffer.max = 512; + + /* Gint workaround to freeze the current display */ + dgetvram(&main, &secondary); + if (gint_vram == main) { + tmp = main; + main = secondary; + secondary = tmp; + } + memcpy(secondary, main, 2*396*224); + + /* display and wait user event */ + input_info.config.cursor = 0; + input_info.config.meta = 0; + input_display(); + return (0); +} diff --git a/src/gui/menu.c b/src/gui/menu.c index caa0a5b..eef885d 100644 --- a/src/gui/menu.c +++ b/src/gui/menu.c @@ -154,7 +154,7 @@ int menu_keyboard(struct menu_group *gmenu) goto check_fkeys; } char buf[256]; - if (input_read(buf, 256) <= 0) + if (input_read(buf, 256, NULL) <= 0) goto check_fkeys; int ac; char **av; diff --git a/src/menu/callgraph.c b/src/menu/callgraph.c index c513bd1..ace6d3e 100644 --- a/src/menu/callgraph.c +++ b/src/menu/callgraph.c @@ -2,12 +2,15 @@ #include "gintrace/ubc.h" #include "gintrace/gui/menu.h" #include "gintrace/gui/display.h" +#include "gintrace/gui/input.h" #include #include #include #include #include +#include +#include #include "./src/menu/internal/dictionary.h" @@ -15,13 +18,15 @@ /* TODO: find a way to have local information (session) */ struct callgraph callgraph; +/* internal buffer */ +static char line[256]; + //--- // callode management //--- /* callnode_create(): create a new callnode */ static struct callnode *callnode_create(struct callnode *parent, - struct ucontext *context, - int type, uintptr_t address) + struct ucontext *context, int type, uintptr_t address) { struct callnode *node; @@ -31,7 +36,6 @@ static struct callnode *callnode_create(struct callnode *parent, node->type = type; node->parent = parent; node->address = address; - callgraph.callnode_counter += 1; } return (node); } @@ -47,12 +51,70 @@ static void callnode_add_child(struct callnode *parent, struct callnode *child) *sibling = child; } +/* callnode_generate_info(): Genera information about a node */ +static size_t callnode_generate_info(char *buf, + size_t max, struct callnode *node) +{ + const char *addrname; + const char *type; + + /* generate the line */ + type = "(err)"; + if (node->type == callnode_type_root) + type = "(root)"; + if (node->type == callnode_type_bsr) + type = "(bsr)"; + if (node->type == callnode_type_bsrf) + type = "(bsrf)"; + if (node->type == callnode_type_jsr) + type = "(jsr)"; + if (node->type == callnode_type_jmp) + type = "(jmp)"; + if (node->type == callnode_type_rte) + type = "(rte)"; + if (node->type == callnode_type_rts) + type = "(rts)"; + + /* check special address */ + addrname = dictionary_syscalls_check((void*)node->address); + if (addrname == NULL) + addrname = dictionary_notes_check((void*)node->address); + if (addrname == NULL) + return(snprintf(buf, max, "%s %p", type, (void*)node->address)); + return (snprintf(buf, max, "%s %p - %s", + type, (void*)node->address, addrname)); +} + +/* callnode_get_size(): Count the number of bytes that the callgraph take */ +static size_t callnode_export(int fd, struct callnode *node) +{ + if (node == NULL) + return (0); + + size_t size = 0; + size += sizeof(size_t); + size += sizeof(uintptr_t); + size += sizeof(struct callnode); + size += callnode_generate_info(line, 256, node); + if (fd >= 0) { + BFile_Write(fd, &size, sizeof(size)); + BFile_Write(fd, &node, sizeof(&node)); + BFile_Write(fd, node, sizeof(struct callnode)); + BFile_Write(fd, line, size); + } + + /* check other node */ + size += callnode_export(fd, node->child); + size += callnode_export(fd, node->sibling); + return (size); +} + /* callnode_display(): Display callnode information */ static void callnode_display(struct callnode *node, uint32_t bitmap[4], int *row, int depth) { - static char line[256]; char shift; + char pipe; int idx; int i; @@ -76,61 +138,28 @@ static void callnode_display(struct callnode *node, uint32_t bitmap[4], } /* generate the line */ - char pipe = '|'; - const char *type = "(err)"; + pipe = '|'; bitmap[idx] |= 1 << (depth & 0x1f); - if (node->type == callnode_type_root) - type = "(root)"; - if (node->type == callnode_type_bsr) - type = "(bsr)"; - if (node->type == callnode_type_bsrf) - type = "(bsrf)"; - if (node->type == callnode_type_jsr) - type = "(jsr)"; - if (node->type == callnode_type_jmp) { + if (node->type == callnode_type_jmp + || node->type == callnode_type_rte + || node->type == callnode_type_rts) { bitmap[idx] &= ~(1 << (depth & 0x1f)); - type = "(jmp)"; pipe = '`'; } - if (node->type == callnode_type_rte) { - bitmap[idx] &= ~(1 << (depth & 0x1f)); - type = "(rte)"; - pipe = '`'; - } - if (node->type == callnode_type_rts) { - bitmap[idx] &= ~(1 << (depth & 0x1f)); - type = "(rts)"; - pipe = '`'; - } - - /* skip display part */ - if (*row + callgraph.cursor.voffset < 0) { - *row = *row + 1; - callnode_display(node->child, bitmap, row, depth + 1); - callnode_display(node->sibling, bitmap, row, depth); - return; - } - - - const char *addrname = dictionary_syscalls_check((void*)node->address); - if (addrname == NULL) - addrname = dictionary_notes_check((void*)node->address); - if (addrname == NULL) { - snprintf(line, 256, "%s %p", type, (void*)node->address); - } else { - snprintf(line, 256, "%s %p - %s", - type, (void*)node->address, addrname); - } /* display the line then check child and siblig */ - if (depth < 0) { - dtext((callgraph.cursor.hoffset + (i << 2)) * (FWIDTH + 1), - (*row + callgraph.cursor.voffset) * (FHEIGHT + 1), - C_BLACK, line); - } else { - dprint((2 + callgraph.cursor.hoffset + (i << 2)) * (FWIDTH + 1), - (*row + callgraph.cursor.voffset) * (FHEIGHT + 1), C_BLACK, - "%c-- %s", pipe, line); + if (*row + callgraph.cursor.voffset >= 0) { + callnode_generate_info(line, 256, node); + if (depth < 0) { + int a = callgraph.cursor.hoffset + (i << 2); + int b = callgraph.cursor.voffset + (*row); + dtext(a * (FWIDTH + 1), b * (FHEIGHT + 1), C_BLACK, line); + } else { + int a = callgraph.cursor.hoffset + (i << 2) + 2; + int b = callgraph.cursor.voffset + (*row); + dprint(a * (FWIDTH + 1), b * (FHEIGHT + 1), + C_BLACK, "%c-- %s", pipe, line); + } } *row = *row + 1; callnode_display(node->child, bitmap, row, depth + 1); @@ -246,7 +275,6 @@ static void callgraph_display(struct ucontext *context) } /* callgraph_keyboard(): Handle one key event */ -/* TODO: remove the cursor, handle node selection ! */ static int callgraph_keyboard(struct ucontext *context, int key) { (void)context; @@ -263,6 +291,89 @@ static int callgraph_keyboard(struct ucontext *context, int key) return (0); } +/* callgraph_command(): handle user command */ +static void callgraph_command(struct ucontext *context, int argc, char **argv) +{ + /* check useless export */ + (void)context; + if (callgraph.root == NULL) { + input_write("nothing to export"); + return; + } + + /* check argument validity */ + if (argc != 2) { + input_write("argument missing"); + return; + } + if (strcmp(argv[0], "export") != 0) { + input_write("'%s': command unknown", argv[0]); + return; + } + + /* convert the filename (arg2) into Bfile pathname */ + /* TODO: handle special extention */ + int i = -1; + uint16_t pathname[14 + strlen(argv[1]) + 1]; + memcpy(pathname, u"\\\\fls0\\", 14); + while (argv[1][++i] != '\0') + pathname[7 + i] = argv[1][i]; + pathname[7 + i] = 0x0000; + + /* check if the file exist */ + input_write_noint("Check if the file exist"); + gint_switch_to_casio(); + int fd = BFile_Open(pathname, BFile_ReadOnly); + if (fd >= 0) { + gint_switch_to_gint(); + while (1) { + if (input_read(line, 3, "'%s' exist, erase ? [n/Y]:", + argv[1]) <= 0) { + input_write("export aborded"); + return; + } + if (line[0] == 'n') { + input_write("export aborded"); + return; + } + if (line[0] != 'Y') + continue; + gint_switch_to_casio(); + BFile_Remove(pathname); + break; + } + } + + /* create the file then dump information */ + gint_switch_to_gint(); + int size = callnode_export(-1, callgraph.root); + input_write_noint("Create the file (%d)", size); + gint_switch_to_casio(); + fd = BFile_Create(pathname, BFile_File, &size); + if (fd != 0) { + gint_switch_to_gint(); + input_write("Bfile_Create: error %d", fd); + return; + } + gint_switch_to_gint(); + input_write_noint("Create success"); + gint_switch_to_casio(); + fd = BFile_Open(pathname, BFile_ReadWrite); + if (fd < 0) { + BFile_Remove(pathname); + gint_switch_to_gint(); + input_write("BFile_Open: error %d", fd); + return; + } + gint_switch_to_gint(); + input_write_noint("Open success, now write..."); + gint_switch_to_casio(); + callnode_export(fd, callgraph.root); + BFile_Close(fd); + gint_switch_to_gint(); + input_write("success"); +} + //--- // Define the menu //--- @@ -271,6 +382,6 @@ struct menu menu_callgraph = { .init = &callgraph_init, .display = &callgraph_display, .keyboard = &callgraph_keyboard, - .command = NULL, + .command = &callgraph_command, .dtor = NULL };