gbdb,video: improve logging
Add more runtime toggles and add the ability to log to a file for faster runs and better searchability.
This commit is contained in:
256
src/apps/gbdb.c
256
src/apps/gbdb.c
@@ -11,6 +11,9 @@
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "gb_disas.h"
|
||||
@@ -49,20 +52,37 @@ static volatile sig_atomic_t paused = 0;
|
||||
|
||||
static struct {
|
||||
bool quiet;
|
||||
int log_fd;
|
||||
} config;
|
||||
|
||||
static void gb_log(const char *fmt, ...)
|
||||
void gb_log(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_list args, args_copy;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
if (config.quiet) {
|
||||
return;
|
||||
va_copy(args_copy, args);
|
||||
if (!config.quiet) {
|
||||
vprintf(fmt, args);
|
||||
}
|
||||
|
||||
vprintf(fmt, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
if (config.log_fd != -1) {
|
||||
vdprintf(config.log_fd, fmt, args_copy);
|
||||
}
|
||||
}
|
||||
|
||||
void gb_error(const char *fmt, ...)
|
||||
{
|
||||
va_list args, args_copy;
|
||||
|
||||
va_start(args, fmt);
|
||||
va_copy(args_copy, args);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (config.log_fd != -1) {
|
||||
vdprintf(config.log_fd, fmt, args_copy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +110,8 @@ static void init(void)
|
||||
lr35902_init(&cpu, memory, &cpu_ops);
|
||||
gb_video_init(&video, memory);
|
||||
gb_mem_init(memory, &video);
|
||||
|
||||
config.log_fd = -1;
|
||||
}
|
||||
|
||||
static void cycle()
|
||||
@@ -106,18 +128,120 @@ static void show_prompt()
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void call_next_cmd(char *arg_list,
|
||||
struct tri *commands,
|
||||
const char *cmdname)
|
||||
{
|
||||
char * remainder;
|
||||
gbdb_cmd *cmd;
|
||||
bool ambiguous = false;
|
||||
remainder = strchr(arg_list, ' ');
|
||||
if (remainder != NULL) {
|
||||
remainder[0] = '\0';
|
||||
remainder++;
|
||||
}
|
||||
|
||||
cmd = tri_get_string_autocomplete(commands, arg_list, &ambiguous);
|
||||
|
||||
if (ambiguous) {
|
||||
gb_error("ambiguous %s command: '%s'\n", cmdname, arg_list);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == NULL) {
|
||||
gb_error("unrecognized %s command: '%s'\n", cmdname, arg_list);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd(remainder);
|
||||
}
|
||||
|
||||
static void set_logfile(char *arglist)
|
||||
{
|
||||
char *token = token = strtok(arglist, " ");
|
||||
int fd;
|
||||
|
||||
if (token != NULL) {
|
||||
strip_newline(token);
|
||||
}
|
||||
|
||||
if (token == NULL || strcmp(token, "") == 0) {
|
||||
if (config.log_fd != -1) {
|
||||
close(config.log_fd);
|
||||
}
|
||||
config.log_fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open(token,
|
||||
O_WRONLY | O_TRUNC | O_CREAT,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (fd < 0) {
|
||||
gb_error("Failed to open logfile %s, %s\n", token, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.log_fd != -1) {
|
||||
close(config.log_fd);
|
||||
}
|
||||
|
||||
config.log_fd = fd;
|
||||
}
|
||||
|
||||
static void set_quiet_on(char *arg_list)
|
||||
{
|
||||
config.quiet = true;
|
||||
}
|
||||
|
||||
static void set_quiet_off(char *arg_list)
|
||||
{
|
||||
config.quiet = false;
|
||||
}
|
||||
|
||||
static void set_quiet(char *arg_list)
|
||||
{
|
||||
static bool init = false;
|
||||
static struct tri commands;
|
||||
|
||||
if (!init) {
|
||||
tri_init(&commands);
|
||||
tri_add_string(&commands, "on", set_quiet_on);
|
||||
tri_add_string(&commands, "true", set_quiet_on);
|
||||
tri_add_string(&commands, "off", set_quiet_off);
|
||||
tri_add_string(&commands, "false", set_quiet_off);
|
||||
init = true;
|
||||
}
|
||||
|
||||
call_next_cmd(arg_list, &commands, "set quiet");
|
||||
}
|
||||
|
||||
static void set_video_debug(char *arg_list)
|
||||
{
|
||||
video.debug_logging = true;
|
||||
}
|
||||
|
||||
static void set(char *arg_list)
|
||||
{
|
||||
/* TODO make this support other options */
|
||||
config.quiet = true;
|
||||
static bool init = false;
|
||||
static struct tri commands;
|
||||
|
||||
if (!init) {
|
||||
tri_init(&commands);
|
||||
tri_add_string(&commands, "quiet", set_quiet);
|
||||
tri_add_string(&commands, "logfile", set_logfile);
|
||||
tri_add_string(&commands, "video_debug", set_video_debug);
|
||||
init = true;
|
||||
}
|
||||
|
||||
call_next_cmd(arg_list, &commands, "set");
|
||||
}
|
||||
|
||||
static void echo(char *arg_list)
|
||||
{
|
||||
if (arg_list != NULL) {
|
||||
printf("%s\n", arg_list);
|
||||
gb_log("%s\n", arg_list);
|
||||
} else {
|
||||
printf("\n");
|
||||
gb_log("\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,32 +298,39 @@ static void regs(char *arg_list)
|
||||
uint8_t f = (cpu.nf << CPU_F_BIT_POS_N) | (cpu.zf << CPU_F_BIT_POS_Z) |
|
||||
(cpu.cf << CPU_F_BIT_POS_C) | (cpu.hf << CPU_F_BIT_POS_H);
|
||||
|
||||
printf("AF: 0x%04x A: 0x%02x F: 0x%02x\n", (uint16_t) (cpu.a << 8) | f, cpu.a, f);
|
||||
printf("BC: 0x%04x B: 0x%02x C: 0x%02x\n", cpu.bc, cpu.b, cpu.c);
|
||||
printf("DE: 0x%04x D: 0x%02x E: 0x%02x\n", cpu.de, cpu.d, cpu.e);
|
||||
printf("HL: 0x%04x H: 0x%02x L: 0x%02x\n", cpu.hl, cpu.h, cpu.l);
|
||||
printf("PC: 0x%04x\n", cpu.pc);
|
||||
printf("SP: 0x%04x\n", cpu.sp);
|
||||
gb_log("AF: 0x%04x A: 0x%02x F: 0x%02x\n", (uint16_t) (cpu.a << 8) | f, cpu.a, f);
|
||||
gb_log("BC: 0x%04x B: 0x%02x C: 0x%02x\n", cpu.bc, cpu.b, cpu.c);
|
||||
gb_log("DE: 0x%04x D: 0x%02x E: 0x%02x\n", cpu.de, cpu.d, cpu.e);
|
||||
gb_log("HL: 0x%04x H: 0x%02x L: 0x%02x\n", cpu.hl, cpu.h, cpu.l);
|
||||
gb_log("PC: 0x%04x\n", cpu.pc);
|
||||
gb_log("SP: 0x%04x\n", cpu.sp);
|
||||
}
|
||||
|
||||
static void peek(char *arg_list)
|
||||
{
|
||||
uint16_t pc = lr35902_get_reg_16(&cpu, LR35902_REG_PC);
|
||||
uint8_t byte = gb_mem_read(memory, pc);
|
||||
printf("0x%04x:%s\n", pc, gb_byte_to_opcode(byte));
|
||||
gb_log("0x%04x:%s\n", pc, gb_byte_to_opcode(byte));
|
||||
}
|
||||
|
||||
static void vstep(char *arg_list)
|
||||
{
|
||||
peek(NULL);
|
||||
step(arg_list);
|
||||
regs(NULL);
|
||||
}
|
||||
|
||||
static void stats(char *arg_list)
|
||||
{
|
||||
printf("Cycles: %llu\n", cpu.metrics.cycles);
|
||||
printf("Retired Insructions %llu\n", cpu.metrics.retired_instrs);
|
||||
printf("Memory Reads: %llu\n", cpu.metrics.mem_reads);
|
||||
printf("Memory Writes: %llu\n", cpu.metrics.mem_writes);
|
||||
gb_log("Cycles: %" PRId64 "\n", cpu.metrics.cycles);
|
||||
gb_log("Retired Insructions %" PRId64 "\n", cpu.metrics.retired_instrs);
|
||||
gb_log("Memory Reads: %" PRId64 " \n", cpu.metrics.mem_reads);
|
||||
gb_log("Memory Writes: %" PRId64 "\n", cpu.metrics.mem_writes);
|
||||
}
|
||||
|
||||
static void help(char *arg_list)
|
||||
{
|
||||
printf(usage);
|
||||
gb_log(usage);
|
||||
}
|
||||
|
||||
static int64_t parse_reg_str(const char *str)
|
||||
@@ -253,20 +384,20 @@ static void assert(char *arg_list)
|
||||
|
||||
val0_str = strtok(arg_list, " ");
|
||||
if (val0_str == NULL) {
|
||||
printf("%s", usage);
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
}
|
||||
val0 = parse_val(val0_str);
|
||||
|
||||
operator_str = strtok(NULL, " ");
|
||||
if (operator_str == NULL) {
|
||||
printf("%s", usage);
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
}
|
||||
|
||||
val1_str = strtok(NULL, " ");
|
||||
if (val1_str == NULL) {
|
||||
printf("%s", usage);
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
}
|
||||
val1 = parse_val(val1_str);
|
||||
@@ -296,13 +427,13 @@ static void assert(char *arg_list)
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
printf("%s", usage);
|
||||
gb_error("%s", usage);
|
||||
}
|
||||
|
||||
return;
|
||||
fail:
|
||||
printf("ASSERT: %s %s %s\n", val0_str, operator_str, val1_str);
|
||||
printf("%s=%lld, %s=%lld\n", val0_str, val0, val1_str, val1);
|
||||
gb_error("ASSERT: %s %s %s\n", val0_str, operator_str, val1_str);
|
||||
gb_error("%s=%" PRId64 ", %s=%" PRId64 "\n", val0_str, val0, val1_str, val1);
|
||||
regs(NULL);
|
||||
stats(NULL);
|
||||
exit(1);
|
||||
@@ -314,7 +445,7 @@ static void mem(char *arg_list)
|
||||
char *token = strtok(arg_list, " ");
|
||||
|
||||
if (token == NULL) {
|
||||
printf("usage: mem <addr> (num bytes) (format)\n");
|
||||
gb_error("usage: mem <addr> (num bytes) (format)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -333,11 +464,11 @@ static void mem(char *arg_list)
|
||||
uint8_t val = gb_mem_read(memory, addr + i);
|
||||
|
||||
if (token != NULL && token[0] == 'i') {
|
||||
printf("0x%04x:%s\n", addr + i, gb_byte_to_opcode(val));
|
||||
gb_log("0x%04x:%s\n", addr + i, gb_byte_to_opcode(val));
|
||||
} else if (token != NULL && token[0] == 'b') {
|
||||
printf("0x%04x:0x%02x %s\n", addr + i, val, gb_byte_to_opcode(val));
|
||||
gb_log("0x%04x:0x%02x %s\n", addr + i, val, gb_byte_to_opcode(val));
|
||||
} else {
|
||||
printf("0x%04x:0x%02x\n", addr + i, val);
|
||||
gb_log("0x%04x:0x%02x\n", addr + i, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +478,7 @@ static void load(char *arg_list)
|
||||
{
|
||||
char *token = strtok(arg_list, " ");
|
||||
if (token == NULL) {
|
||||
printf("usage: load <file>\n");
|
||||
gb_error("usage: load <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -360,7 +491,7 @@ static void load_bootrom(char *arg_list)
|
||||
{
|
||||
char *token = strtok(arg_list, " ");
|
||||
if (token == NULL) {
|
||||
printf("usage: bootrom <file>\n");
|
||||
gb_error("usage: bootrom <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -446,7 +577,7 @@ static int do_set_breakpoint(uint16_t addr, bool temp)
|
||||
gb_mem_force_write(memory, addr, 0xd3);
|
||||
num_breakpoints++;
|
||||
} else {
|
||||
printf("maximum number of breakpoints reached\n");
|
||||
gb_error("maximum number of breakpoints reached\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +587,7 @@ static void set_breakpoint(char *arg_string)
|
||||
char *token = strtok(arg_string, " ");
|
||||
|
||||
if (token == NULL) {
|
||||
printf("usage: breakpoint add <addr>\n");
|
||||
gb_error("usage: breakpoint add <addr>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -470,14 +601,14 @@ static void runto(char *arg_string)
|
||||
char *token = strtok(arg_string, " ");
|
||||
|
||||
if (token == NULL) {
|
||||
printf("usage: runto <addr>\n");
|
||||
gb_error("usage: runto <addr>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
addr = parse_val(token);
|
||||
rc = do_set_breakpoint(addr, true);
|
||||
if (rc < 0) {
|
||||
printf("failed to set breakpoint\n");
|
||||
gb_error("failed to set breakpoint\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -517,7 +648,7 @@ static void breakpoint_addr_hit(uint16_t addr)
|
||||
|
||||
|
||||
if (bkpt == NULL) {
|
||||
printf("No breakpoint found at addr=%d\n", addr);
|
||||
gb_error("No breakpoint found at addr=%d\n", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -532,7 +663,7 @@ static void delete_breakpoint(char *arg_string)
|
||||
int rc, bkpt;
|
||||
|
||||
if (token == NULL) {
|
||||
printf("usage: breakpoint rm <bkpt num>\n");
|
||||
gb_error("usage: breakpoint rm <bkpt num>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -540,7 +671,7 @@ static void delete_breakpoint(char *arg_string)
|
||||
|
||||
rc = do_delete_breakpoint(bkpt);
|
||||
if (rc < 0) {
|
||||
printf("%d is not a valid breakpoint number\n", bkpt);
|
||||
gb_error("%d is not a valid breakpoint number\n", bkpt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,10 +681,10 @@ static void display_breakpoints(char *arg_string)
|
||||
|
||||
if (num_breakpoints) {
|
||||
for (i = 0; i < num_breakpoints; i++) {
|
||||
printf("#%d: 0x%04x\n", breakpoints[i].id, breakpoints[i].addr);
|
||||
gb_log("#%d: 0x%04x\n", breakpoints[i].id, breakpoints[i].addr);
|
||||
}
|
||||
} else {
|
||||
printf("No breakpoints set\n");
|
||||
gb_log("No breakpoints set\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,28 +706,7 @@ static void breakpoint(char *arg_list)
|
||||
init = true;
|
||||
}
|
||||
|
||||
char * remainder;
|
||||
gbdb_cmd *cmd;
|
||||
bool ambiguous = false;
|
||||
remainder = strchr(arg_list, ' ');
|
||||
if (remainder != NULL) {
|
||||
remainder[0] = '\0';
|
||||
remainder++;
|
||||
}
|
||||
|
||||
cmd = tri_get_string_autocomplete(&commands, arg_list, &ambiguous);
|
||||
|
||||
if (ambiguous) {
|
||||
printf("ambiguous breakpoint command: '%s'\n", arg_list);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == NULL) {
|
||||
printf("unrecognized breakpoint command: '%s'\n", arg_list);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd(remainder);
|
||||
call_next_cmd(arg_list, &commands, "breakpoint");
|
||||
}
|
||||
|
||||
static bool breakpoint_is_at_addr(uint16_t addr) {
|
||||
@@ -657,13 +767,14 @@ static void process_cmd(const char *cmd_str, struct tri *commands)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
cmd = tri_get_string_autocomplete(commands, cmd_buffer, &ambiguous);
|
||||
|
||||
if (ambiguous) {
|
||||
printf("ambiguous command: '%s'\n", cmd_buffer);
|
||||
gb_error("ambiguous command: '%s'\n", cmd_buffer);
|
||||
break;
|
||||
} else if (cmd == NULL) {
|
||||
printf("unrecognized command: '%s'\n", cmd_buffer);
|
||||
gb_error("unrecognized command: '%s'\n", cmd_buffer);
|
||||
break;
|
||||
} else {
|
||||
cmd(arg_str);
|
||||
@@ -708,6 +819,7 @@ int main(int argc, char **argv)
|
||||
|
||||
tri_init(&commands);
|
||||
tri_add_string(&commands, "step", step);
|
||||
tri_add_string(&commands, "vstep", vstep);
|
||||
tri_add_string(&commands, "run", run);
|
||||
tri_add_string(&commands, "regs", regs);
|
||||
tri_add_string(&commands, "stats", stats);
|
||||
@@ -732,7 +844,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (argc > 1) {
|
||||
if (load_initfile(argv[1], &commands)) {
|
||||
printf("Failed to load initfile\n");
|
||||
gb_error("Failed to load initfile\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,5 +870,9 @@ int main(int argc, char **argv)
|
||||
process_cmd(cmd_str, &commands);
|
||||
}
|
||||
|
||||
if (config.log_fd != -1) {
|
||||
close(config.log_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user