Skip to content

Commit

Permalink
Merge pull request #1843 from ghaerr/debug
Browse files Browse the repository at this point in the history
[debug] Rewrite stack tracing code, add disasm to distribution
  • Loading branch information
ghaerr authored Apr 4, 2024
2 parents 894d201 + ebac188 commit 96ed606
Show file tree
Hide file tree
Showing 15 changed files with 206 additions and 119 deletions.
6 changes: 0 additions & 6 deletions elkscmd/.gitignore

This file was deleted.

3 changes: 3 additions & 0 deletions elkscmd/Applications
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,6 @@ nano-X/bin/nxworld.map ::lib/nxworld.map :nanox
basic/basic :basic :1200k
advent/advent :other
advent/advent.db ::lib/advent.db :other
debug/disasm :other
debug/system.sym ::lib/system.sym :other
debug/testsym :other
1 change: 1 addition & 0 deletions elkscmd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ SUBDIRS = \
basic \
bc \
busyelks \
debug \
disk_utils \
fsck_dos \
elvis \
Expand Down
16 changes: 8 additions & 8 deletions elkscmd/debug/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ include $(BASEDIR)/Make.rules
###############################################################################

PRGS = testsym disasm opcodes
INSTRUMENTOBJS = instrument.o syms.o shared.o shared-asm.o
INSTRUMENTOBJS = instrument.o readprologue.o syms.o
#INSTRUMENTOBJS += rdtsc.o
DISASMOBJS = dis.o disasm.o syms.o
TESTOBJS = testsym.o stacktrace.o printreg.o $(INSTRUMENTOBJS)

Expand All @@ -29,22 +30,21 @@ NOINSTFLAGS = -fno-instrument-functions -fno-instrument-functions-simple

HOSTPRGS = nm86 hostdisasm

all: $(HOSTPRGS) $(PRGS)
all: $(HOSTPRGS) $(PRGS) system.sym

testsym: $(TESTOBJS)
$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
./nm86 $@ > $@.map
cp $@ $(TOPDIR)/elkscmd/rootfs_template/root

disasm: $(DISASMOBJS)
$(LD) $(LDFLAGS) -maout-heap=0xffff -o $@ $^ $(LDLIBS)
cp -p $@ $(TOPDIR)/elkscmd/rootfs_template/root
-mkdir $(TOPDIR)/elkscmd/rootfs_template/lib
cp -p $(TOPDIR)/elks/arch/i86/boot/system.sym $(TOPDIR)/elkscmd/rootfs_template/lib

system.sym:
cp -p $(TOPDIR)/elks/arch/i86/boot/system.sym $(TOPDIR)/elkscmd/debug

opcodes: opcodes.o
$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
cp $@ $(TOPDIR)/elkscmd/rootfs_template/root
# cp $@ $(TOPDIR)/elkscmd/rootfs_template/root

disasm.o: disasm.c
$(CC) $(CFLAGS) $(NOINSTFLAGS) -c -o $*.o $<
Expand All @@ -62,4 +62,4 @@ install: $(PRGS)
$(INSTALL) $(PRGS) $(DESTDIR)/bin

clean:
rm -f $(PRGS) $(HOSTPRGS) *.o *.map
rm -f $(PRGS) $(HOSTPRGS) *.o *.map system.sym
10 changes: 5 additions & 5 deletions elkscmd/debug/dis.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ char * noinstrument getsymbol(int seg, int offset)

if (f_ksyms) {
if (seg == textseg)
return sym_text_symbol((void *)offset, 1);
return sym_text_symbol((void *)offset, -1);
if (seg == ftextseg)
return sym_ftext_symbol((void *)offset, 1);
return sym_ftext_symbol((void *)offset, -1);
if (seg == dataseg)
return sym_data_symbol((void *)offset, 1);
return sym_data_symbol((void *)offset, -1);
}
if (f_syms) {
if (seg == dataseg)
return sym_data_symbol((void *)offset, 1);
return sym_text_symbol((void *)offset, 1);
return sym_data_symbol((void *)offset, -1);
return sym_text_symbol((void *)offset, -1);
}
sprintf(buf, f_asmout? "0x%04x": "%04x", offset);
return buf;
Expand Down
31 changes: 16 additions & 15 deletions elkscmd/debug/instrument.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

static char ftrace;
static int count;
static unsigned int start_sp;
static unsigned int max_stack;
static size_t start_sp;
static size_t max_stack;

/* runs before main and rewrites argc/argv on stack if --ftrace found */
__attribute__((no_instrument_function,constructor(120)))
Expand All @@ -32,7 +32,9 @@ static void ftrace_checkargs(void)
ftrace = 1;
__argc--;
}
#if HAS_RDTSC
_get_micro_count(); /* init timer base */
#endif
}

/* every function this function calls must also be noinstrument!! */
Expand All @@ -49,24 +51,25 @@ void noinstrument __cyg_profile_func_enter_simple(void)
assert(bp == __builtin_frame_address(0));
assert(calling_fn == __builtin_return_address(0));
#endif
int i;
int i, offset;
char callsite[32];

/* calc stack used */
if (count == 0) start_sp = (unsigned int)bp;
unsigned int stack_used = start_sp - (unsigned int)bp;
if (count == 0) start_sp = (size_t)bp;
size_t stack_used = start_sp - (size_t)bp;
if (stack_used > max_stack) max_stack = stack_used;

/* calc caller address */
i = _get_push_count(calling_fn);
i = _get_push_count(_get_fn_start_address(calling_fn));
if (i & BP_PUSHED) { /* caller pushed BP */
bp = (int **)bp[0]; /* one level down to get caller BP */
bp = (int **)bp[0]; /* one level up to get caller BP */
i &= COUNT_MASK;
} else bp += 4; /* caller didn't push BP, skip past our ret addr */
if (i >= 0)
strcpy(callsite, sym_text_symbol(bp[i], 1)); /* return address of caller */
else
/* return address of caller */
offset = (size_t)bp[i] - (size_t)_get_fn_start_address(bp[i]);
strcpy(callsite, sym_text_symbol(bp[i], offset));
} else { /* caller didn't push BP */
strcpy(callsite, "<unknown>");
}
for (i=0; i<count; i++)
putchar('|');
printf(">%s, from %s, stack %u/%u", sym_text_symbol(calling_fn, 0), callsite,
Expand All @@ -84,20 +87,18 @@ void noinstrument __cyg_profile_func_exit_simple(void)
--count;
}

#if HAS_RDTSC
/* return CPU cycles / 1000 via RDTSC instruction */
unsigned long noinstrument _get_micro_count(void)
{
#if HAS_RDTSC
static unsigned long long last_ts;

unsigned long long ts = _get_rdtsc(); /* REQUIRES 386 CPU! */
unsigned long diff = (ts - last_ts) / 1000;
last_ts = ts;
return diff;
#else
return 0;
#endif
}
#endif

/***static char * noinstrument lltohexstr(unsigned long long val)
{
Expand Down
8 changes: 4 additions & 4 deletions elkscmd/debug/instrument.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ void noinstrument _print_stack(int arg1);
void noinstrument _print_regs(void);
void noinstrument _print_segs(void);

/* shared.c */
int noinstrument _get_push_count(int *addr);
/* readprologue.c */
int * noinstrument _get_fn_start_address(int *addr);
int noinstrument _get_push_count(int *fnstart);

/* shared-asm.S */
int noinstrument _get_csbyte(char *addr);
/* rdtsc.S */
unsigned long long noinstrument _get_rdtsc(void);
50 changes: 50 additions & 0 deletions elkscmd/debug/prologues
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Possible ia16-elf-gcc function prologues
----------------------------------------

SI only
193: 56 push %si

SI & DI only
0: 56 push %si
1: 57 push %di

BASIC
0: 55 push %bp
1: 89 e5 mov %sp,%bp

BASIC w/SI
3d: 56 push %si
3e: 55 push %bp
3f: 89 e5 mov %sp,%bp

BASIC w/SI & DI
13e: 56 push %si
13f: 57 push %di
140: 55 push %bp
141: 89 e5 mov %sp,%bp

BASIC w/SI & DI and small locals
1c4: 56 push %si
1c5: 57 push %di
1c6: 55 push %bp
1c7: 89 e5 mov %sp,%bp
1c9: 83 ec 24 sub $0x24,%sp

BASIC w/SI & DI and large locals
6d: 56 push %si
6e: 57 push %di
6f: 55 push %bp
70: 89 e5 mov %sp,%bp
72: 81 ec 84 00 sub $0x84,%sp

Suggested algorithm:
Search for 55 89 e5 in reverse order at IP-3 (BASIC), IP-6 (BASIC w/small locals)
and IP-6 (BASIC w/large locals).
If found, BP_PUSHED, IP=matched IP;
Then search for 57 DI_PUSHED, --IP;
then search for 56 SI_PUSHED, --IP;
Last location (IP) is then function start.

Use -finstrument-functions-simple. # function profiling
Possibly use -fno-omit-frame-pointer. # alwaus push BP in prologue
Possibly use -fno-optimize-sibling-calls. # disable tail call optimization
20 changes: 0 additions & 20 deletions elkscmd/debug/shared-asm.S → elkscmd/debug/rdtsc.S
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,6 @@
.code16
.text

.global _get_csbyte
// int _get_csbyte(char *addr)
// return byte at CS:addr
_get_csbyte:
push %bp
mov %sp,%bp
mov 4(%bp),%bx
mov %cs:(%bx),%al
xor %ah,%ah
pop %bp
ret

.global _get_cs
// int _get_cs(void)
// return CS
_get_cs:
push %cs
pop %ax
ret

.global _get_rdtsc
// unsigned long long _get_rdtsc(void)
// 386+ only, reads CPU time stamp counter
Expand Down
75 changes: 75 additions & 0 deletions elkscmd/debug/readprologue.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* ELKS shared instrumentation functions for ia16-elf-gcc
*
* June 2022 Greg Haerr
* Mar 2024 Added backwards scan w/o symbol table using _get_fn_start_address
*/
#include <stdio.h>
#include "instrument.h"
#include "syms.h"

#define _get_csbyte(ip) __extension__ ({ \
unsigned char _v; \
asm volatile ("mov %%cs:(%%bx),%%al" \
:"=Ral" (_v) \
:"b" (ip)); \
_v; })

/*
* Calculate function start address containing addr.
* Proper operation requires compilation with -fno-omit-frame-pointer
* and -fno-optimize-sibling-calls.
*/
int * noinstrument _get_fn_start_address(int *addr)
{
char *ip = (char *)addr;
int i = 0;

/* adjust forward if addr is function start (hack) */
if (_get_csbyte(ip) == 0x56) { /* push %si */
i = -1;
if (_get_csbyte(ip+1) == 0x57) /* push %di */
i = -2;
}

/* look backwards for prologue: push %bp/mov %sp,%bp (55 89 e5) */
for(;;) {
/* main called at 0x37 from crt0.S which has no prologue at _start */
if ((unsigned int)ip-i <= 0x37)
return 0; /* _start address or start address not found */

if (_get_csbyte(ip-i+0) == 0x55 && /* push %bp */
_get_csbyte(ip-i+1) == 0x89 && /* mov %sp,%bp */
_get_csbyte(ip-i+2) == 0xe5) {
ip = ip - i;
/* prologue possibly prededed by optional push %si/%di */
if ((i = _get_csbyte(ip-1)) == 0x57) /* push %di */
return (int *)(ip-2); /* start is push %si,push %di */
if (i == 0x56) /* push %si */
return (int *)(ip-1); /* start is push %si */
return (int *)ip; /* start is push %bp */
}
i++;
}
}

/*
* Return pushed word count and register bitmask by function at passed address,
* used to traverse BP chain and display registers.
*/
int noinstrument _get_push_count(int *fnstart)
{
char *fp = (char *)fnstart;
int count = 0;

int opcode = _get_csbyte(fp++);
if (opcode == 0x56) /* push %si */
count = (count+1) | SI_PUSHED, opcode = _get_csbyte(fp++);
if (opcode == 0x57) /* push %di */
count = (count+1) | DI_PUSHED, opcode = _get_csbyte(fp++);
if (opcode == 0x55 || /* push %bp */
(opcode == 0x59 && (unsigned int)fp < 0x40)) /* hack for crt0.S 'pop %cx' start */
count = (count + 1) | BP_PUSHED, opcode = _get_csbyte(fp);
//printf("%s (%x) pushes %x\n", sym_text_symbol(addr, 1), (int)addr, count);
return count;
}
30 changes: 0 additions & 30 deletions elkscmd/debug/shared.c

This file was deleted.

Loading

0 comments on commit 96ed606

Please sign in to comment.