CPU: Make trace-to-file toggleable at runtime and in release builds
This commit is contained in:
parent
4b7820d1e5
commit
bf1d51b5d8
|
@ -14,6 +14,7 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "timing_event.h"
|
#include "timing_event.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
Log_SetChannel(CPU::Core);
|
Log_SetChannel(CPU::Core);
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
|
@ -22,11 +23,15 @@ static void SetPC(u32 new_pc);
|
||||||
static void UpdateLoadDelay();
|
static void UpdateLoadDelay();
|
||||||
static void Branch(u32 target);
|
static void Branch(u32 target);
|
||||||
static void FlushPipeline();
|
static void FlushPipeline();
|
||||||
|
static void UpdateDebugDispatcherFlag();
|
||||||
|
|
||||||
State g_state;
|
State g_state;
|
||||||
bool g_using_interpreter = false;
|
bool g_using_interpreter = false;
|
||||||
bool TRACE_EXECUTION = 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 constexpr u32 INVALID_BREAKPOINT_PC = UINT32_C(0xFFFFFFFF);
|
||||||
static std::vector<Breakpoint> s_breakpoints;
|
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 u32 s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC;
|
||||||
static bool s_single_step = false;
|
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, ...)
|
void WriteToExecutionLog(const char* format, ...)
|
||||||
{
|
{
|
||||||
static std::FILE* log_file = nullptr;
|
|
||||||
static bool log_file_opened = false;
|
|
||||||
|
|
||||||
std::va_list ap;
|
std::va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
|
|
||||||
if (!log_file_opened)
|
if (!s_log_file_opened)
|
||||||
{
|
{
|
||||||
log_file = FileSystem::OpenCFile("cpu_log.txt", "wb");
|
s_log_file = FileSystem::OpenCFile("cpu_log.txt", "wb");
|
||||||
log_file_opened = true;
|
s_log_file_opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_file)
|
if (s_log_file)
|
||||||
{
|
{
|
||||||
std::vfprintf(log_file, format, ap);
|
std::vfprintf(s_log_file, format, ap);
|
||||||
std::fflush(log_file);
|
std::fflush(s_log_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
@ -79,6 +108,7 @@ void Shutdown()
|
||||||
// GTE::Shutdown();
|
// GTE::Shutdown();
|
||||||
PGXP::Shutdown();
|
PGXP::Shutdown();
|
||||||
ClearBreakpoints();
|
ClearBreakpoints();
|
||||||
|
StopTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
|
@ -527,8 +557,6 @@ restart_instruction:
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
if (TRACE_EXECUTION)
|
if (TRACE_EXECUTION)
|
||||||
PrintInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
|
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
|
#endif
|
||||||
|
|
||||||
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
|
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
|
||||||
|
@ -1469,7 +1497,7 @@ static void UpdateDebugDispatcherFlag()
|
||||||
|
|
||||||
// TODO: cop0 breakpoints
|
// 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)
|
if (use_debug_dispatcher == g_state.use_debug_dispatcher)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1757,6 +1785,13 @@ static void ExecuteImpl()
|
||||||
if (!FetchInstruction())
|
if (!FetchInstruction())
|
||||||
continue;
|
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 0 // GTE flag test debugging
|
||||||
if (g_state.m_current_instruction_pc == 0x8002cdf4)
|
if (g_state.m_current_instruction_pc == 0x8002cdf4)
|
||||||
{
|
{
|
||||||
|
|
|
@ -70,7 +70,7 @@ struct State
|
||||||
Reg next_load_delay_reg = Reg::count;
|
Reg next_load_delay_reg = Reg::count;
|
||||||
u32 next_load_delay_value = 0;
|
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 registers are stored here so we can access them on ARM with a single instruction
|
||||||
GTE::Regs gte_regs = {};
|
GTE::Regs gte_regs = {};
|
||||||
|
@ -103,15 +103,33 @@ void SingleStep();
|
||||||
// Forces an early exit from the CPU dispatcher.
|
// Forces an early exit from the CPU dispatcher.
|
||||||
void ForceDispatcherExit();
|
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 TickCount GetPendingTicks()
|
||||||
ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; }
|
{
|
||||||
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { g_state.pending_ticks += ticks; }
|
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
|
// state helpers
|
||||||
ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; }
|
ALWAYS_INLINE bool InUserMode()
|
||||||
ALWAYS_INLINE bool InKernelMode() { return !g_state.cop0_regs.sr.KUc; }
|
{
|
||||||
|
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.
|
// Memory reads variants which do not raise exceptions.
|
||||||
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
|
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.
|
// Write to CPU execution log file.
|
||||||
void WriteToExecutionLog(const char* format, ...);
|
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.
|
// Breakpoint callback - if the callback returns false, the breakpoint will be removed.
|
||||||
using BreakpointCallback = bool (*)(VirtualMemoryAddress address);
|
using BreakpointCallback = bool (*)(VirtualMemoryAddress address);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/cheats.h"
|
#include "core/cheats.h"
|
||||||
#include "core/controller.h"
|
#include "core/controller.h"
|
||||||
|
#include "core/cpu_core.h"
|
||||||
#include "core/gpu.h"
|
#include "core/gpu.h"
|
||||||
#include "core/host_display.h"
|
#include "core/host_display.h"
|
||||||
#include "core/system.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 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);
|
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))
|
if (ImGui::MenuItem("Dump RAM...", nullptr, nullptr, system_valid))
|
||||||
DoDumpRAM();
|
DoDumpRAM();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue