CPU: Make trace-to-file toggleable at runtime and in release builds

This commit is contained in:
Connor McLaughlin 2021-01-04 01:55:15 +10:00
parent 4b7820d1e5
commit bf1d51b5d8
3 changed files with 88 additions and 21 deletions

View File

@ -14,6 +14,7 @@
#include "system.h"
#include "timing_event.h"
#include <cstdio>
Log_SetChannel(CPU::Core);
namespace CPU {
@ -22,11 +23,15 @@ static void SetPC(u32 new_pc);
static void UpdateLoadDelay();
static void Branch(u32 target);
static void FlushPipeline();
static void UpdateDebugDispatcherFlag();
State g_state;
bool g_using_interpreter = false;
bool TRACE_EXECUTION = false;
bool LOG_EXECUTION = false;
static std::FILE* s_log_file = nullptr;
static bool s_log_file_opened = false;
static bool s_trace_to_log = false;
static constexpr u32 INVALID_BREAKPOINT_PC = UINT32_C(0xFFFFFFFF);
static std::vector<Breakpoint> s_breakpoints;
@ -34,24 +39,48 @@ static u32 s_breakpoint_counter = 1;
static u32 s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC;
static bool s_single_step = false;
bool IsTraceEnabled()
{
return s_trace_to_log;
}
void StartTrace()
{
if (s_trace_to_log)
return;
s_trace_to_log = true;
UpdateDebugDispatcherFlag();
}
void StopTrace()
{
if (!s_trace_to_log)
return;
if (s_log_file)
std::fclose(s_log_file);
s_log_file_opened = false;
s_trace_to_log = false;
UpdateDebugDispatcherFlag();
}
void WriteToExecutionLog(const char* format, ...)
{
static std::FILE* log_file = nullptr;
static bool log_file_opened = false;
std::va_list ap;
va_start(ap, format);
if (!log_file_opened)
if (!s_log_file_opened)
{
log_file = FileSystem::OpenCFile("cpu_log.txt", "wb");
log_file_opened = true;
s_log_file = FileSystem::OpenCFile("cpu_log.txt", "wb");
s_log_file_opened = true;
}
if (log_file)
if (s_log_file)
{
std::vfprintf(log_file, format, ap);
std::fflush(log_file);
std::vfprintf(s_log_file, format, ap);
std::fflush(s_log_file);
}
va_end(ap);
@ -79,6 +108,7 @@ void Shutdown()
// GTE::Shutdown();
PGXP::Shutdown();
ClearBreakpoints();
StopTrace();
}
void Reset()
@ -527,8 +557,6 @@ restart_instruction:
#ifdef _DEBUG
if (TRACE_EXECUTION)
PrintInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
if (LOG_EXECUTION)
LogInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
#endif
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
@ -1469,7 +1497,7 @@ static void UpdateDebugDispatcherFlag()
// TODO: cop0 breakpoints
const bool use_debug_dispatcher = has_any_breakpoints;
const bool use_debug_dispatcher = has_any_breakpoints || s_trace_to_log;
if (use_debug_dispatcher == g_state.use_debug_dispatcher)
return;
@ -1757,6 +1785,13 @@ static void ExecuteImpl()
if (!FetchInstruction())
continue;
// trace functionality
if constexpr (debug)
{
if (s_trace_to_log)
LogInstruction(g_state.current_instruction.bits, g_state.current_instruction_pc, &g_state.regs);
}
#if 0 // GTE flag test debugging
if (g_state.m_current_instruction_pc == 0x8002cdf4)
{

View File

@ -70,7 +70,7 @@ struct State
Reg next_load_delay_reg = Reg::count;
u32 next_load_delay_value = 0;
CacheControl cache_control{ 0 };
CacheControl cache_control{0};
// GTE registers are stored here so we can access them on ARM with a single instruction
GTE::Regs gte_regs = {};
@ -103,15 +103,33 @@ void SingleStep();
// Forces an early exit from the CPU dispatcher.
void ForceDispatcherExit();
ALWAYS_INLINE Registers& GetRegs() { return g_state.regs; }
ALWAYS_INLINE Registers& GetRegs()
{
return g_state.regs;
}
ALWAYS_INLINE TickCount GetPendingTicks() { return g_state.pending_ticks; }
ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; }
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { g_state.pending_ticks += ticks; }
ALWAYS_INLINE TickCount GetPendingTicks()
{
return g_state.pending_ticks;
}
ALWAYS_INLINE void ResetPendingTicks()
{
g_state.pending_ticks = 0;
}
ALWAYS_INLINE void AddPendingTicks(TickCount ticks)
{
g_state.pending_ticks += ticks;
}
// state helpers
ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; }
ALWAYS_INLINE bool InKernelMode() { return !g_state.cop0_regs.sr.KUc; }
ALWAYS_INLINE bool InUserMode()
{
return g_state.cop0_regs.sr.KUc;
}
ALWAYS_INLINE bool InKernelMode()
{
return !g_state.cop0_regs.sr.KUc;
}
// Memory reads variants which do not raise exceptions.
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
@ -132,6 +150,11 @@ void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_aft
// Write to CPU execution log file.
void WriteToExecutionLog(const char* format, ...);
// Trace Routines
bool IsTraceEnabled();
void StartTrace();
void StopTrace();
// Breakpoint callback - if the callback returns false, the breakpoint will be removed.
using BreakpointCallback = bool (*)(VirtualMemoryAddress address);

View File

@ -7,6 +7,7 @@
#include "common/string_util.h"
#include "core/cheats.h"
#include "core/controller.h"
#include "core/cpu_core.h"
#include "core/gpu.h"
#include "core/host_display.h"
#include "core/system.h"
@ -1175,6 +1176,14 @@ void SDLHostInterface::DrawDebugMenu()
settings_changed |= ImGui::MenuItem("Dump CPU to VRAM Copies", nullptr, &debug_settings.dump_cpu_to_vram_copies);
settings_changed |= ImGui::MenuItem("Dump VRAM to CPU Copies", nullptr, &debug_settings.dump_vram_to_cpu_copies);
if (ImGui::MenuItem("CPU Trace Logging", nullptr, CPU::IsTraceEnabled()))
{
if (!CPU::IsTraceEnabled())
CPU::StartTrace();
else
CPU::StopTrace();
}
if (ImGui::MenuItem("Dump RAM...", nullptr, nullptr, system_valid))
DoDumpRAM();