diff --git a/docs/USAGE.md b/docs/USAGE.md index 04f318f70..fd9fa551d 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -245,6 +245,11 @@ The GDBJIT debugging support, only available with the compilation option GDBJIT= * 1 : Dynarec will generate GDBJIT debuginfo. * 2 : Dynarec will generate detailed GDBJIT debuginfo with internal state. +#### BOX64_DYNAREC_PERFMAP * +Dynarec generate map file for Linux perf tool. +* 0 : Dynarec will not generate perf map. (Default) +* 1 : Dynarec will generate perf map. + #### BOX64_DYNAREC_MISSING * Dynarec print the missing opcodes * 0 : not print the missing opcode (Default, unless DYNAREC_LOG>=1 or DYNAREC_DUMP>=1 is used) diff --git a/docs/box64.pod b/docs/box64.pod index 7957a3d16..e12f7c0a5 100644 --- a/docs/box64.pod +++ b/docs/box64.pod @@ -375,6 +375,13 @@ The GDBJIT debugging support, only available with the compilation option GDBJIT= * 1 : Dynarec will generate GDBJIT debuginfo. * 2 : Dynarec will generate detailed GDBJIT debuginfo with internal state. +=item B=I<0|1> + +Dynarec generate map file for Linux perf tool. + + * 0 : Dynarec will not generate perf map. (Default) + * 1 : Dynarec will generate perf map. + =item B=I<0|1> Handling of SSE Flush to 0 flags diff --git a/src/core.c b/src/core.c index c7e6bd2c3..363932353 100644 --- a/src/core.c +++ b/src/core.c @@ -17,6 +17,9 @@ #include #include #endif +#include +#include +#include #endif #include "build_info.h" @@ -99,6 +102,8 @@ uintptr_t box64_dynarec_test_start = 0; uintptr_t box64_dynarec_test_end = 0; int box64_dynarec_gdbjit = 0; int box64_dynarec_df = 1; +int box64_dynarec_perf_map = 0; +int box64_dynarec_perf_map_fd = -1; #ifdef ARM64 int arm64_asimd = 0; int arm64_aes = 0; @@ -912,6 +917,15 @@ void LoadLogEnv() if (box64_dynarec_gdbjit) printf_log(LOG_INFO, "Dynarec will generate debuginfo for gdbjit\n"); } + p = getenv("BOX64_DYNAREC_PERFMAP"); + if (p) { + if (strlen(p) == 1) { + if (p[0] >= '0' && p[0] <= '1') + box64_dynarec_perf_map = p[0] - '0'; + } + if (box64_dynarec_perf_map) + printf_log(LOG_INFO, "Dynarec will generate map file for Linux perf tool\n"); + } p = getenv("BOX64_DYNAREC_DF"); if(p) { if(strlen(p)==1) { @@ -2526,6 +2540,14 @@ int initialize(int argc, const char **argv, char** env, x64emu_t** emulator, elf *emulator = emu; +#ifdef DYNAREC + if (box64_dynarec_perf_map) { + char pathname[32]; + snprintf(pathname, sizeof(pathname), "/tmp/perf-%d.map", getpid()); + box64_dynarec_perf_map_fd = open(pathname, O_CREAT | O_RDWR | O_APPEND, S_IRUSR | S_IWUSR); + } +#endif + return 0; } @@ -2565,5 +2587,12 @@ int emulate(x64emu_t* emu, elfheader_t* elf_header) } #endif +#ifdef DYNAREC + if (box64_dynarec_perf_map && box64_dynarec_perf_map_fd != -1) { + close(box64_dynarec_perf_map_fd); + box64_dynarec_perf_map_fd = -1; + } +#endif + return ret; } diff --git a/src/dynarec/arm64/dynarec_arm64_functions.c b/src/dynarec/arm64/dynarec_arm64_functions.c index 020341d59..ba31f91b6 100644 --- a/src/dynarec/arm64/dynarec_arm64_functions.c +++ b/src/dynarec/arm64/dynarec_arm64_functions.c @@ -734,7 +734,7 @@ static register_mapping_t register_mappings[] = { void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t rex) { - if (!box64_dynarec_dump && !box64_dynarec_gdbjit) return; + if (!box64_dynarec_dump && !box64_dynarec_gdbjit && !box64_dynarec_perf_map) return; static char buf[512]; int length = sprintf(buf, "barrier=%d state=%d/%d/%d(%d:%d->%d:%d), %s=%X/%X, use=%X, need=%X/%X, sm=%d(%d/%d)", @@ -849,6 +849,9 @@ void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t r } dyn->gdbjit_block = GdbJITBlockAddLine(dyn->gdbjit_block, (dyn->native_start + dyn->insts[ninst].address), inst_name); } + if (box64_dynarec_perf_map && box64_dynarec_perf_map_fd != -1) { + writePerfMap(dyn->insts[ninst].x64.addr, dyn->native_start + dyn->insts[ninst].address, dyn->insts[ninst].size / 4); + } } void print_opcode(dynarec_native_t* dyn, int ninst, uint32_t opcode) diff --git a/src/dynarec/dynarec_native.c b/src/dynarec/dynarec_native.c index 5abbb9ef4..47c1c99e4 100644 --- a/src/dynarec/dynarec_native.c +++ b/src/dynarec/dynarec_native.c @@ -841,3 +841,13 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit //block->done = 1; return (void*)block; } + +void writePerfMap(uintptr_t func_addr, uintptr_t code_addr, size_t code_size) +{ + char pbuf[128]; + uint64_t sz = 0; + uintptr_t start = 0; + const char* symbname = FindNearestSymbolName(FindElfAddress(my_context, func_addr), (void*)func_addr, &start, &sz); + snprintf(pbuf, sizeof(pbuf), "0x%lx %ld %s\n", code_addr, code_size, symbname); + write(box64_dynarec_perf_map_fd, pbuf, strlen(pbuf)); +} diff --git a/src/dynarec/la64/dynarec_la64_functions.c b/src/dynarec/la64/dynarec_la64_functions.c index b12bf69db..cf3c43d42 100644 --- a/src/dynarec/la64/dynarec_la64_functions.c +++ b/src/dynarec/la64/dynarec_la64_functions.c @@ -26,6 +26,7 @@ #include "custommem.h" #include "bridge.h" #include "gdbjit.h" +#include "elfloader.h" #define XMM0 0 #define XMM8 16 @@ -331,7 +332,7 @@ static register_mapping_t register_mappings[] = { void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t rex) { - if (!box64_dynarec_dump && !box64_dynarec_gdbjit) return; + if (!box64_dynarec_dump && !box64_dynarec_gdbjit && !box64_dynarec_perf_map) return; static char buf[512]; int length = sprintf(buf, "barrier=%d state=%d/%d(%d), %s=%X/%X, use=%X, need=%X/%X, fuse=%d, sm=%d(%d/%d)", @@ -397,6 +398,9 @@ void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t r } dyn->gdbjit_block = GdbJITBlockAddLine(dyn->gdbjit_block, (dyn->native_start + dyn->insts[ninst].address), inst_name); } + if (box64_dynarec_perf_map && box64_dynarec_perf_map_fd != -1) { + writePerfMap(dyn->insts[ninst].x64.addr, dyn->native_start + dyn->insts[ninst].address, dyn->insts[ninst].size / 4); + } } // will go badly if address is unaligned @@ -522,4 +526,4 @@ void get_free_scratch(dynarec_la64_t* dyn, int ninst, uint8_t* tmp1, uint8_t* tm *tmp1 = tmp[0]; *tmp2 = tmp[1]; *tmp3 = tmp[2]; -} \ No newline at end of file +} diff --git a/src/dynarec/rv64/dynarec_rv64_functions.c b/src/dynarec/rv64/dynarec_rv64_functions.c index b26b2aadd..08dec9293 100644 --- a/src/dynarec/rv64/dynarec_rv64_functions.c +++ b/src/dynarec/rv64/dynarec_rv64_functions.c @@ -698,7 +698,7 @@ static register_mapping_t register_mappings[] = { void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t rex) { - if (!box64_dynarec_dump && !box64_dynarec_gdbjit) return; + if (!box64_dynarec_dump && !box64_dynarec_gdbjit && !box64_dynarec_perf_map) return; static char buf[512]; int length = sprintf(buf, "barrier=%d state=%d/%d(%d), %s=%X/%X, use=%X, need=%X/%X, fuse=%d, sm=%d(%d/%d), sew@entry=%d, sew@exit=%d", @@ -772,6 +772,9 @@ void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t r } dyn->gdbjit_block = GdbJITBlockAddLine(dyn->gdbjit_block, (dyn->native_start + dyn->insts[ninst].address), inst_name); } + if (box64_dynarec_perf_map && box64_dynarec_perf_map_fd != -1) { + writePerfMap(dyn->insts[ninst].x64.addr, dyn->native_start + dyn->insts[ninst].address, dyn->insts[ninst].size / 4); + } } void print_opcode(dynarec_native_t* dyn, int ninst, uint32_t opcode) diff --git a/src/include/debug.h b/src/include/debug.h index 14e062d5e..563011e0a 100644 --- a/src/include/debug.h +++ b/src/include/debug.h @@ -43,6 +43,8 @@ extern int box64_dynarec_missing; extern int box64_dynarec_aligned_atomics; extern int box64_dynarec_nativeflags; extern int box64_dynarec_df; +extern int box64_dynarec_perf_map; +extern int box64_dynarec_perf_map_fd; #ifdef ARM64 extern int arm64_asimd; extern int arm64_aes; diff --git a/src/include/dynarec_native.h b/src/include/dynarec_native.h index dd5218f60..4a5219a71 100644 --- a/src/include/dynarec_native.h +++ b/src/include/dynarec_native.h @@ -26,4 +26,6 @@ void addInst(instsize_t* insts, size_t* size, int x64_size, int native_size); void CancelBlock64(int need_lock); void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bits); -#endif //__DYNAREC_ARM_H_ \ No newline at end of file +void writePerfMap(uintptr_t func_addr, uintptr_t code_addr, size_t code_size); + +#endif //__DYNAREC_ARM_H_