ImDebugger: Add a window to inspect upcoming CoreTiming events

This commit is contained in:
Henrik Rydgård 2024-12-07 16:05:55 +01:00
parent 3ca3a797c9
commit b3346df646
13 changed files with 134 additions and 27 deletions

View File

@ -459,6 +459,7 @@
<ClInclude Include="Data\Collections\CharQueue.h" />
<ClInclude Include="Data\Collections\FixedSizeQueue.h" />
<ClInclude Include="Data\Collections\Hashmaps.h" />
<ClInclude Include="Data\Collections\LinkedList.h" />
<ClInclude Include="Data\Collections\Slice.h" />
<ClInclude Include="Data\Collections\ThreadSafeList.h" />
<ClInclude Include="Data\Collections\TinySet.h" />

View File

@ -674,6 +674,9 @@
<ClInclude Include="Data\Collections\CharQueue.h">
<Filter>Data\Collections</Filter>
</ClInclude>
<ClInclude Include="Data\Collections\LinkedList.h">
<Filter>Data\Collections</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />

View File

@ -0,0 +1,6 @@
#pragma once
template <class T>
struct LinkedListItem : public T {
LinkedListItem<T> *next;
};

View File

@ -37,17 +37,12 @@
#include "Common/CommonTypes.h"
#include "Common/Log.h"
#include "Common/File/Path.h"
#include "Common/Data/Collections/LinkedList.h"
namespace File {
class IOFile;
};
template <class T>
struct LinkedListItem : public T
{
LinkedListItem<T> *next;
};
class PointerWrap;
class PointerWrapSection

View File

@ -40,13 +40,7 @@ int CPU_HZ = 222000000;
#define INITIAL_SLICE_LENGTH 20000
#define MAX_SLICE_LENGTH 100000000
namespace CoreTiming
{
struct EventType {
TimedCallback callback;
const char *name;
};
namespace CoreTiming {
static std::vector<EventType> event_types;
// Only used during restore.
@ -54,14 +48,6 @@ static std::set<int> usedEventTypes;
static std::set<int> restoredEventTypes;
static int nextEventTypeRestoreId = -1;
struct BaseEvent {
s64 time;
u64 userdata;
int type;
};
typedef LinkedListItem<BaseEvent> Event;
Event *first;
Event *eventPool = 0;
@ -120,6 +106,14 @@ u64 GetGlobalTimeUs() {
return lastGlobalTimeUs + usSinceLast;
}
const Event *GetFirstEvent() {
return first;
}
const std::vector<EventType> &GetEventTypes() {
return event_types;
}
Event* GetNewEvent()
{
if(!eventPool)

View File

@ -18,7 +18,9 @@
#pragma once
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Data/Collections/LinkedList.h"
// This is a system to schedule events into the emulated machine's future. Time is measured
// in main CPU clock cycles.
@ -70,14 +72,25 @@ inline s64 cyclesToUs(s64 cycles) {
return (cycles * 1000000) / CPU_HZ;
}
namespace CoreTiming
{
void Init();
void Shutdown();
namespace CoreTiming {
typedef void (*MHzChangeCallback)();
typedef void (*TimedCallback)(u64 userdata, int cyclesLate);
struct EventType {
TimedCallback callback;
const char *name;
};
struct BaseEvent {
s64 time;
u64 userdata;
int type;
};
typedef LinkedListItem<BaseEvent> Event;
void Init();
void Shutdown();
u64 GetTicks();
u64 GetIdleTicks();
u64 GetGlobalTimeUs();
@ -85,6 +98,7 @@ namespace CoreTiming
// Returns the event_type identifier.
int RegisterEvent(const char *name, TimedCallback callback);
// For save states.
void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback);
void UnregisterAllEvents();
@ -94,6 +108,8 @@ namespace CoreTiming
void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata=0);
s64 UnscheduleEvent(int event_type, u64 userdata);
const std::vector<EventType> &GetEventTypes();
const Event *GetFirstEvent();
void RemoveEvent(int event_type);
bool IsScheduled(int event_type);
void Advance();
@ -116,6 +132,8 @@ namespace CoreTiming
void SetClockFrequencyHz(int cpuHz);
int GetClockFrequencyHz();
// TODO: Add accessors?
extern int slicelength;
}; // end of namespace

View File

@ -779,3 +779,18 @@ bool CreateSysDirectories() {
}
return true;
}
const char *CoreStateToString(CoreState state) {
switch (state) {
case CORE_RUNNING_CPU: return "RUNNING_CPU";
case CORE_NEXTFRAME: return "NEXTFRAME";
case CORE_STEPPING_CPU: return "STEPPING_CPU";
case CORE_POWERUP: return "POWERUP";
case CORE_POWERDOWN: return "POWERDOWN";
case CORE_BOOT_ERROR: return "BOOT_ERROR";
case CORE_RUNTIME_ERROR: return "RUNTIME_ERROR";
case CORE_STEPPING_GE: return "STEPPING_GE";
case CORE_RUNNING_GE: return "RUNNING_GE";
default: return "N/A";
}
}

View File

@ -130,6 +130,8 @@ enum CoreState {
CORE_RUNNING_GE,
};
const char *CoreStateToString(CoreState state);
extern bool coreCollectDebugStats;
extern volatile CoreState coreState;

View File

@ -44,6 +44,22 @@ static uint32_t g_skipPcOnce = 0;
static std::vector<std::pair<int, int>> restrictPrimRanges;
static std::string restrictPrimRule;
const char *BreakNextToString(BreakNext next) {
switch (next) {
case BreakNext::NONE: return "NONE,";
case BreakNext::OP: return "OP";
case BreakNext::DRAW: return "DRAW";
case BreakNext::TEX: return "TEX";
case BreakNext::NONTEX: return "NONTEX";
case BreakNext::FRAME: return "FRAME";
case BreakNext::VSYNC: return "VSYNC";
case BreakNext::PRIM: return "PRIM";
case BreakNext::CURVE: return "CURVE";
case BreakNext::COUNT: return "COUNT";
default: return "N/A";
}
}
static void Init() {
if (!inited) {
GPUBreakpoints::Init([](bool flag) {
@ -69,6 +85,10 @@ bool IsActive() {
return active;
}
BreakNext GetBreakNext() {
return breakNext;
}
void SetBreakNext(BreakNext next) {
SetActive(true);
breakNext = next;
@ -166,6 +186,7 @@ NotifyResult NotifyCommand(u32 pc) {
}
g_skipPcOnce = pc;
breakNext = BreakNext::NONE;
return NotifyResult::Break; // new. caller will call GPUStepping::EnterStepping().
}

View File

@ -39,6 +39,8 @@ bool IsActive();
void SetBreakNext(BreakNext next);
void SetBreakCount(int c, bool relative = false);
BreakNext GetBreakNext();
const char *BreakNextToString(BreakNext next);
enum class NotifyResult {
Execute,

View File

@ -26,7 +26,7 @@
#include "Core/HLE/sceAudiocodec.h"
#include "Core/HLE/sceMp3.h"
#include "Core/HLE/AtracCtx.h"
#include "Core/CoreTiming.h"
// Threads window
#include "Core/HLE/sceKernelThread.h"
@ -41,7 +41,36 @@
extern bool g_TakeScreenshot;
void DrawSchedulerView(ImConfig &cfg) {
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Event Scheduler", &cfg.schedulerOpen)) {
ImGui::End();
return;
}
s64 ticks = CoreTiming::GetTicks();
if (ImGui::BeginChild("event_list", ImVec2(300.0f, 0.0))) {
const CoreTiming::Event *event = CoreTiming::GetFirstEvent();
while (event) {
ImGui::Text("%s (%lld)", CoreTiming::GetEventTypes()[event->type].name, event->time - ticks);
event = event->next;
}
ImGui::EndChild();
}
ImGui::SameLine();
if (ImGui::BeginChild("general")) {
ImGui::Text("CoreState: %s", CoreStateToString(coreState));
ImGui::Text("downcount: %d", currentMIPS->downcount);
ImGui::Text("slicelength: %d", CoreTiming::slicelength);
ImGui::Text("Ticks: %lld", ticks);
ImGui::Text("Clock (MHz): %0.1f", (float)CoreTiming::GetClockFrequencyHz() / 1000000.0f);
ImGui::Text("Global time (us): %lld", CoreTiming::GetGlobalTimeUs());
ImGui::EndChild();
}
ImGui::End();
}
void DrawRegisterView(MIPSDebugInterface *mipsDebug, bool *open) {
ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Registers", open)) {
ImGui::End();
return;
@ -814,6 +843,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
ImGui::MenuItem("Registers", nullptr, &cfg_.regsOpen);
ImGui::MenuItem("Callstacks", nullptr, &cfg_.callstackOpen);
ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen);
ImGui::MenuItem("Scheduler", nullptr, &cfg_.schedulerOpen);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("HLE")) {
@ -932,6 +962,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
if (cfg_.geStateOpen) {
DrawGeStateWindow(cfg_, gpuDebug);
}
if (cfg_.schedulerOpen) {
DrawSchedulerView(cfg_);
}
}
void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, CoreState coreState) {
@ -1155,6 +1189,7 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) {
sync.Sync("debugStatsOpen", &debugStatsOpen, false);
sync.Sync("geDebuggerOpen", &geDebuggerOpen, false);
sync.Sync("geStateOpen", &geStateOpen, false);
sync.Sync("schedulerOpen", &schedulerOpen, false);
sync.SetSection(ini->GetOrCreateSection("Settings"));
sync.Sync("displayLatched", &displayLatched, false);

View File

@ -81,6 +81,7 @@ struct ImConfig {
bool debugStatsOpen;
bool geDebuggerOpen;
bool geStateOpen;
bool schedulerOpen;
// HLE explorer settings
// bool filterByUsed = true;

View File

@ -252,6 +252,12 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) {
// TODO: This doesn't work correctly.
// GPUDebug::SetBreakNext(GPUDebug::BreakNext::FRAME);
//}
bool disableStepButtons = GPUDebug::GetBreakNext() != GPUDebug::BreakNext::NONE;
if (disableStepButtons) {
ImGui::BeginDisabled();
}
ImGui::SameLine();
if (ImGui::Button("Tex")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX);
@ -276,6 +282,9 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) {
if (ImGui::Button("Single step")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP);
}
if (disableStepButtons) {
ImGui::EndDisabled();
}
// Line break
if (ImGui::Button("Goto PC")) {
@ -290,6 +299,11 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) {
ImGui::EndPopup();
}
// Display any pending step event.
if (GPUDebug::GetBreakNext() != GPUDebug::BreakNext::NONE) {
ImGui::Text("Step pending (waiting for CPU): %s", GPUDebug::BreakNextToString(GPUDebug::GetBreakNext()));
}
// Let's display the current CLUT.
// First, let's list any active display lists in the left column.