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:
Yatis 2021-02-26 19:19:37 +01:00
parent bfbb425bc0
commit ef8e11d116
9 changed files with 421 additions and 22 deletions

View File

@ -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

Binary file not shown.

View File

@ -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__*/

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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