Merge pull request #4144 from unknownbrackets/debugger

Debugger changes, mostly reorganizing
This commit is contained in:
Henrik Rydgård 2013-10-12 10:40:39 -07:00
commit 1f99be4131
14 changed files with 692 additions and 210 deletions

View File

@ -1004,6 +1004,10 @@ add_library(GPU OBJECT
GPU/Common/PostShader.cpp
GPU/Common/PostShader.h
GPU/Common/SplineCommon.h
GPU/Debugger/Breakpoints.cpp
GPU/Debugger/Breakpoints.h
GPU/Debugger/Stepping.cpp
GPU/Debugger/Stepping.h
GPU/GLES/GLES_GPU.cpp
GPU/GLES/GLES_GPU.h
GPU/GLES/FragmentShaderGenerator.cpp

View File

@ -141,8 +141,7 @@ static inline void UpdateRunLoop() {
NativeRender();
}
void Core_RunLoop()
{
void Core_RunLoop() {
while (globalUIState != UISTATE_INGAME && globalUIState != UISTATE_EXIT) {
time_update();
@ -162,7 +161,7 @@ void Core_RunLoop()
#endif
}
while (!coreState) {
while (!coreState && globalUIState == UISTATE_INGAME) {
time_update();
UpdateRunLoop();
#ifdef _WIN32

View File

@ -321,8 +321,13 @@ int ElfReader::LoadInto(u32 loadAddress)
return SCE_KERNEL_ERROR_UNSUPPORTED_PRX_TYPE;
// technically ELFCLASSNONE would freeze the system, but that's not really desireable
if (header->e_ident[EI_CLASS] != ELFCLASS32)
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
if (header->e_ident[EI_CLASS] != ELFCLASS32) {
if (header->e_ident[EI_CLASS] != 0) {
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
}
ERROR_LOG(LOADER, "Bad ELF, EI_CLASS (fifth byte) is 0x00, should be 0x01 - would lock up a PSP.");
}
if (header->e_ident[EI_DATA] != ELFDATA2LSB)
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;

View File

@ -26,7 +26,7 @@ char* GetAssembleError()
void SplitLine(const char* Line, char* Name, char* Arguments)
{
while (*Line == ' ' || *Line == '\t') Line++;
while (*Line != ' ')
while (*Line != ' ' && *Line != '\t')
{
if (*Line == 0)
{

View File

@ -0,0 +1,322 @@
// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <vector>
#include <set>
#include "base/mutex.h"
#include "GPU/Debugger/Breakpoints.h"
#include "GPU/GPUState.h"
namespace GPUBreakpoints {
static recursive_mutex breaksLock;
static std::vector<bool> breakCmds;
static std::set<u32> breakPCs;
static std::set<u32> breakTextures;
// Small optimization to avoid a lock/lookup for the common case.
static size_t breakPCsCount = 0;
static size_t breakTexturesCount = 0;
// If these are set, the above are also, but they should be temporary.
static std::vector<bool> breakCmdsTemp;
static std::set<u32> breakPCsTemp;
static std::set<u32> breakTexturesTemp;
static bool textureChangeTemp = false;
static u32 lastTexture = 0xFFFFFFFF;
// These are commands we run before breaking on a texture.
// They are commands that affect the decoding of the texture.
const static u8 textureRelatedCmds[] = {
GE_CMD_TEXADDR0, GE_CMD_TEXADDR1, GE_CMD_TEXADDR2, GE_CMD_TEXADDR3, GE_CMD_TEXADDR4, GE_CMD_TEXADDR5, GE_CMD_TEXADDR6, GE_CMD_TEXADDR7,
GE_CMD_TEXBUFWIDTH0, GE_CMD_TEXBUFWIDTH1, GE_CMD_TEXBUFWIDTH2, GE_CMD_TEXBUFWIDTH3, GE_CMD_TEXBUFWIDTH4, GE_CMD_TEXBUFWIDTH5, GE_CMD_TEXBUFWIDTH6, GE_CMD_TEXBUFWIDTH7,
GE_CMD_TEXSIZE0, GE_CMD_TEXSIZE1, GE_CMD_TEXSIZE2, GE_CMD_TEXSIZE3, GE_CMD_TEXSIZE4, GE_CMD_TEXSIZE5, GE_CMD_TEXSIZE6, GE_CMD_TEXSIZE7,
GE_CMD_CLUTADDR, GE_CMD_CLUTADDRUPPER, GE_CMD_LOADCLUT, GE_CMD_CLUTFORMAT,
GE_CMD_TEXFORMAT, GE_CMD_TEXMODE, GE_CMD_TEXTUREMAPENABLE,
// Sometimes found between clut/texture params.
GE_CMD_TEXFLUSH, GE_CMD_TEXSYNC,
};
static std::vector<bool> nonTextureCmds;
void Init() {
ClearAllBreakpoints();
nonTextureCmds.clear();
nonTextureCmds.resize(256, true);
for (size_t i = 0; i < ARRAY_SIZE(textureRelatedCmds); ++i) {
nonTextureCmds[textureRelatedCmds[i]] = false;
}
}
void AddNonTextureTempBreakpoints() {
for (int i = 0; i < 256; ++i) {
if (nonTextureCmds[i]) {
AddCmdBreakpoint(i, true);
}
}
}
u32 GetAdjustedTextureAddress(u32 op) {
const u8 cmd = op >> 24;
bool interesting = (cmd >= GE_CMD_TEXADDR0 && cmd <= GE_CMD_TEXADDR7);
interesting = interesting || (cmd >= GE_CMD_TEXBUFWIDTH0 && cmd <= GE_CMD_TEXBUFWIDTH7);
if (!interesting) {
return (u32)-1;
}
int level = cmd <= GE_CMD_TEXADDR7 ? cmd - GE_CMD_TEXADDR0 : cmd - GE_CMD_TEXBUFWIDTH0;
u32 addr;
// Okay, so would this op modify the low or high part?
if (cmd <= GE_CMD_TEXADDR7) {
addr = (op & 0xFFFFF0) | ((gstate.texbufwidth[level] << 8) & 0x0F000000);
} else {
addr = (gstate.texaddr[level] & 0xFFFFF0) | ((op << 8) & 0x0F000000);
}
return addr;
}
bool IsTextureChangeBreakpoint(u32 op, u32 addr) {
if (!textureChangeTemp) {
return false;
}
const u8 cmd = op >> 24;
bool enabled = gstate.isTextureMapEnabled();
// Only for level 0.
if (cmd != GE_CMD_TEXADDR0 && cmd != GE_CMD_TEXBUFWIDTH0) {
// But we don't break when it's not enabled.
if (cmd == GE_CMD_TEXTUREMAPENABLE) {
enabled = (op & 1) != 0;
} else {
return false;
}
}
if (enabled && addr != lastTexture) {
textureChangeTemp = false;
lastTexture = addr;
return true;
} else {
return false;
}
}
bool IsTextureCmdBreakpoint(u32 op) {
const u32 addr = GetAdjustedTextureAddress(op);
if (addr != (u32)-1) {
return IsTextureChangeBreakpoint(op, addr) || IsTextureBreakpoint(addr);
} else {
return IsTextureChangeBreakpoint(op, gstate.getTextureAddress(0));
}
}
bool IsBreakpoint(u32 pc, u32 op) {
if (IsAddressBreakpoint(pc) || IsOpBreakpoint(op)) {
return true;
}
if ((breakTexturesCount != 0 || textureChangeTemp) && IsTextureCmdBreakpoint(op)) {
// Break on the next non-texture.
AddNonTextureTempBreakpoints();
}
return false;
}
bool IsAddressBreakpoint(u32 addr, bool &temp) {
if (breakPCsCount == 0) {
temp = false;
return false;
}
lock_guard guard(breaksLock);
temp = breakPCsTemp.find(addr) != breakPCsTemp.end();
return breakPCs.find(addr) != breakPCs.end();
}
bool IsAddressBreakpoint(u32 addr) {
if (breakPCsCount == 0) {
return false;
}
lock_guard guard(breaksLock);
return breakPCs.find(addr) != breakPCs.end();
}
bool IsTextureBreakpoint(u32 addr, bool &temp) {
if (breakTexturesCount == 0) {
temp = false;
return false;
}
lock_guard guard(breaksLock);
temp = breakTexturesTemp.find(addr) != breakTexturesTemp.end();
return breakTextures.find(addr) != breakTextures.end();
}
bool IsTextureBreakpoint(u32 addr) {
if (breakTexturesCount == 0) {
return false;
}
lock_guard guard(breaksLock);
return breakTextures.find(addr) != breakTextures.end();
}
bool IsCmdBreakpoint(u8 cmd, bool &temp) {
temp = breakCmdsTemp[cmd];
return breakCmds[cmd];
}
bool IsCmdBreakpoint(u8 cmd) {
return breakCmds[cmd];
}
void AddAddressBreakpoint(u32 addr, bool temp) {
lock_guard guard(breaksLock);
if (temp) {
if (breakPCs.find(addr) == breakPCs.end()) {
breakPCsTemp.insert(addr);
breakPCs.insert(addr);
}
// Already normal breakpoint, let's not make it temporary.
} else {
// Remove the temporary marking.
breakPCsTemp.erase(addr);
breakPCs.insert(addr);
}
breakPCsCount = breakPCs.size();
}
void AddCmdBreakpoint(u8 cmd, bool temp) {
if (temp) {
if (!breakCmds[cmd]) {
breakCmdsTemp[cmd] = true;
breakCmds[cmd] = true;
}
// Ignore adding a temp breakpoint when a normal one exists.
} else {
// This is no longer temporary.
breakCmdsTemp[cmd] = false;
breakCmds[cmd] = true;
}
}
void AddTextureBreakpoint(u32 addr, bool temp) {
lock_guard guard(breaksLock);
if (temp) {
if (breakTextures.find(addr) == breakTextures.end()) {
breakTexturesTemp.insert(addr);
breakTextures.insert(addr);
}
} else {
breakTexturesTemp.erase(addr);
breakTextures.insert(addr);
}
breakTexturesCount = breakTextures.size();
}
void AddTextureChangeTempBreakpoint() {
textureChangeTemp = true;
}
void RemoveAddressBreakpoint(u32 addr) {
lock_guard guard(breaksLock);
breakPCsTemp.erase(addr);
breakPCs.erase(addr);
breakPCsCount = breakPCs.size();
}
void RemoveCmdBreakpoint(u8 cmd) {
breakCmdsTemp[cmd] = false;
breakCmds[cmd] = false;
}
void RemoveTextureBreakpoint(u32 addr) {
lock_guard guard(breaksLock);
breakTexturesTemp.erase(addr);
breakTextures.erase(addr);
breakTexturesCount = breakTextures.size();
}
void RemoveTextureChangeTempBreakpoint() {
textureChangeTemp = false;
}
void UpdateLastTexture(u32 addr) {
lastTexture = addr;
}
void ClearAllBreakpoints() {
lock_guard guard(breaksLock);
breakCmds.clear();
breakCmds.resize(256, false);
breakPCs.clear();
breakTextures.clear();
breakCmdsTemp.clear();
breakCmdsTemp.resize(256, false);
breakPCsTemp.clear();
breakTexturesTemp.clear();
breakPCsCount = breakPCs.size();
breakTexturesCount = breakTextures.size();
textureChangeTemp = false;
}
void ClearTempBreakpoints() {
lock_guard guard(breaksLock);
// Reset ones that were temporary back to non-breakpoints in the primary arrays.
for (int i = 0; i < 256; ++i) {
if (breakCmdsTemp[i]) {
breakCmds[i] = false;
breakCmdsTemp[i] = false;
}
}
for (auto it = breakPCsTemp.begin(), end = breakPCsTemp.end(); it != end; ++it) {
breakPCs.erase(*it);
}
breakPCsTemp.clear();
breakPCsCount = breakPCs.size();
for (auto it = breakTexturesTemp.begin(), end = breakTexturesTemp.end(); it != end; ++it) {
breakTextures.erase(*it);
}
breakTexturesTemp.clear();
breakPCsCount = breakTextures.size();
textureChangeTemp = false;
}
};

View File

@ -0,0 +1,56 @@
// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/CommonTypes.h"
namespace GPUBreakpoints {
void Init();
bool IsBreakpoint(u32 pc, u32 op);
bool IsAddressBreakpoint(u32 addr, bool &temp);
bool IsAddressBreakpoint(u32 addr);
bool IsCmdBreakpoint(u8 cmd, bool &temp);
bool IsCmdBreakpoint(u8 cmd);
bool IsTextureBreakpoint(u32 addr, bool &temp);
bool IsTextureBreakpoint(u32 addr);
void AddAddressBreakpoint(u32 addr, bool temp = false);
void AddCmdBreakpoint(u8 cmd, bool temp = false);
void AddTextureBreakpoint(u32 addr, bool temp = false);
void AddTextureChangeTempBreakpoint();
void RemoveAddressBreakpoint(u32 addr);
void RemoveCmdBreakpoint(u8 cmd);
void RemoveTextureBreakpoint(u32 addr);
void RemoveTextureChangeTempBreakpoint();
void UpdateLastTexture(u32 addr);
void ClearAllBreakpoints();
void ClearTempBreakpoints();
static inline bool IsOpBreakpoint(u32 op, bool &temp) {
return IsCmdBreakpoint(op >> 24, temp);
}
static inline bool IsOpBreakpoint(u32 op) {
return IsCmdBreakpoint(op >> 24);
}
};

184
GPU/Debugger/Stepping.cpp Normal file
View File

@ -0,0 +1,184 @@
// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "base/mutex.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Debugger/Stepping.h"
#include "Core/Core.h"
namespace GPUStepping {
enum PauseAction {
PAUSE_CONTINUE,
PAUSE_BREAK,
PAUSE_GETFRAMEBUF,
PAUSE_GETDEPTHBUF,
PAUSE_GETSTENCILBUF,
PAUSE_GETTEX,
PAUSE_SETCMDVALUE,
};
static bool isStepping;
static recursive_mutex pauseLock;
static condition_variable pauseWait;
static PauseAction pauseAction = PAUSE_CONTINUE;
static recursive_mutex actionLock;
static condition_variable actionWait;
// In case of accidental wakeup.
static bool actionComplete;
// Many things need to run on the GPU thread. For example, reading the framebuffer.
// A message system is used to achieve this (temporarily "unpausing" the thread.)
// Below are values used to perform actions that return results.
static bool bufferResult;
static GPUDebugBuffer bufferFrame;
static GPUDebugBuffer bufferDepth;
static GPUDebugBuffer bufferStencil;
static GPUDebugBuffer bufferTex;
static u32 pauseSetCmdValue;
static void SetPauseAction(PauseAction act, bool waitComplete = true) {
{
lock_guard guard(pauseLock);
actionLock.lock();
pauseAction = act;
}
actionComplete = false;
pauseWait.notify_one();
while (waitComplete && !actionComplete) {
actionWait.wait(actionLock);
}
actionLock.unlock();
}
static void RunPauseAction() {
lock_guard guard(actionLock);
switch (pauseAction) {
case PAUSE_CONTINUE:
// Don't notify, just go back, woke up by accident.
return;
case PAUSE_BREAK:
break;
case PAUSE_GETFRAMEBUF:
bufferResult = gpuDebug->GetCurrentFramebuffer(bufferFrame);
break;
case PAUSE_GETDEPTHBUF:
bufferResult = gpuDebug->GetCurrentDepthbuffer(bufferDepth);
break;
case PAUSE_GETSTENCILBUF:
bufferResult = gpuDebug->GetCurrentStencilbuffer(bufferStencil);
break;
case PAUSE_GETTEX:
bufferResult = gpuDebug->GetCurrentTexture(bufferTex);
break;
case PAUSE_SETCMDVALUE:
gpuDebug->SetCmdValue(pauseSetCmdValue);
break;
default:
ERROR_LOG(HLE, "Unsupported pause action, forgot to add it to the switch.");
}
actionComplete = true;
actionWait.notify_one();
pauseAction = PAUSE_BREAK;
}
bool EnterStepping(std::function<void()> callback) {
lock_guard guard(pauseLock);
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) {
// Shutting down, don't try to step.
return false;
}
if (!gpuDebug) {
return false;
}
// Just to be sure.
if (pauseAction == PAUSE_CONTINUE) {
pauseAction = PAUSE_BREAK;
}
isStepping = true;
callback();
do {
RunPauseAction();
pauseWait.wait(pauseLock);
} while (pauseAction != PAUSE_CONTINUE);
isStepping = false;
return true;
}
static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) {
if (!isStepping) {
return false;
}
SetPauseAction(type);
buffer = &resultBuffer;
return bufferResult;
}
bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer) {
return GetBuffer(buffer, PAUSE_GETFRAMEBUF, bufferFrame);
}
bool GPU_GetCurrentDepthbuffer(const GPUDebugBuffer *&buffer) {
return GetBuffer(buffer, PAUSE_GETDEPTHBUF, bufferDepth);
}
bool GPU_GetCurrentStencilbuffer(const GPUDebugBuffer *&buffer) {
return GetBuffer(buffer, PAUSE_GETSTENCILBUF, bufferStencil);
}
bool GPU_GetCurrentTexture(const GPUDebugBuffer *&buffer) {
return GetBuffer(buffer, PAUSE_GETTEX, bufferTex);
}
bool GPU_SetCmdValue(u32 op) {
if (!isStepping) {
return false;
}
pauseSetCmdValue = op;
SetPauseAction(PAUSE_SETCMDVALUE);
return true;
}
void ResumeFromStepping() {
SetPauseAction(PAUSE_CONTINUE, false);
}
void ForceUnpause() {
SetPauseAction(PAUSE_CONTINUE, false);
actionComplete = true;
actionWait.notify_one();
}
};

38
GPU/Debugger/Stepping.h Normal file
View File

@ -0,0 +1,38 @@
// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "base/functional.h"
#include "Common/CommonTypes.h"
#include "GPU/Common/GPUDebugInterface.h"
namespace GPUStepping {
// Should be called from the GPU thread.
// Begins stepping and calls callback while inside a lock preparing stepping.
// This would be a good place to deliver a message to code that stepping is ready.
bool EnterStepping(std::function<void()> callback);
bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer);
bool GPU_GetCurrentDepthbuffer(const GPUDebugBuffer *&buffer);
bool GPU_GetCurrentStencilbuffer(const GPUDebugBuffer *&buffer);
bool GPU_GetCurrentTexture(const GPUDebugBuffer *&buffer);
bool GPU_SetCmdValue(u32 op);
void ResumeFromStepping();
void ForceUnpause();
};

View File

@ -160,6 +160,8 @@
<ClInclude Include="Common\PostShader.h" />
<ClInclude Include="Common\SplineCommon.h" />
<ClInclude Include="Common\VertexDecoderCommon.h" />
<ClInclude Include="Debugger\Breakpoints.h" />
<ClInclude Include="Debugger\Stepping.h" />
<ClInclude Include="Directx9\GPU_DX9.h" />
<ClInclude Include="Directx9\helper\dx_state.h" />
<ClInclude Include="Directx9\helper\fbo.h" />
@ -203,6 +205,8 @@
<ClCompile Include="Common\IndexGenerator.cpp" />
<ClCompile Include="Common\PostShader.cpp" />
<ClCompile Include="Common\VertexDecoderCommon.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp" />
<ClCompile Include="Debugger\Stepping.cpp" />
<ClCompile Include="Directx9\GPU_DX9.cpp" />
<ClCompile Include="Directx9\helper\dx_state.cpp" />
<ClCompile Include="Directx9\helper\fbo.cpp" />

View File

@ -19,6 +19,9 @@
<Filter Include="DirectX9\helper">
<UniqueIdentifier>{ba434472-5d5e-4b08-ab16-bfb7ec8e7068}</UniqueIdentifier>
</Filter>
<Filter Include="Debugger">
<UniqueIdentifier>{0cbddc00-4aa3-41d0-bed2-a454d37f838e}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ge_constants.h">
@ -148,6 +151,12 @@
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Common\PostShader.h" />
<ClInclude Include="Debugger\Breakpoints.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="Debugger\Stepping.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@ -268,6 +277,12 @@
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="Common\PostShader.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\Stepping.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View File

@ -3,6 +3,7 @@
#include "Windows/InputBox.h"
#include "Windows/Main.h"
#include "Core/Config.h"
#include "GPU/Debugger/Breakpoints.h"
#include <algorithm>
const PTCHAR CtrlDisplayListView::windowClass = _T("CtrlDisplayListView");
@ -205,7 +206,7 @@ void CtrlDisplayListView::onPaint(WPARAM wParam, LPARAM lParam)
DeleteObject(backgroundPen);
// display address/symbol
if (CGEDebugger::IsAddressBreakPoint(address))
if (GPUBreakpoints::IsAddressBreakpoint(address))
{
textColor = 0x0000FF;
int yOffset = std::max(-1,(rowHeight-14+1)/2);

View File

@ -19,7 +19,8 @@
#include <string>
#include <set>
#include "native/base/mutex.h"
#include "base/functional.h"
#include "base/mutex.h"
#include "Windows/GEDebugger/GEDebugger.h"
#include "Windows/GEDebugger/SimpleGLWindow.h"
#include "Windows/GEDebugger/CtrlDisplayListView.h"
@ -32,44 +33,20 @@
#include "GPU/GPUInterface.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/GPUState.h"
#include "GPU/Debugger/Breakpoints.h"
#include "GPU/Debugger/Stepping.h"
#include "Core/Config.h"
#include <windowsx.h>
#include <commctrl.h>
enum PauseAction {
PAUSE_CONTINUE,
PAUSE_BREAK,
PAUSE_GETFRAMEBUF,
PAUSE_GETDEPTHBUF,
PAUSE_GETSTENCILBUF,
PAUSE_GETTEX,
PAUSE_SETCMDVALUE,
};
using namespace GPUBreakpoints;
using namespace GPUStepping;
static bool attached = false;
// TODO
static bool textureCaching = true;
static recursive_mutex pauseLock;
static condition_variable pauseWait;
static PauseAction pauseAction = PAUSE_CONTINUE;
static recursive_mutex actionLock;
static condition_variable actionWait;
static bool actionComplete;
static recursive_mutex breaksLock;
static std::vector<bool> breakCmds;
static std::set<u32> breakPCs;
static u32 tempBreakpoint = -1;
static std::set<u32> breakTextures;
static BreakNextType breakNext = BREAK_NONE;
static u32 lastTexture = -1;
static bool bufferResult;
static GPUDebugBuffer bufferFrame;
static GPUDebugBuffer bufferDepth;
static GPUDebugBuffer bufferStencil;
static GPUDebugBuffer bufferTex;
static u32 pauseSetCmdValue;
enum PrimaryDisplayType {
PRIMARY_FRAMEBUF,
@ -77,129 +54,14 @@ enum PrimaryDisplayType {
PRIMARY_STENCILBUF,
};
// TODO: Simplify and move out of windows stuff, just block in a common way for everyone.
void CGEDebugger::Init() {
SimpleGLWindow::RegisterClass();
CtrlDisplayListView::registerClass();
}
bool CGEDebugger::IsAddressBreakPoint(u32 pc) {
lock_guard guard(breaksLock);
return breakPCs.find(pc) != breakPCs.end();
}
bool CGEDebugger::IsOpBreakPoint(u32 op) {
u8 cmd = op >> 24;
return breakCmds[cmd];
}
// Hmm, this is probably kinda slow now...
bool CGEDebugger::IsTextureBreakPoint(u32 op) {
u8 cmd = op >> 24;
bool interesting = (cmd >= GE_CMD_TEXADDR0 && cmd <= GE_CMD_TEXADDR7);
interesting = interesting || (cmd >= GE_CMD_TEXBUFWIDTH0 && cmd <= GE_CMD_TEXBUFWIDTH7);
if (breakNext == BREAK_NEXT_NONTEX) {
// Okay, so we hit an interesting texture, but let's not break if this is a clut/etc.
// Otherwise we get garbage widths and colors. It's annoying.
bool textureCmd = interesting || cmd == GE_CMD_CLUTADDR || cmd == GE_CMD_CLUTADDRUPPER || cmd == GE_CMD_LOADCLUT || cmd == GE_CMD_CLUTFORMAT;
textureCmd = textureCmd || (cmd >= GE_CMD_TEXSIZE0 && cmd <= GE_CMD_TEXSIZE7);
textureCmd = textureCmd || cmd == GE_CMD_TEXFORMAT || cmd == GE_CMD_TEXMODE || cmd == GE_CMD_TEXTUREMAPENABLE;
if (!textureCmd) {
return true;
}
}
if (!interesting || !gpuDebug) {
return false;
}
// Okay, so we just set a texture of some sort, check if it was one we were waiting for.
auto state = gpuDebug->GetGState();
int level = cmd <= GE_CMD_TEXADDR7 ? cmd - GE_CMD_TEXADDR0 : cmd - GE_CMD_TEXBUFWIDTH0;
// Are we breaking on any texture? As long as it's level 0.
if (level == 0 && breakNext == BREAK_NEXT_TEX && lastTexture != state.getTextureAddress(level)) {
// Don't break right away, we'll get a garbage texture...
breakNext = BREAK_NEXT_NONTEX;
}
lock_guard guard(breaksLock);
if (breakTextures.find(state.getTextureAddress(level)) != breakTextures.end() && breakNext == BREAK_NONE) {
breakNext = BREAK_NEXT_NONTEX;
}
return false;
}
bool CGEDebugger::IsOpOrTextureBreakPoint(u32 op) {
return IsOpBreakPoint(op) || IsTextureBreakPoint(op);
}
static void SetPauseAction(PauseAction act, bool waitComplete = true) {
{
lock_guard guard(pauseLock);
actionLock.lock();
pauseAction = act;
}
actionComplete = false;
pauseWait.notify_one();
while (waitComplete && !actionComplete) {
actionWait.wait(actionLock);
}
actionLock.unlock();
}
static void RunPauseAction() {
lock_guard guard(actionLock);
switch (pauseAction) {
case PAUSE_CONTINUE:
// Don't notify, just go back, woke up by accident.
return;
case PAUSE_BREAK:
break;
case PAUSE_GETFRAMEBUF:
bufferResult = gpuDebug->GetCurrentFramebuffer(bufferFrame);
break;
case PAUSE_GETDEPTHBUF:
bufferResult = gpuDebug->GetCurrentDepthbuffer(bufferDepth);
break;
case PAUSE_GETSTENCILBUF:
bufferResult = gpuDebug->GetCurrentStencilbuffer(bufferStencil);
break;
case PAUSE_GETTEX:
bufferResult = gpuDebug->GetCurrentTexture(bufferTex);
break;
case PAUSE_SETCMDVALUE:
gpuDebug->SetCmdValue(pauseSetCmdValue);
break;
default:
ERROR_LOG(HLE, "Unsupported pause action, forgot to add it to the switch.");
}
actionComplete = true;
actionWait.notify_one();
pauseAction = PAUSE_BREAK;
}
static void ForceUnpause() {
SetPauseAction(PAUSE_CONTINUE, false);
actionComplete = true;
actionWait.notify_one();
}
CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
: Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent), frameWindow(NULL), texWindow(NULL) {
breakCmds.resize(256, false);
GPUBreakpoints::Init();
Core_ListenShutdown(ForceUnpause);
// minimum size = a little more than the default
@ -282,9 +144,9 @@ void CGEDebugger::UpdatePreviews() {
// TODO: Do something different if not paused?
wchar_t desc[256];
GPUDebugBuffer *primaryBuffer = NULL;
const GPUDebugBuffer *primaryBuffer = NULL;
bool bufferResult = false;
GPUgstate state;
bufferResult = false;
if (gpuDebug != NULL) {
state = gpuDebug->GetGState();
@ -292,21 +154,24 @@ void CGEDebugger::UpdatePreviews() {
switch (PrimaryDisplayType(fbTabs->CurrentTabIndex())) {
case PRIMARY_FRAMEBUF:
SetPauseAction(PAUSE_GETFRAMEBUF);
primaryBuffer = &bufferFrame;
_snwprintf(desc, ARRAY_SIZE(desc), L"Color: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight());
bufferResult = GPU_GetCurrentFramebuffer(primaryBuffer);
if (bufferResult) {
_snwprintf(desc, ARRAY_SIZE(desc), L"Color: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight());
}
break;
case PRIMARY_DEPTHBUF:
SetPauseAction(PAUSE_GETDEPTHBUF);
primaryBuffer = &bufferDepth;
_snwprintf(desc, ARRAY_SIZE(desc), L"Depth: 0x%08x (%dx%d)", state.getDepthBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight());
bufferResult = GPU_GetCurrentDepthbuffer(primaryBuffer);
if (bufferResult) {
_snwprintf(desc, ARRAY_SIZE(desc), L"Depth: 0x%08x (%dx%d)", state.getDepthBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight());
}
break;
case PRIMARY_STENCILBUF:
SetPauseAction(PAUSE_GETSTENCILBUF);
primaryBuffer = &bufferStencil;
_snwprintf(desc, ARRAY_SIZE(desc), L"Stencil: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight());
bufferResult = GPU_GetCurrentStencilbuffer(primaryBuffer);
if (bufferResult) {
_snwprintf(desc, ARRAY_SIZE(desc), L"Stencil: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight());
}
break;
}
@ -319,12 +184,12 @@ void CGEDebugger::UpdatePreviews() {
SetDlgItemText(m_hDlg, IDC_GEDBG_FRAMEBUFADDR, L"Failed");
}
bufferResult = false;
SetPauseAction(PAUSE_GETTEX);
const GPUDebugBuffer *bufferTex = NULL;
bufferResult = GPU_GetCurrentTexture(bufferTex);
if (bufferResult) {
auto fmt = SimpleGLWindow::Format(bufferTex.GetFormat());
texWindow->Draw(bufferTex.GetData(), bufferTex.GetStride(), bufferTex.GetHeight(), bufferTex.GetFlipped(), fmt);
auto fmt = SimpleGLWindow::Format(bufferTex->GetFormat());
texWindow->Draw(bufferTex->GetData(), bufferTex->GetStride(), bufferTex->GetHeight(), bufferTex->GetFlipped(), fmt);
if (gpuDebug != NULL) {
if (state.isTextureAlphaUsed()) {
@ -335,9 +200,9 @@ void CGEDebugger::UpdatePreviews() {
_snwprintf(desc, ARRAY_SIZE(desc), L"Texture: 0x%08x (%dx%d)", state.getTextureAddress(0), state.getTextureWidth(0), state.getTextureHeight(0));
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, desc);
lastTexture = state.getTextureAddress(0);
UpdateLastTexture(state.getTextureAddress(0));
} else {
lastTexture = -1;
UpdateLastTexture((u32)-1);
}
} else {
texWindow->Clear();
@ -346,7 +211,7 @@ void CGEDebugger::UpdatePreviews() {
} else {
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L"Texture: disabled");
}
lastTexture = -1;
UpdateLastTexture((u32)-1);
}
DisplayList list;
@ -391,7 +256,7 @@ void CGEDebugger::SetBreakNext(BreakNextType type) {
attached = true;
SetupPreviews();
SetPauseAction(PAUSE_CONTINUE, false);
ResumeFromStepping();
breakNext = type;
}
@ -458,6 +323,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case IDC_GEDBG_STEPTEX:
AddTextureChangeTempBreakpoint();
SetBreakNext(BREAK_NEXT_TEX);
break;
@ -467,13 +333,17 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case IDC_GEDBG_BREAKTEX:
{
u32 texAddr;
if (!gpuDebug) {
break;
}
const auto state = gpuDebug->GetGState();
u32 texAddr = state.getTextureAddress(0);
// TODO: Better interface that allows add/remove or something.
if (InputBox_GetHex(GetModuleHandle(NULL), m_hDlg, L"Texture Address", lastTexture, texAddr)) {
if (breakTextures.find(texAddr) != breakTextures.end()) {
breakTextures.erase(texAddr);
if (InputBox_GetHex(GetModuleHandle(NULL), m_hDlg, L"Texture Address", texAddr, texAddr)) {
if (IsTextureBreakpoint(texAddr)) {
RemoveTextureBreakpoint(texAddr);
} else {
breakTextures.insert(texAddr);
AddTextureBreakpoint(texAddr);
}
}
}
@ -486,7 +356,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L"");
// TODO: detach? Should probably have separate UI, or just on activate?
SetPauseAction(PAUSE_CONTINUE, false);
ResumeFromStepping();
breakNext = BREAK_NONE;
break;
}
@ -495,7 +365,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case WM_GEDBG_BREAK_CMD:
{
u32 pc = (u32)wParam;
tempBreakpoint = -1;
ClearTempBreakpoints();
auto info = gpuDebug->DissassembleOp(pc);
NOTICE_LOG(COMMON, "Waiting at %08x, %s", pc, info.desc.c_str());
UpdatePreviews();
@ -515,29 +385,28 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case WM_GEDBG_TOGGLEPCBREAKPOINT:
{
lock_guard guard(breaksLock);
u32 pc = (u32)wParam;
auto iter = breakPCs.find(pc);
if (iter != breakPCs.end())
breakPCs.erase(iter);
else
breakPCs.insert(pc);
bool temp;
bool isBreak = IsAddressBreakpoint(pc, temp);
if (isBreak && !temp) {
RemoveAddressBreakpoint(pc);
} else {
AddAddressBreakpoint(pc);
}
}
break;
case WM_GEDBG_RUNTOWPARAM:
{
lock_guard guard(breaksLock);
u32 pc = (u32)wParam;
tempBreakpoint = pc;
AddAddressBreakpoint(pc, true);
SendMessage(m_hDlg,WM_COMMAND,IDC_GEDBG_RESUME,0);
}
break;
case WM_GEDBG_SETCMDWPARAM:
{
pauseSetCmdValue = (u32)wParam;
SetPauseAction(PAUSE_SETCMDVALUE);
GPU_SetCmdValue((u32)wParam);
}
break;
}
@ -551,30 +420,19 @@ bool WindowsHost::GPUDebuggingActive() {
return attached;
}
static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) {
lock_guard guard(pauseLock);
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) {
return;
}
static void DeliverMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
PostMessage(geDebuggerWindow->GetDlgHandle(), msg, wParam, lParam);
}
// Just to be sure.
if (pauseAction == PAUSE_CONTINUE) {
pauseAction = PAUSE_BREAK;
}
do {
RunPauseAction();
pauseWait.wait(pauseLock);
} while (pauseAction != PAUSE_CONTINUE);
static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) {
EnterStepping(std::bind(&DeliverMessage, msg, wParam, lParam));
}
void WindowsHost::GPUNotifyCommand(u32 pc) {
u32 op = Memory::ReadUnchecked_U32(pc);
u8 cmd = op >> 24;
if (breakNext == BREAK_NEXT_OP || CGEDebugger::IsOpOrTextureBreakPoint(op) || CGEDebugger::IsAddressBreakPoint(pc) || pc == tempBreakpoint) {
if (breakNext == BREAK_NEXT_OP || IsBreakpoint(pc, op)) {
PauseWithMessage(WM_GEDBG_BREAK_CMD, (WPARAM) pc);
}
}

View File

@ -56,12 +56,6 @@ public:
static void Init();
static bool IsAddressBreakPoint(u32 pc);
static bool IsOpBreakPoint(u32 op);
static bool IsTextureBreakPoint(u32 op);
// Separate so the UI can just show op break points separately.
static bool IsOpOrTextureBreakPoint(u32 op);
protected:
BOOL DlgProc(UINT message, WPARAM wParam, LPARAM lParam);

View File

@ -197,6 +197,8 @@ LOCAL_SRC_FILES := \
$(SRC)/GPU/Common/VertexDecoderCommon.cpp.arm \
$(SRC)/GPU/Common/TextureDecoder.cpp \
$(SRC)/GPU/Common/PostShader.cpp \
$(SRC)/GPU/Debugger/Breakpoints.cpp \
$(SRC)/GPU/Debugger/Stepping.cpp \
$(SRC)/GPU/GLES/Framebuffer.cpp \
$(SRC)/GPU/GLES/GLES_GPU.cpp.arm \
$(SRC)/GPU/GLES/TextureCache.cpp.arm \