Add profiling support

This commit is contained in:
Lauri Kasanen 2018-12-16 20:04:09 +02:00
parent a8779878e9
commit 9812f78917
9 changed files with 195 additions and 10 deletions

View file

@ -182,7 +182,8 @@ int cen64_main(int argc, const char **argv) {
if (device_create(device, &ddipl, dd_variant, &ddrom,
&pifrom, &cart, &eeprom, &sram,
&flashram, is_in, controller, options.no_audio, options.no_video) == NULL) {
&flashram, is_in, controller,
options.no_audio, options.no_video, options.enable_profiling) == NULL) {
printf("Failed to create a device.\n");
status = EXIT_FAILURE;
}
@ -190,7 +191,7 @@ int cen64_main(int argc, const char **argv) {
else {
device->multithread = options.multithread;
status = run_device(device, options.no_video);
device_destroy(device);
device_destroy(device, options.cart_path);
}
cen64_free(&cen64_device_mem);
@ -424,7 +425,7 @@ int run_device(struct cen64_device *device, bool no_video) {
if (cen64_thread_create(&thread, run_device_thread, device)) {
printf("Failed to create the main emulation thread.\n");
device_destroy(device);
device_destroy(device, NULL);
return 1;
}

View file

@ -28,6 +28,7 @@
#include "vr4300/cpu.h"
#include "vr4300/cp1.h"
#include <setjmp.h>
#include <limits.h>
cen64_cold int angrylion_rdp_init(struct cen64_device *device);
cen64_cold static int device_debug_spin(struct cen64_device *device);
@ -45,7 +46,7 @@ struct cen64_device *device_create(struct cen64_device *device,
const struct save_file *eeprom, const struct save_file *sram,
const struct save_file *flashram, struct is_viewer *is,
const struct controller *controller,
bool no_audio, bool no_video) {
bool no_audio, bool no_video, bool profiling) {
// Initialize the bus.
device->bus.ai = &device->ai;
@ -117,7 +118,7 @@ struct cen64_device *device_create(struct cen64_device *device,
}
// Initialize the VR4300.
if (vr4300_init(&device->vr4300, &device->bus)) {
if (vr4300_init(&device->vr4300, &device->bus, profiling)) {
debug("create_device: Failed to initialize the VR4300.\n");
return NULL;
}
@ -127,8 +128,30 @@ struct cen64_device *device_create(struct cen64_device *device,
}
// Cleans up memory allocated for the device.
void device_destroy(struct cen64_device *device) {
void device_destroy(struct cen64_device *device, const char *cart_path) {
rsp_destroy(&device->rsp);
// Save profiling data, if any
if (cart_path && device->vr4300.profile_samples) {
char path[PATH_MAX];
snprintf(path, PATH_MAX, "%s.profile", cart_path);
path[PATH_MAX - 1] = '\0';
FILE *f = fopen(path, "w");
if (!f) {
printf("Can't open %s\n", path);
return;
}
uint32_t i;
for (i = 0; i < 8 * 1024 * 1024; i++) {
if (device->vr4300.profile_samples[i] < 10)
continue;
fprintf(f, "%x %lu\n", i + 0x80000000, device->vr4300.profile_samples[i]);
}
fclose(f);
}
}
// Called when we should (probably?) leave simulation.

View file

@ -54,7 +54,7 @@ struct cen64_device {
bool running;
};
cen64_cold void device_destroy(struct cen64_device *device);
cen64_cold void device_destroy(struct cen64_device *device, const char *cart_path);
cen64_cold struct cen64_device *device_create(struct cen64_device *device,
const struct rom_file *ddipl, const struct dd_variant *dd_variant,
const struct rom_file *ddrom,
@ -62,7 +62,7 @@ cen64_cold struct cen64_device *device_create(struct cen64_device *device,
const struct save_file *eeprom, const struct save_file *sram,
const struct save_file *flashram, struct is_viewer *is,
const struct controller *controller,
bool no_audio, bool no_video);
bool no_audio, bool no_video, bool profiling);
cen64_cold void device_exit(struct bus_controller *bus);
cen64_cold void device_run(struct cen64_device *device);

View file

@ -30,6 +30,7 @@ const struct cen64_options default_cen64_options = {
false, // console
#endif
false, // enable_debugger
false, // enable_profiling
false, // multithread
false, // no_audio
false, // no_video
@ -59,6 +60,9 @@ int parse_options(struct cen64_options *options, int argc, const char *argv[]) {
options->debugger_addr = "localhost:64646";
}
else if (!strcmp(argv[i], "-profile"))
options->enable_profiling = true;
else if (!strcmp(argv[i], "-multithread"))
options->multithread = true;
@ -255,6 +259,7 @@ void print_command_line_usage(const char *invokation_string) {
" -debug [addr][:port] : Starts the debugger on interface:port.\n"
" By default, CEN64 uses localhost:64646.\n"
" NOTE: the debugger is not implemented yet.\n"
" -profile : Profile the ROM (cpu-side).\n"
" -multithread : Run in a threaded (but quasi-accurate) mode.\n"
" : This mode cannot be run with the debugger.\n"
" -ddipl <path> : Path to the 64DD IPL ROM (enables 64DD mode).\n"

View file

@ -32,6 +32,7 @@ struct cen64_options {
#endif
bool enable_debugger;
bool enable_profiling;
bool multithread;
bool no_audio;
bool no_video;

View file

@ -0,0 +1,142 @@
//
// cen64-profile2callgrind: Convert a cen64 profile file into a callgrind one.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2018 Lauri Kasanen
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include <string>
using namespace std;
static void die(const char fmt[], ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(1);
}
int main(int argc, char **argv) {
string s;
map<uint32_t, string> funcs;
char buf[PATH_MAX];
if (argc != 3) {
die("Usage: %s my.z64.profile my.elf\n", argv[0]);
}
s = "nm ";
s += argv[2];
s += " | grep -i \" t \"";
FILE *f = popen(s.c_str(), "r");
if (!f) die("Can't get functions\n");
// Add the bootloader
funcs[0x80000000] = "PIF bootloader";
while (fgets(buf, PATH_MAX, f)) {
uint32_t tmp;
if (sscanf(buf, "%x", &tmp) != 1)
die("Failed getting functions\n");
char *ptr = strrchr(buf, ' ');
if (!ptr)
die("Failed getting functions\n");
ptr++;
uint32_t len = strlen(ptr);
if (ptr[len - 1] == '\n')
ptr[len - 1] = '\0';
// Skip internal/libc magic parts
if (ptr[0] == '_' && ptr[1] == '_')
continue;
funcs[tmp] = ptr;
}
pclose(f);
strcpy(buf, argv[1]);
char *ptr = strstr(buf, ".profile");
if (ptr) {
*ptr = '\0';
}
strcat(buf, ".callgrind");
FILE *out = fopen(buf, "w");
if (!out) die("Can't open '%s' for output\n", buf);
fprintf(out, "# callgrind format\n");
fprintf(out, "cmd: %s\n", argv[2]);
fprintf(out, "events: instructions\n\n");
fprintf(out, "ob=%s\n\n", argv[2]);
uint64_t summary = 0;
// We have a list of functions, now turn the samples into C line info
f = fopen(argv[1], "r");
if (!f) die("Can't open profile file\n");
while (fgets(buf, PATH_MAX, f)) {
uint32_t addr;
uint64_t num;
if (sscanf(buf, "%x %lu", &addr, &num) != 2)
die("Malformed profile file\n");
summary += num;
char *ptr = strchr(buf, ' ');
*ptr = '\0';
s = "addr2line -s -e ";
s += argv[2];
s += " ";
s += buf;
char linebuf[PATH_MAX];
FILE *p = popen(s.c_str(), "r");
if (!fgets(linebuf, PATH_MAX, p))
die("Failed getting addr\n");
pclose(p);
ptr = strchr(linebuf, ':');
*ptr = '\0';
ptr++;
uint32_t line = 0;
if (*ptr != '?' && sscanf(ptr, "%u", &line) != 1)
die("Failed getting addr\n");
// Okay, we have everything for this sample. Put it out
fprintf(out, "fl=%s\n", linebuf);
map<uint32_t, string>::const_iterator it = funcs.lower_bound(addr);
it--;
fprintf(out, "fn=%s\n", it->second.c_str());
fprintf(out, "%u %lu\n\n", line, num);
}
fprintf(out, "totals: %lu\n", summary);
fclose(f);
fclose(out);
return 0;
}

View file

@ -30,7 +30,7 @@ static void vr4300_connect_bus(struct vr4300 *vr4300,
}
// Initializes the VR4300 component.
int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus) {
int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus, bool profiling) {
vr4300_connect_bus(vr4300, bus);
vr4300_cp0_init(vr4300);
@ -45,6 +45,12 @@ int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus) {
// MESS uses this version, so we will too?
vr4300->mi_regs[MI_VERSION_REG] = 0x01010101;
vr4300->mi_regs[MI_INIT_MODE_REG] = 0x80;
if (profiling)
vr4300->profile_samples = calloc(8 * 1024 * 1024, sizeof(uint64_t));
else
vr4300->profile_samples = NULL;
return 0;
}

View file

@ -102,6 +102,7 @@ struct vr4300 {
struct vr4300_dcache dcache;
struct vr4300_icache icache;
uint64_t *profile_samples;
};
struct vr4300_stats {
@ -111,7 +112,7 @@ struct vr4300_stats {
unsigned long opcode_counts[NUM_VR4300_OPCODES];
};
cen64_cold int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus);
cen64_cold int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus, bool profiling);
cen64_cold void vr4300_print_summary(struct vr4300_stats *stats);
cen64_flatten cen64_hot void vr4300_cycle_(struct vr4300 *vr4300);

View file

@ -185,6 +185,12 @@ static int vr4300_ex_stage(struct vr4300 *vr4300) {
vr4300_opcode_mnemonics[rfex_latch->opcode.id]);
#endif
if (vr4300->profile_samples) {
uint32_t idx = rfex_latch->common.pc - 0x80000000;
idx &= (8 * 1024 * 1024) - 1;
vr4300->profile_samples[idx]++;
}
exdc_latch->dest = VR4300_REGISTER_R0;
exdc_latch->request.type = VR4300_BUS_REQUEST_NONE;
return vr4300_function_table[rfex_latch->opcode.id](