Gintracer: v0.5.0 - Add Callgraph menu and stability
@add * callgraph: add menu * callgraph: Generate the callgraph even if a breakpoint has been placed after a subroutine. @fix * disable the UBC when we are in the "user" handler (more stability) * the key [EXE] has been mapped by the GUI and exit the handler when pressed.
This commit is contained in:
parent
bfbb425bc0
commit
ef8e11d116
7
TODO.txt
7
TODO.txt
|
@ -1,10 +1,11 @@
|
|||
@update
|
||||
* common EXE to continue
|
||||
* call uninit() to each menu (disasm = set the next break point) !
|
||||
* fix fixed size space character
|
||||
* fix fixed size 'space' character
|
||||
* disasm circular buffer (code only)
|
||||
|
||||
@feature
|
||||
* night mode
|
||||
* call graph
|
||||
* call graph export for laptop
|
||||
* input box
|
||||
* command management for each menu
|
||||
* night mode
|
||||
|
|
BIN
gintrace.g3a
BIN
gintrace.g3a
Binary file not shown.
|
@ -0,0 +1,39 @@
|
|||
#ifndef __GINTRACE_MENU_CALLGRAPH_H__
|
||||
# define __GINTRACE_MENU_CALLGRAPH_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gintrace/ubc.h"
|
||||
|
||||
struct callnode {
|
||||
struct ucontext context;
|
||||
uintptr_t address;
|
||||
enum {
|
||||
callnode_type_root = 0,
|
||||
callnode_type_bsr = 1,
|
||||
callnode_type_bsrf = 2,
|
||||
callnode_type_jsr = 3,
|
||||
callnode_type_jmp = 4,
|
||||
callnode_type_rte = 5,
|
||||
callnode_type_rts = 6,
|
||||
} type;
|
||||
struct callnode *parent;
|
||||
struct callnode *sibling;
|
||||
struct callnode *child;
|
||||
};
|
||||
|
||||
struct callgraph {
|
||||
struct {
|
||||
int hoffset;
|
||||
int voffset;
|
||||
} cursor;
|
||||
struct callnode *root;
|
||||
struct callnode *parent;
|
||||
int callnode_counter;
|
||||
};
|
||||
|
||||
/* extern menu information */
|
||||
extern struct menu menu_callgraph;
|
||||
|
||||
#endif /*__GINTRACE_MENU_CALLGRAPH_H__*/
|
|
@ -127,13 +127,13 @@ struct sh7305_ubc_context {
|
|||
//---
|
||||
/* ucontext: UBC context */
|
||||
struct ucontext {
|
||||
uint32_t reg[16];
|
||||
uint32_t gbr;
|
||||
uint32_t macl;
|
||||
uint32_t mach;
|
||||
uint32_t ssr;
|
||||
uint32_t spc;
|
||||
uint32_t pr;
|
||||
uintptr_t reg[16];
|
||||
uintptr_t gbr;
|
||||
uintptr_t macl;
|
||||
uintptr_t mach;
|
||||
uintptr_t ssr;
|
||||
uintptr_t spc;
|
||||
uintptr_t pr;
|
||||
};
|
||||
|
||||
//---
|
||||
|
@ -149,6 +149,10 @@ extern int ubc_set_handler(void (*handler)(struct ucontext *ctx));
|
|||
/* ubc_set_breakpoint(): Setup one breakpoint */
|
||||
extern int ubc_set_breakpoint(int channel, void *address, void *mask);
|
||||
|
||||
/* ubc_block(): Block UBC interruption */
|
||||
extern int ubc_block(void);
|
||||
/* ubc_unblock(): Unblock UBC interruption */
|
||||
extern int ubc_unblock(void);
|
||||
|
||||
//---
|
||||
// "low-level" interface
|
||||
|
|
|
@ -142,7 +142,7 @@ int menu_keyboard(struct menu_group *gmenu)
|
|||
if (gmenu == NULL)
|
||||
return (menu_retval_efault);
|
||||
key = getkey().key;
|
||||
if (key == KEY_EXIT) {
|
||||
if (key == KEY_EXE) {
|
||||
gmenu->is_open = 1;
|
||||
return (menu_retval_success);
|
||||
}
|
||||
|
|
62
src/main.c
62
src/main.c
|
@ -2,27 +2,75 @@
|
|||
#include "gintrace/menu/disasm.h"
|
||||
#include "gintrace/menu/context.h"
|
||||
#include "gintrace/menu/hexdump.h"
|
||||
#include "gintrace/menu/callgraph.h"
|
||||
#include "gintrace/ubc.h"
|
||||
|
||||
#include <gint/gint.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/bfile.h>
|
||||
|
||||
/* workaround test */
|
||||
extern struct tracer tracer;
|
||||
|
||||
/* save the selected menu */
|
||||
static struct menu_group *gmenu = NULL;
|
||||
|
||||
/* gintrac_handler(): UBC handler */
|
||||
/* syscall address */
|
||||
static void *syscall = NULL;
|
||||
|
||||
/* gintrac_handler(): UBC handler
|
||||
* @note:
|
||||
* To force generate the callgraph, we use a dirty workaround to force break
|
||||
* at each instruction. But, the disassembler menu can skip one instruction
|
||||
* using OPTN key, so you should not "unskip" the user action. */
|
||||
static void gintrace_handler(struct ucontext *context)
|
||||
{
|
||||
static uintptr_t breakpoint = 0x00000000;
|
||||
static uintptr_t spc = 0x00000000;
|
||||
|
||||
/* force disable the UBC to avoid error */
|
||||
ubc_block();
|
||||
|
||||
/* check callgraph job */
|
||||
if (breakpoint != 0x00000000
|
||||
&& spc != 0x00000000
|
||||
&& spc != breakpoint) {
|
||||
menu_callgraph.init(context);
|
||||
spc = context->spc;
|
||||
ubc_set_breakpoint(0, (void*)context->spc, NULL);
|
||||
ubc_unblock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* user break point */
|
||||
gint_switch_to_gint();
|
||||
menu_init(gmenu, context);
|
||||
while (menu_is_open(gmenu) == 0) {
|
||||
menu_draw(gmenu);
|
||||
menu_keyboard(gmenu);
|
||||
}
|
||||
|
||||
/* if no instruction skip, restore */
|
||||
spc = context->spc;
|
||||
ubc_set_breakpoint(0, (void*)context->spc, NULL);
|
||||
breakpoint = tracer.next_break;
|
||||
|
||||
/* unblock UBC interrupt */
|
||||
ubc_unblock();
|
||||
gint_switch_to_casio();
|
||||
}
|
||||
|
||||
/* casio_handler(): Casio handler */
|
||||
static void casio_handler(void)
|
||||
{
|
||||
void (*bfile_openfile_os)(const uint16_t *filename, int mode, int p3);
|
||||
|
||||
bfile_openfile_os = syscall;
|
||||
bfile_openfile_os(u"\\fls0\\dyntest", BFile_ReadOnly, 0);
|
||||
}
|
||||
|
||||
/* main(): User entry */
|
||||
int main(void)
|
||||
{
|
||||
/* initialize all internal menu and get the "first" menu to display */
|
||||
|
@ -30,16 +78,22 @@ int main(void)
|
|||
menu_register(gmenu, &menu_disasm, "Disasm");
|
||||
menu_register(gmenu, &menu_context, "Context");
|
||||
menu_register(gmenu, &menu_hexdump, "Hexdump");
|
||||
menu_register(gmenu, NULL, "CallG");
|
||||
menu_register(gmenu, &menu_callgraph, "CallG");
|
||||
|
||||
/* get syscall address */
|
||||
void **systab = *(void ***)0x8002007c;
|
||||
syscall = systab[0x1da3];
|
||||
|
||||
|
||||
/* intialize UBC information */
|
||||
ubc_install();
|
||||
ubc_set_handler(&gintrace_handler);
|
||||
//ubc_set_breakpoint(0, (void*)0x80358a6c, NULL);
|
||||
ubc_set_breakpoint(0, (void*)0x80358a7e, NULL);
|
||||
ubc_set_breakpoint(0, syscall, NULL);
|
||||
|
||||
/* try to trace the function */
|
||||
gint_switch((void *)0x80358a6c);
|
||||
//gint_switch((void *)0x80358a6c); // stack menu (syscall %1e66)
|
||||
gint_switch((void*)&casio_handler);
|
||||
|
||||
//TODO : destructor part !!
|
||||
return (0);
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
#include "gintrace/menu/callgraph.h"
|
||||
#include "gintrace/ubc.h"
|
||||
#include "gintrace/gui/menu.h"
|
||||
#include "gintrace/gui/display.h"
|
||||
|
||||
#include <gint/std/stdio.h>
|
||||
#include <gint/std/stdlib.h>
|
||||
#include <gint/std/string.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
#include "./src/menu/disassembler/dictionary.h"
|
||||
|
||||
/* define the menu information */
|
||||
/* TODO: find a way to have local information (session) */
|
||||
struct callgraph callgraph;
|
||||
|
||||
//---
|
||||
// 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 callnode *node;
|
||||
|
||||
node = calloc(sizeof(struct callnode), 1);
|
||||
if (node != NULL) {
|
||||
memcpy(&node->context, context, sizeof(struct ucontext));
|
||||
node->type = type;
|
||||
node->parent = parent;
|
||||
node->address = address;
|
||||
callgraph.callnode_counter += 1;
|
||||
}
|
||||
return (node);
|
||||
}
|
||||
|
||||
/* callnode_add_child(): Add child into the child queue */
|
||||
static void callnode_add_child(struct callnode *parent, struct callnode *child)
|
||||
{
|
||||
struct callnode **sibling;
|
||||
|
||||
sibling = &parent->child;
|
||||
while (*sibling != NULL)
|
||||
sibling = &(*sibling)->sibling;
|
||||
*sibling = child;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
int idx;
|
||||
int i;
|
||||
|
||||
if (node == NULL || *row + callgraph.cursor.voffset >= GUI_DISP_NB_ROW)
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
/* handle the bitmap (ugly / 20) */
|
||||
i = -1;
|
||||
idx = 0;
|
||||
while (++i < depth) {
|
||||
idx = i >> 6;
|
||||
if (idx >= 4)
|
||||
break;
|
||||
shift = i & 0x1f;
|
||||
if ((bitmap[idx] & (1 << shift)) != 0) {
|
||||
dtext((2 + callgraph.cursor.hoffset + (i << 2))
|
||||
* (FWIDTH + 1), (*row
|
||||
+ callgraph.cursor.voffset)
|
||||
* (FHEIGHT + 1), C_BLACK, "| ");
|
||||
}
|
||||
}
|
||||
|
||||
/* generate the line */
|
||||
char pipe = '|';
|
||||
const char *type = "(err)";
|
||||
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) {
|
||||
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 = '`';
|
||||
}
|
||||
const char *addrname =
|
||||
disasm_dictionary_check_syscalls((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);
|
||||
}
|
||||
*row = *row + 1;
|
||||
callnode_display(node->child, bitmap, row, depth + 1);
|
||||
callnode_display(node->sibling, bitmap, row, depth);
|
||||
}
|
||||
|
||||
//---
|
||||
// Menu
|
||||
//---
|
||||
|
||||
/* callgraph_ctor: Menu constructor */
|
||||
static void callgraph_ctor(void)
|
||||
{
|
||||
callgraph.cursor.hoffset = 0;
|
||||
callgraph.cursor.voffset = 0;
|
||||
callgraph.root = NULL;
|
||||
}
|
||||
|
||||
/* callgraph_init(): Invoked each time a break point occur */
|
||||
/* FIXME: recursive !! */
|
||||
static void callgraph_init(struct ucontext *context)
|
||||
{
|
||||
struct callnode *node;
|
||||
uintptr_t address;
|
||||
uint16_t *pc;
|
||||
uint32_t b;
|
||||
int type;
|
||||
|
||||
/* check root node */
|
||||
if (callgraph.root == NULL) {
|
||||
node = callnode_create(NULL, context,
|
||||
callnode_type_root, context->spc);
|
||||
if (node == NULL)
|
||||
return;
|
||||
callgraph.root = node;
|
||||
callgraph.parent = callgraph.root;
|
||||
}
|
||||
|
||||
/* check error */
|
||||
if (callgraph.parent == NULL)
|
||||
return;
|
||||
|
||||
/* check opcode */
|
||||
type = -1;
|
||||
pc = (void*)(uintptr_t)context->spc;
|
||||
if ((pc[0] & 0xf000) == 0xb000) {
|
||||
type = callnode_type_bsr;
|
||||
b = pc[0] & 0x0fff;
|
||||
if ((b & 0x800) != 0)
|
||||
b = (0xfffff000 | b);
|
||||
address = context->spc + 4 + (b << 1);
|
||||
}
|
||||
if ((pc[0] & 0xf0ff) == 0x0003) {
|
||||
type = callnode_type_bsrf;
|
||||
b = (pc[0] & 0x0f00) >> 8;
|
||||
address = context->reg[b];
|
||||
}
|
||||
if ((pc[0] & 0xf0ff) == 0x400b) {
|
||||
type = callnode_type_jsr;
|
||||
b = (pc[0] & 0x0f00) >> 8;
|
||||
address = context->reg[b];
|
||||
}
|
||||
if ((pc[0] & 0xf0ff) == 0x402b) {
|
||||
type = callnode_type_jmp;
|
||||
b = (pc[0] & 0x0f00) >> 8;
|
||||
address = context->reg[b];
|
||||
}
|
||||
if ((pc[0] & 0xffff) == 0x002b) {
|
||||
type = callnode_type_rte;
|
||||
address = context->spc;
|
||||
}
|
||||
if ((pc[0] & 0xffff) == 0x000b) {
|
||||
type = callnode_type_rts;
|
||||
address = context->spc;
|
||||
}
|
||||
if (type == -1)
|
||||
return;
|
||||
|
||||
/* generate the node then link with its sibling */
|
||||
node = callnode_create(callgraph.parent, context, type, address);
|
||||
if (node == NULL)
|
||||
return;
|
||||
callnode_add_child(callgraph.parent, node);
|
||||
|
||||
/* find the next "current" node */
|
||||
if (node->type == callnode_type_bsr
|
||||
|| node->type == callnode_type_jsr
|
||||
|| node->type == callnode_type_bsrf
|
||||
|| node->type == callnode_type_jmp) {
|
||||
callgraph.parent = node;
|
||||
return;
|
||||
}
|
||||
while (callgraph.parent != NULL) {
|
||||
if (callgraph.parent->type == callnode_type_jmp) {
|
||||
callgraph.parent = callgraph.parent->parent;
|
||||
continue;
|
||||
}
|
||||
callgraph.parent = callgraph.parent->parent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* callgraph_display(); Display trace information */
|
||||
static void callgraph_display(struct ucontext *context)
|
||||
{
|
||||
uint32_t bitmap[4];
|
||||
int row;
|
||||
|
||||
(void)context;
|
||||
row = 0;
|
||||
memset(bitmap, 0x00, sizeof(bitmap));
|
||||
callnode_display(callgraph.root, bitmap, &row, -1);
|
||||
}
|
||||
|
||||
/* callgraph_keyboard(): Handle one key event */
|
||||
/* TODO: remove the cursor, handle node selection ! */
|
||||
static int callgraph_keyboard(struct ucontext *context, int key)
|
||||
{
|
||||
(void)context;
|
||||
if (key == KEY_LEFT)
|
||||
callgraph.cursor.hoffset += 1;
|
||||
if (key == KEY_RIGHT)
|
||||
callgraph.cursor.hoffset -= 1;
|
||||
|
||||
if (key == KEY_UP)
|
||||
callgraph.cursor.voffset += 1;
|
||||
if (key == KEY_DOWN)
|
||||
callgraph.cursor.voffset -= 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
//---
|
||||
// Define the menu
|
||||
//---
|
||||
struct menu menu_callgraph = {
|
||||
.ctor = &callgraph_ctor,
|
||||
.init = &callgraph_init,
|
||||
.display = &callgraph_display,
|
||||
.keyboard = &callgraph_keyboard,
|
||||
.command = NULL,
|
||||
.dtor = NULL
|
||||
};
|
|
@ -259,7 +259,7 @@ int disasm_display_addr_info(int *row, struct buffcursor *cursor,
|
|||
/* check note */
|
||||
if (cursor->note_idx != 0) {
|
||||
drect(0, (*row) * (FHEIGHT + 1) - 1, DWIDTH,
|
||||
((*row) * (FHEIGHT + 1) - 1) + (FHEIGHT + 2),
|
||||
((*row) * (FHEIGHT + 1) - 1) + (FHEIGHT + 1),
|
||||
0x556655);
|
||||
dtext(tracer.disp.hoffset * (FWIDTH + 1),
|
||||
(*row) * (FHEIGHT + 1), C_WHITE, ptr);
|
||||
|
@ -409,6 +409,7 @@ static int disasm_keyboard(struct ucontext *context, int key)
|
|||
int line_idx;
|
||||
|
||||
/* horizontal update */
|
||||
(void)context;
|
||||
if (key == KEY_LEFT)
|
||||
tracer.disp.hoffset += 1;
|
||||
if (key == KEY_RIGHT)
|
||||
|
@ -452,14 +453,9 @@ static int disasm_keyboard(struct ucontext *context, int key)
|
|||
/* skip instruction */
|
||||
if (key == KEY_OPTN) {
|
||||
context->spc = tracer.next_break;
|
||||
ubc_set_breakpoint(0, (void*)tracer.next_break, NULL);
|
||||
return (1);
|
||||
}
|
||||
/* go to net break point */
|
||||
if (key == KEY_EXE) {
|
||||
ubc_set_breakpoint(0, (void*)tracer.next_break, NULL);
|
||||
return (1);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,3 +94,37 @@ int ubc_set_handler(void (*handler)(struct ucontext *ctxt))
|
|||
ubc_handler = handler;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t __ubc_block_counter = 0;
|
||||
static uint32_t __ubc_cbcr_ubde = 0;
|
||||
static uint32_t __ubc_cbr0_ce = 0;
|
||||
|
||||
/* ubc_block(): Block UBC interruption */
|
||||
int ubc_block(void)
|
||||
{
|
||||
if (__ubc_block_counter == 0) {
|
||||
__ubc_cbr0_ce = SH7305_UBC.CBR0.CE;
|
||||
__ubc_cbcr_ubde = SH7305_UBC.CBCR.UBDE;
|
||||
SH7305_UBC.CBCR.UBDE = 0;
|
||||
SH7305_UBC.CBR0.CE = 0;
|
||||
ubc_kernel_update();
|
||||
}
|
||||
__ubc_block_counter += 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/* ubc_block(): Block UBC interruption */
|
||||
int ubc_unblock(void)
|
||||
{
|
||||
if (__ubc_block_counter <= 0)
|
||||
return (-1);
|
||||
__ubc_block_counter -= 1;
|
||||
if (__ubc_block_counter == 0) {
|
||||
SH7305_UBC.CBCR.UBDE = __ubc_cbcr_ubde;
|
||||
SH7305_UBC.CBR0.CE = __ubc_cbr0_ce;
|
||||
ubc_kernel_update();
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue