pa2.2: add ftrace
This commit is contained in:
parent
0f7c6fd508
commit
9229e4318e
8 changed files with 190 additions and 5 deletions
|
@ -65,6 +65,7 @@
|
||||||
devShells.nemu = pkgs.mkShell {
|
devShells.nemu = pkgs.mkShell {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
clang-tools
|
clang-tools
|
||||||
|
gdb
|
||||||
];
|
];
|
||||||
inputsFrom = [
|
inputsFrom = [
|
||||||
self.packages.${system}.nemu
|
self.packages.${system}.nemu
|
||||||
|
|
0
nemu/.result.tmp
Normal file
0
nemu/.result.tmp
Normal file
24
nemu/Kconfig
24
nemu/Kconfig
|
@ -143,8 +143,11 @@ config TRACE_END
|
||||||
|
|
||||||
config ITRACE
|
config ITRACE
|
||||||
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
|
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
|
||||||
bool "Enable instruction tracer"
|
bool "Enable instruction tracing"
|
||||||
default y
|
default y
|
||||||
|
help
|
||||||
|
Instraction tracing will log past instructions into a ring buffer
|
||||||
|
and print them when NEMU exit unexpectedly.
|
||||||
|
|
||||||
config ITRACE_COND
|
config ITRACE_COND
|
||||||
depends on ITRACE
|
depends on ITRACE
|
||||||
|
@ -158,8 +161,8 @@ config ITRACE_BUFFER
|
||||||
|
|
||||||
config MTRACE
|
config MTRACE
|
||||||
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
|
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
|
||||||
bool "Enable memory tracer"
|
bool "Enable memory tracing"
|
||||||
|
default n
|
||||||
|
|
||||||
config MTRACE_RANGE
|
config MTRACE_RANGE
|
||||||
depends on MTRACE
|
depends on MTRACE
|
||||||
|
@ -174,6 +177,21 @@ config MTRACE_RANGE_MAX
|
||||||
int "Max range count in MTRACE_RANGE"
|
int "Max range count in MTRACE_RANGE"
|
||||||
default 10
|
default 10
|
||||||
|
|
||||||
|
config FTRACE
|
||||||
|
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
|
||||||
|
bool "Enable function tracing"
|
||||||
|
default y
|
||||||
|
|
||||||
|
config FTRACE_STACK_SIZE
|
||||||
|
depends on FTRACE
|
||||||
|
int "Max function track stack size"
|
||||||
|
default 100
|
||||||
|
|
||||||
|
config FTRACE_LOG
|
||||||
|
depends on FTRACE
|
||||||
|
bool "Print log when entering a funciton"
|
||||||
|
default n
|
||||||
|
|
||||||
config DIFFTEST
|
config DIFFTEST
|
||||||
depends on TARGET_NATIVE_ELF
|
depends on TARGET_NATIVE_ELF
|
||||||
bool "Enable differential testing"
|
bool "Enable differential testing"
|
||||||
|
|
18
nemu/include/ftrace.h
Normal file
18
nemu/include/ftrace.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef __FUNC_DEF_H__
|
||||||
|
#define __FUNC_DEF_H__
|
||||||
|
#include <common.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_FTRACE
|
||||||
|
typedef struct {
|
||||||
|
vaddr_t start;
|
||||||
|
vaddr_t len;
|
||||||
|
char * name;
|
||||||
|
} func_t;
|
||||||
|
|
||||||
|
extern func_t *func_table;
|
||||||
|
void ftrace_call(vaddr_t, vaddr_t);
|
||||||
|
void ftrace_return(vaddr_t, vaddr_t);
|
||||||
|
// const char *get_func_name(vaddr_t addr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -92,6 +92,8 @@
|
||||||
|
|
||||||
#define PG_ALIGN __attribute((aligned(4096)))
|
#define PG_ALIGN __attribute((aligned(4096)))
|
||||||
|
|
||||||
|
#define FAILED_GOTO(tag, exp) do {if((exp)) goto tag;} while(0)
|
||||||
|
|
||||||
#if !defined(likely)
|
#if !defined(likely)
|
||||||
#define likely(cond) __builtin_expect(cond, 1)
|
#define likely(cond) __builtin_expect(cond, 1)
|
||||||
#define unlikely(cond) __builtin_expect(cond, 0)
|
#define unlikely(cond) __builtin_expect(cond, 0)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <cpu/cpu.h>
|
#include <cpu/cpu.h>
|
||||||
#include <cpu/ifetch.h>
|
#include <cpu/ifetch.h>
|
||||||
#include <cpu/decode.h>
|
#include <cpu/decode.h>
|
||||||
|
#include <ftrace.h>
|
||||||
|
|
||||||
#define R(i) gpr(i)
|
#define R(i) gpr(i)
|
||||||
#define Mr vaddr_read
|
#define Mr vaddr_read
|
||||||
|
@ -59,6 +60,16 @@ static void do_branch(Decode *s, bool condition, word_t offset) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ftrace(Decode *s, int rd, vaddr_t dst) {
|
||||||
|
uint32_t i = s->isa.inst.val;
|
||||||
|
int rs1 = BITS(i, 19, 15);
|
||||||
|
if(rs1 == 1 && rd == 0) {
|
||||||
|
ftrace_return(s->pc, dst);
|
||||||
|
} else {
|
||||||
|
ftrace_call(s->pc, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int decode_exec(Decode *s) {
|
static int decode_exec(Decode *s) {
|
||||||
int rd = 0;
|
int rd = 0;
|
||||||
word_t src1 = 0, src2 = 0, imm = 0;
|
word_t src1 = 0, src2 = 0, imm = 0;
|
||||||
|
@ -74,8 +85,8 @@ static int decode_exec(Decode *s) {
|
||||||
INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm);
|
INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm);
|
||||||
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm);
|
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm);
|
||||||
|
|
||||||
INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do {s->dnpc = s->pc + imm; R(rd) = s->pc + 4; } while(0));
|
INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do {s->dnpc = s->pc + imm; R(rd) = s->pc + 4; ftrace_call(s->pc, s->pc + imm); } while(0));
|
||||||
INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do {s->dnpc = src1 + imm; R(rd) = s->pc + 4; } while(0));
|
INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do {s->dnpc = src1 + imm; R(rd) = s->pc + 4; ftrace(s, rd, src1 + imm); } while(0));
|
||||||
INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm));
|
INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm));
|
||||||
INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, do_branch(s, src1 != src2, imm));
|
INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, do_branch(s, src1 != src2, imm));
|
||||||
INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm));
|
INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm));
|
||||||
|
|
|
@ -40,6 +40,7 @@ static void welcome() {
|
||||||
void sdb_set_batch_mode();
|
void sdb_set_batch_mode();
|
||||||
|
|
||||||
static char *log_file = NULL;
|
static char *log_file = NULL;
|
||||||
|
static char *elf_file = NULL;
|
||||||
static char *diff_so_file = NULL;
|
static char *diff_so_file = NULL;
|
||||||
static char *img_file = NULL;
|
static char *img_file = NULL;
|
||||||
static int difftest_port = 1234;
|
static int difftest_port = 1234;
|
||||||
|
@ -72,6 +73,7 @@ static int parse_args(int argc, char *argv[]) {
|
||||||
{"log" , required_argument, NULL, 'l'},
|
{"log" , required_argument, NULL, 'l'},
|
||||||
{"diff" , required_argument, NULL, 'd'},
|
{"diff" , required_argument, NULL, 'd'},
|
||||||
{"port" , required_argument, NULL, 'p'},
|
{"port" , required_argument, NULL, 'p'},
|
||||||
|
{"elf" , required_argument, NULL, 'f'},
|
||||||
{"help" , no_argument , NULL, 'h'},
|
{"help" , no_argument , NULL, 'h'},
|
||||||
{0 , 0 , NULL, 0 },
|
{0 , 0 , NULL, 0 },
|
||||||
};
|
};
|
||||||
|
@ -82,6 +84,7 @@ static int parse_args(int argc, char *argv[]) {
|
||||||
case 'p': sscanf(optarg, "%d", &difftest_port); break;
|
case 'p': sscanf(optarg, "%d", &difftest_port); break;
|
||||||
case 'l': log_file = optarg; break;
|
case 'l': log_file = optarg; break;
|
||||||
case 'd': diff_so_file = optarg; break;
|
case 'd': diff_so_file = optarg; break;
|
||||||
|
case 'f': elf_file = optarg; break;
|
||||||
case 1: img_file = optarg; return 0;
|
case 1: img_file = optarg; return 0;
|
||||||
default:
|
default:
|
||||||
printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]);
|
printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]);
|
||||||
|
@ -89,6 +92,7 @@ static int parse_args(int argc, char *argv[]) {
|
||||||
printf("\t-l,--log=FILE output log to FILE\n");
|
printf("\t-l,--log=FILE output log to FILE\n");
|
||||||
printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n");
|
printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n");
|
||||||
printf("\t-p,--port=PORT run DiffTest with port PORT\n");
|
printf("\t-p,--port=PORT run DiffTest with port PORT\n");
|
||||||
|
printf("\t-f,--elf=FILE elf file with debug info\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
@ -126,6 +130,12 @@ void init_monitor(int argc, char *argv[]) {
|
||||||
/* Initialize the simple debugger. */
|
/* Initialize the simple debugger. */
|
||||||
init_sdb();
|
init_sdb();
|
||||||
|
|
||||||
|
// printf("elf_file: %s\n", elf_file);
|
||||||
|
if(elf_file != NULL) {
|
||||||
|
void init_elf(const char *path);
|
||||||
|
init_elf(elf_file);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_ISA_loongarch32r
|
#ifndef CONFIG_ISA_loongarch32r
|
||||||
IFDEF(CONFIG_ITRACE, init_disasm(
|
IFDEF(CONFIG_ITRACE, init_disasm(
|
||||||
MUXDEF(CONFIG_ISA_x86, "i686",
|
MUXDEF(CONFIG_ISA_x86, "i686",
|
||||||
|
|
125
nemu/src/utils/ftrace.c
Normal file
125
nemu/src/utils/ftrace.c
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
#include "debug.h"
|
||||||
|
#include "macro.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <common.h>
|
||||||
|
#include <elf.h>
|
||||||
|
#include <ftrace.h>
|
||||||
|
|
||||||
|
// Put this into another file
|
||||||
|
#ifdef CONFIG_FTRACE
|
||||||
|
static vaddr_t ftrace_stack[CONFIG_FTRACE_STACK_SIZE] = {0};
|
||||||
|
static vaddr_t ftrace_stack_len = 0;
|
||||||
|
func_t *func_table = NULL;
|
||||||
|
int func_table_len = 0, func_table_size = 8;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int cmp_func_t(const void *a, const void *b) {
|
||||||
|
return ((func_t *)a)->start > ((func_t *)b)->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static func_t *get_func(vaddr_t addr) {
|
||||||
|
int l = 0, r = func_table_len - 1;
|
||||||
|
while(l <= r) {
|
||||||
|
int mid = (l + r) / 2;
|
||||||
|
if(func_table[mid].start <= addr) l = mid + 1;
|
||||||
|
else r = mid - 1;
|
||||||
|
}
|
||||||
|
return l == 0 ? NULL : &func_table[l - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_elf(const char *path) {
|
||||||
|
FILE *elf_file = fopen(path, "rb");
|
||||||
|
Elf32_Ehdr header;
|
||||||
|
Elf32_Shdr section_header[200], *psh;
|
||||||
|
|
||||||
|
func_table = (func_t *)calloc(func_table_size, sizeof(func_t));
|
||||||
|
assert(func_table);
|
||||||
|
|
||||||
|
FAILED_GOTO(failed_header, fread(&header, sizeof(Elf32_Ehdr), 1, elf_file) <= 0);
|
||||||
|
FAILED_GOTO(failed_header, fseek(elf_file, header.e_shoff, SEEK_SET) != 0);
|
||||||
|
FAILED_GOTO(failed_header, fread(section_header, header.e_shentsize, header.e_shnum, elf_file) <= 0);
|
||||||
|
|
||||||
|
char *shstrtab = calloc(1, section_header[header.e_shstrndx].sh_size);
|
||||||
|
FAILED_GOTO(failed_shstrtab, fseek(elf_file, section_header[header.e_shstrndx].sh_offset, SEEK_SET) != 0);
|
||||||
|
FAILED_GOTO(failed_shstrtab, fread(shstrtab, section_header[header.e_shstrndx].sh_size, 1, elf_file) <= 0);
|
||||||
|
|
||||||
|
Elf32_Shdr *symtab = NULL, *strtab = NULL;
|
||||||
|
for(int i = 0; i < header.e_shnum; i++) {
|
||||||
|
psh = section_header + i;
|
||||||
|
if (psh->sh_type == SHT_SYMTAB) {
|
||||||
|
symtab = psh;
|
||||||
|
} else if (psh->sh_type == SHT_STRTAB && strncmp(shstrtab + psh->sh_name, ".strtab", 8) == 0) {
|
||||||
|
strtab = psh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sym_length = symtab->sh_size / sizeof(Elf32_Sym);
|
||||||
|
Elf32_Sym *sym = calloc(sym_length, sizeof(Elf32_Sym));
|
||||||
|
assert(sym);
|
||||||
|
FAILED_GOTO(failed_funcname, fseek(elf_file, symtab->sh_offset, SEEK_SET) != 0);
|
||||||
|
FAILED_GOTO(failed_funcname, fread(sym, sizeof(Elf32_Sym), sym_length, elf_file) <= 0);
|
||||||
|
|
||||||
|
for(int j = 0; j < sym_length; j++) {
|
||||||
|
if(ELF32_ST_TYPE(sym[j].st_info) != STT_FUNC) continue;
|
||||||
|
// Only read function type symbol
|
||||||
|
func_t *f = &func_table[func_table_len];
|
||||||
|
char *func = (char *)malloc(30);
|
||||||
|
FAILED_GOTO(failed_funcname, fseek(elf_file, strtab->sh_offset + sym[j].st_name, SEEK_SET) != 0);
|
||||||
|
FAILED_GOTO(failed_funcname, fgets(func, 30, elf_file) <= 0);
|
||||||
|
f->start = sym[j].st_value;
|
||||||
|
f->len = sym[j].st_size;
|
||||||
|
f->name = func;
|
||||||
|
++func_table_len;
|
||||||
|
if(func_table_len >= func_table_size) {
|
||||||
|
Assert(func_table_size * 2 > func_table_size, "Function table exceed memory limit");
|
||||||
|
func_table_size *= 2;
|
||||||
|
func_table = realloc(func_table, func_table_size * sizeof(func_t));
|
||||||
|
Assert(func_table, "Function table exceed memory limit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qsort(func_table, func_table_len, sizeof(func_t), cmp_func_t);
|
||||||
|
goto success;
|
||||||
|
|
||||||
|
success:
|
||||||
|
free(sym);
|
||||||
|
free(shstrtab);
|
||||||
|
return;
|
||||||
|
|
||||||
|
failed_funcname:
|
||||||
|
free(sym);
|
||||||
|
failed_shstrtab:
|
||||||
|
free(shstrtab);
|
||||||
|
failed_header:
|
||||||
|
for(int i = 0; i < func_table_len; i++) {
|
||||||
|
func_t *f = &func_table[i];
|
||||||
|
if(f->name) { free(f->name); }
|
||||||
|
}
|
||||||
|
free(func_table);
|
||||||
|
Error("Failed reading elf file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ftrace_call(vaddr_t pc, vaddr_t addr) {
|
||||||
|
func_t *f = get_func(addr);
|
||||||
|
Assert(ftrace_stack_len < CONFIG_FTRACE_STACK_SIZE,
|
||||||
|
"Ftrace stack exceed size limit, consider turn off ftrace or increase "
|
||||||
|
"FTRACE_STACK_SIZE.");
|
||||||
|
ftrace_stack[ftrace_stack_len] = pc + 4;
|
||||||
|
Trace("%*s0x%x call 0x%x <%s+0x%x>", ftrace_stack_len, "", pc, addr,
|
||||||
|
f == NULL ? "???" : f->name, addr - f->start);
|
||||||
|
ftrace_stack_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ftrace_return(vaddr_t pc, vaddr_t addr) {
|
||||||
|
--ftrace_stack_len;
|
||||||
|
for (; addr != ftrace_stack[ftrace_stack_len] && ftrace_stack_len >= 0;
|
||||||
|
ftrace_stack_len--) {
|
||||||
|
vaddr_t tco_addr = ftrace_stack[ftrace_stack_len];
|
||||||
|
func_t *f = get_func(tco_addr);
|
||||||
|
Trace("%*s0x%x ret 0x%x <%s+0x%x> (TCO)", ftrace_stack_len, "", pc, tco_addr,
|
||||||
|
f == NULL ? "???" : f->name, tco_addr - f->start);
|
||||||
|
}
|
||||||
|
func_t *f = get_func(addr);
|
||||||
|
Trace("%*s0x%x ret 0x%x <%s+0x%x>", ftrace_stack_len, "", pc, addr,
|
||||||
|
f == NULL ? "???" : f->name, addr - f->start);
|
||||||
|
}
|
Loading…
Reference in a new issue