Debugger: Added performance profiler tool

This commit is contained in:
Sour 2020-02-08 17:08:33 -05:00
parent 3312871006
commit 0cf94db654
26 changed files with 1310 additions and 137 deletions

View file

@ -2,13 +2,19 @@
#include "CallstackManager.h" #include "CallstackManager.h"
#include "Debugger.h" #include "Debugger.h"
#include "DebugBreakHelper.h" #include "DebugBreakHelper.h"
#include "Profiler.h"
CallstackManager::CallstackManager(Debugger* debugger) CallstackManager::CallstackManager(Debugger* debugger)
{ {
_debugger = debugger; _debugger = debugger;
_profiler.reset(new Profiler(debugger));
} }
void CallstackManager::Push(uint32_t srcAddr, uint32_t destAddr, uint32_t returnAddress, StackFrameFlags flags) CallstackManager::~CallstackManager()
{
}
void CallstackManager::Push(AddressInfo &src, uint32_t srcAddr, AddressInfo& dest, uint32_t destAddr, AddressInfo& ret, uint32_t returnAddress, StackFrameFlags flags)
{ {
if(_callstack.size() >= 511) { if(_callstack.size() >= 511) {
//Ensure callstack stays below 512 entries - games can use various tricks that could keep making the callstack grow //Ensure callstack stays below 512 entries - games can use various tricks that could keep making the callstack grow
@ -20,13 +26,15 @@ void CallstackManager::Push(uint32_t srcAddr, uint32_t destAddr, uint32_t return
stackFrame.Target = destAddr; stackFrame.Target = destAddr;
stackFrame.Return = returnAddress; stackFrame.Return = returnAddress;
stackFrame.AbsReturn = ret;
stackFrame.Flags = flags; stackFrame.Flags = flags;
_callstack.push_back(stackFrame); _callstack.push_back(stackFrame);
_profiler->StackFunction(dest, flags);
} }
void CallstackManager::Pop(uint32_t destAddress) void CallstackManager::Pop(AddressInfo& dest, uint32_t destAddress)
{ {
if(_callstack.empty()) { if(_callstack.empty()) {
return; return;
@ -34,6 +42,7 @@ void CallstackManager::Pop(uint32_t destAddress)
StackFrameInfo prevFrame = _callstack.back(); StackFrameInfo prevFrame = _callstack.back();
_callstack.pop_back(); _callstack.pop_back();
_profiler->UnstackFunction();
uint32_t returnAddr = prevFrame.Return; uint32_t returnAddr = prevFrame.Return;
@ -46,6 +55,7 @@ void CallstackManager::Pop(uint32_t destAddress)
foundMatch = true; foundMatch = true;
for(int j = (int)_callstack.size() - i - 1; j >= 0; j--) { for(int j = (int)_callstack.size() - i - 1; j >= 0; j--) {
_callstack.pop_back(); _callstack.pop_back();
_profiler->UnstackFunction();
} }
break; break;
} }
@ -53,7 +63,7 @@ void CallstackManager::Pop(uint32_t destAddress)
if(!foundMatch) { if(!foundMatch) {
//Couldn't find a matching frame, replace the current one //Couldn't find a matching frame, replace the current one
Push(returnAddr, destAddress, returnAddr, StackFrameFlags::None); Push(prevFrame.AbsReturn, returnAddr, dest, destAddress, prevFrame.AbsReturn, returnAddr, StackFrameFlags::None);
} }
} }
} }
@ -77,3 +87,8 @@ int32_t CallstackManager::GetReturnAddress()
} }
return _callstack.back().Return; return _callstack.back().Return;
} }
Profiler* CallstackManager::GetProfiler()
{
return _profiler.get();
}

View file

@ -3,19 +3,23 @@
#include "DebugTypes.h" #include "DebugTypes.h"
class Debugger; class Debugger;
class Profiler;
class CallstackManager class CallstackManager
{ {
private: private:
Debugger* _debugger; Debugger* _debugger;
deque<StackFrameInfo> _callstack; deque<StackFrameInfo> _callstack;
unique_ptr<Profiler> _profiler;
public: public:
CallstackManager(Debugger* debugger); CallstackManager(Debugger* debugger);
~CallstackManager();
void Push(uint32_t srcAddr, uint32_t destAddr, uint32_t returnAddress, StackFrameFlags flags); void Push(AddressInfo& src, uint32_t srcAddr, AddressInfo& dest, uint32_t destAddr, AddressInfo& ret, uint32_t returnAddress, StackFrameFlags flags);
void Pop(uint32_t destAddr); void Pop(AddressInfo& dest, uint32_t destAddr);
void GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize); void GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize);
int32_t GetReturnAddress(); int32_t GetReturnAddress();
Profiler* GetProfiler();
}; };

View file

@ -127,6 +127,7 @@
<ClInclude Include="PcmReader.h" /> <ClInclude Include="PcmReader.h" />
<ClInclude Include="PlayerListMessage.h" /> <ClInclude Include="PlayerListMessage.h" />
<ClInclude Include="PpuTools.h" /> <ClInclude Include="PpuTools.h" />
<ClInclude Include="Profiler.h" />
<ClInclude Include="RecordedRomTest.h" /> <ClInclude Include="RecordedRomTest.h" />
<ClInclude Include="RegisterHandlerB.h" /> <ClInclude Include="RegisterHandlerB.h" />
<ClInclude Include="CpuTypes.h" /> <ClInclude Include="CpuTypes.h" />
@ -273,6 +274,7 @@
<ClCompile Include="PcmReader.cpp" /> <ClCompile Include="PcmReader.cpp" />
<ClCompile Include="Ppu.cpp" /> <ClCompile Include="Ppu.cpp" />
<ClCompile Include="PpuTools.cpp" /> <ClCompile Include="PpuTools.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="RecordedRomTest.cpp" /> <ClCompile Include="RecordedRomTest.cpp" />
<ClCompile Include="RegisterHandlerB.cpp" /> <ClCompile Include="RegisterHandlerB.cpp" />
<ClCompile Include="RewindData.cpp" /> <ClCompile Include="RewindData.cpp" />

View file

@ -488,6 +488,9 @@
<ClInclude Include="Sa1BwRamHandler.h"> <ClInclude Include="Sa1BwRamHandler.h">
<Filter>SNES\Coprocessors\SA1</Filter> <Filter>SNES\Coprocessors\SA1</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Profiler.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="stdafx.cpp" /> <ClCompile Include="stdafx.cpp" />
@ -773,6 +776,9 @@
<ClCompile Include="Msu1.cpp"> <ClCompile Include="Msu1.cpp">
<Filter>SNES\Coprocessors\MSU1</Filter> <Filter>SNES\Coprocessors\MSU1</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Profiler.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="SNES"> <Filter Include="SNES">

View file

@ -20,6 +20,7 @@
#include "Console.h" #include "Console.h"
#include "MemoryAccessCounter.h" #include "MemoryAccessCounter.h"
#include "ExpressionEvaluator.h" #include "ExpressionEvaluator.h"
#include "Profiler.h"
CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType) CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
{ {
@ -50,6 +51,7 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
void CpuDebugger::Reset() void CpuDebugger::Reset()
{ {
_enableBreakOnUninitRead = true; _enableBreakOnUninitRead = true;
_callstackManager.reset(new CallstackManager(_debugger));
_prevOpCode = 0xFF; _prevOpCode = 0xFF;
} }
@ -88,10 +90,12 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
//JSR, JSL //JSR, JSL
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, state.PS, _cpuType); uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, state.PS, _cpuType);
uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF); uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF);
_callstackManager->Push(_prevProgramCounter, pc, returnPc, StackFrameFlags::None); AddressInfo srcAddress = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
AddressInfo retAddress = GetMemoryMappings().GetAbsoluteAddress(returnPc);
_callstackManager->Push(srcAddress, _prevProgramCounter, addressInfo, pc, retAddress, returnPc, StackFrameFlags::None);
} else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) { } else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) {
//RTS, RTL, RTI //RTS, RTL, RTI
_callstackManager->Pop(pc); _callstackManager->Pop(addressInfo, pc);
} }
if(_step->BreakAddress == (int32_t)pc && (_prevOpCode == 0x60 || _prevOpCode == 0x40 || _prevOpCode == 0x6B || _prevOpCode == 0x44 || _prevOpCode == 0x54)) { if(_step->BreakAddress == (int32_t)pc && (_prevOpCode == 0x60 || _prevOpCode == 0x40 || _prevOpCode == 0x6B || _prevOpCode == 0x44 || _prevOpCode == 0x54)) {
@ -205,7 +209,10 @@ void CpuDebugger::Step(int32_t stepCount, StepType type)
void CpuDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) void CpuDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{ {
_callstackManager->Push(_prevProgramCounter, currentPc, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq); AddressInfo src = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
AddressInfo ret = GetMemoryMappings().GetAbsoluteAddress(originalPc);
AddressInfo dest = GetMemoryMappings().GetAbsoluteAddress(currentPc);
_callstackManager->Push(src, _prevProgramCounter, dest, currentPc, ret, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
_eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq); _eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq);
} }

View file

@ -221,6 +221,7 @@ struct StackFrameInfo
uint32_t Source; uint32_t Source;
uint32_t Target; uint32_t Target;
uint32_t Return; uint32_t Return;
AddressInfo AbsReturn;
StackFrameFlags Flags; StackFrameFlags Flags;
}; };

129
Core/Profiler.cpp Normal file
View file

@ -0,0 +1,129 @@
#include "stdafx.h"
#include <limits>
#include "Profiler.h"
#include "DebugBreakHelper.h"
#include "Debugger.h"
#include "Console.h"
#include "MemoryManager.h"
#include "MemoryDumper.h"
#include "DebugTypes.h"
Profiler::Profiler(Debugger* debugger)
{
_debugger = debugger;
_memoryManager = debugger->GetConsole()->GetMemoryManager().get();
InternalReset();
}
Profiler::~Profiler()
{
}
void Profiler::StackFunction(AddressInfo &addr, StackFrameFlags stackFlag)
{
UpdateCycles();
if(addr.Address >= 0) {
_stackFlags.push_back(stackFlag);
_cycleCountStack.push(_currentCycleCount);
_functionStack.push_back(_currentFunction);
uint32_t key = addr.Address | ((uint8_t)addr.Type << 24);
if(_functions.find(key) == _functions.end()) {
_functions[key] = ProfiledFunction();
_functions[key].Address = addr;
}
ProfiledFunction& func = _functions[key];
func.CallCount++;
_currentFunction = key;
_currentCycleCount = 0;
}
}
void Profiler::UpdateCycles()
{
uint64_t masterClock = _memoryManager->GetMasterClock();
ProfiledFunction& func = _functions[_currentFunction];
uint64_t clockGap = masterClock - _prevMasterClock;
func.ExclusiveCycles += clockGap;
func.InclusiveCycles += clockGap;
int32_t len = (int32_t)_functionStack.size();
for(int32_t i = len - 1; i >= 0; i--) {
_functions[_functionStack[i]].InclusiveCycles += clockGap;
if(_stackFlags[i] != StackFrameFlags::None) {
//Don't apply inclusive times to stack frames before an IRQ/NMI
break;
}
}
_currentCycleCount += clockGap;
_prevMasterClock = masterClock;
}
void Profiler::UnstackFunction()
{
UpdateCycles();
if(!_functionStack.empty()) {
//Return to the previous function
ProfiledFunction& func = _functions[_currentFunction];
func.MinCycles = std::min(func.MinCycles, _currentCycleCount);
func.MaxCycles = std::max(func.MaxCycles, _currentCycleCount);
_currentFunction = _functionStack.back();
_functionStack.pop_back();
StackFrameFlags stackFlag = _stackFlags.back();
_stackFlags.pop_back();
if(stackFlag == StackFrameFlags::None) {
//Prevent IRQ/NMI from adding cycles to the calling function
//Add the subroutine's cycle count to the parent function's inclusive cycle count
//_functions[_currentFunction].InclusiveCycles += _currentCycleCount;
}
//Add the subroutine's cycle count to the current routine's cycle count
_currentCycleCount = _cycleCountStack.top() + _currentCycleCount;
_cycleCountStack.pop();
}
}
void Profiler::Reset()
{
DebugBreakHelper helper(_debugger);
InternalReset();
}
void Profiler::InternalReset()
{
_prevMasterClock = _memoryManager->GetMasterClock();
_currentCycleCount = 0;
_currentFunction = Profiler::ResetFunctionIndex;
_functions.clear();
_functions[Profiler::ResetFunctionIndex] = ProfiledFunction();
_functions[Profiler::ResetFunctionIndex].Address = { Profiler::ResetFunctionIndex, SnesMemoryType::Register };
}
void Profiler::GetProfilerData(ProfiledFunction* profilerData, uint32_t& functionCount)
{
DebugBreakHelper helper(_debugger);
UpdateCycles();
functionCount = 0;
for(auto func : _functions) {
profilerData[functionCount] = func.second;
functionCount++;
if(functionCount >= 100000) {
break;
}
}
}

50
Core/Profiler.h Normal file
View file

@ -0,0 +1,50 @@
#pragma once
#include "stdafx.h"
#include <unordered_map>
#include <stack>
#include "DebugTypes.h"
class Debugger;
class MemoryManager;
struct ProfiledFunction
{
uint64_t ExclusiveCycles = 0;
uint64_t InclusiveCycles = 0;
uint64_t CallCount = 0;
uint64_t MinCycles = UINT64_MAX;
uint64_t MaxCycles = 0;
AddressInfo Address;
};
class Profiler
{
private:
static constexpr int32_t ResetFunctionIndex = -1;
Debugger* _debugger;
MemoryManager* _memoryManager;
unordered_map<int32_t, ProfiledFunction> _functions;
deque<int32_t> _functionStack;
deque<StackFrameFlags> _stackFlags;
std::stack<uint64_t> _cycleCountStack;
uint64_t _currentCycleCount;
uint64_t _prevMasterClock;
int32_t _currentFunction;
void InternalReset();
void UpdateCycles();
public:
Profiler(Debugger* debugger);
~Profiler();
void StackFunction(AddressInfo& addr, StackFrameFlags stackFlag);
void UnstackFunction();
void Reset();
void GetProfilerData(ProfiledFunction* profilerData, uint32_t& functionCount);
};

View file

@ -12,6 +12,7 @@
#include "MemoryAccessCounter.h" #include "MemoryAccessCounter.h"
#include "ExpressionEvaluator.h" #include "ExpressionEvaluator.h"
#include "EmuSettings.h" #include "EmuSettings.h"
#include "Profiler.h"
SpcDebugger::SpcDebugger(Debugger* debugger) SpcDebugger::SpcDebugger(Debugger* debugger)
{ {
@ -30,6 +31,7 @@ SpcDebugger::SpcDebugger(Debugger* debugger)
void SpcDebugger::Reset() void SpcDebugger::Reset()
{ {
_callstackManager.reset(new CallstackManager(_debugger));
_prevOpCode = 0xFF; _prevOpCode = 0xFF;
} }
@ -64,10 +66,12 @@ void SpcDebugger::ProcessRead(uint16_t addr, uint8_t value, MemoryOperationType
//JSR, BRK //JSR, BRK
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, 0, CpuType::Spc); uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, 0, CpuType::Spc);
uint16_t returnPc = _prevProgramCounter + opSize; uint16_t returnPc = _prevProgramCounter + opSize;
_callstackManager->Push(_prevProgramCounter, spcState.PC, returnPc, StackFrameFlags::None); AddressInfo src = _spc->GetAbsoluteAddress(_prevProgramCounter);
AddressInfo ret = _spc->GetAbsoluteAddress(returnPc);
_callstackManager->Push(src, _prevProgramCounter, addressInfo, spcState.PC, ret, returnPc, StackFrameFlags::None);
} else if(_prevOpCode == 0x6F || _prevOpCode == 0x7F) { } else if(_prevOpCode == 0x6F || _prevOpCode == 0x7F) {
//RTS, RTI //RTS, RTI
_callstackManager->Pop(spcState.PC); _callstackManager->Pop(addressInfo, spcState.PC);
} }
if(_step->BreakAddress == (int32_t)spcState.PC && (_prevOpCode == 0x6F || _prevOpCode == 0x7F)) { if(_step->BreakAddress == (int32_t)spcState.PC && (_prevOpCode == 0x6F || _prevOpCode == 0x7F)) {

View file

@ -14,6 +14,7 @@
#include "../Core/CallstackManager.h" #include "../Core/CallstackManager.h"
#include "../Core/LabelManager.h" #include "../Core/LabelManager.h"
#include "../Core/ScriptManager.h" #include "../Core/ScriptManager.h"
#include "../Core/Profiler.h"
extern shared_ptr<Console> _console; extern shared_ptr<Console> _console;
@ -59,6 +60,8 @@ extern "C"
DllExport void __stdcall SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { GetDebugger()->SetBreakpoints(breakpoints, length); } DllExport void __stdcall SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { GetDebugger()->SetBreakpoints(breakpoints, length); }
DllExport int32_t __stdcall EvaluateExpression(char* expression, CpuType cpuType, EvalResultType *resultType, bool useCache) { return GetDebugger()->EvaluateExpression(expression, cpuType, *resultType, useCache); } DllExport int32_t __stdcall EvaluateExpression(char* expression, CpuType cpuType, EvalResultType *resultType, bool useCache) { return GetDebugger()->EvaluateExpression(expression, cpuType, *resultType, useCache); }
DllExport void __stdcall GetCallstack(CpuType cpuType, StackFrameInfo *callstackArray, uint32_t &callstackSize) { GetDebugger()->GetCallstackManager(cpuType)->GetCallstack(callstackArray, callstackSize); } DllExport void __stdcall GetCallstack(CpuType cpuType, StackFrameInfo *callstackArray, uint32_t &callstackSize) { GetDebugger()->GetCallstackManager(cpuType)->GetCallstack(callstackArray, callstackSize); }
DllExport void __stdcall GetProfilerData(CpuType cpuType, ProfiledFunction* profilerData, uint32_t& functionCount) { GetDebugger()->GetCallstackManager(cpuType)->GetProfiler()->GetProfilerData(profilerData, functionCount); }
DllExport void __stdcall ResetProfiler(CpuType cpuType) { GetDebugger()->GetCallstackManager(cpuType)->GetProfiler()->Reset(); }
DllExport void __stdcall GetState(DebugState &state) { GetDebugger()->GetState(state, false); } DllExport void __stdcall GetState(DebugState &state) { GetDebugger()->GetState(state, false); }

View file

@ -27,6 +27,7 @@ namespace Mesen.GUI.Config
public SpriteViewerConfig SpriteViewer = new SpriteViewerConfig(); public SpriteViewerConfig SpriteViewer = new SpriteViewerConfig();
public DbgIntegrationConfig DbgIntegration = new DbgIntegrationConfig(); public DbgIntegrationConfig DbgIntegration = new DbgIntegrationConfig();
public ScriptWindowConfig ScriptWindow = new ScriptWindowConfig(); public ScriptWindowConfig ScriptWindow = new ScriptWindowConfig();
public ProfilerConfig Profiler = new ProfilerConfig();
public DebugInfo() public DebugInfo()
{ {

View file

@ -0,0 +1,10 @@
using System.Drawing;
namespace Mesen.GUI.Config
{
public class ProfilerConfig
{
public Size WindowSize = new Size(0, 0);
public Point WindowLocation;
}
}

View file

@ -39,6 +39,7 @@ namespace Mesen.GUI.Debugger
case DebugWindow.EventViewer: frm = new frmEventViewer(); frm.Icon = Properties.Resources.NesEventViewer; break; case DebugWindow.EventViewer: frm = new frmEventViewer(); frm.Icon = Properties.Resources.NesEventViewer; break;
case DebugWindow.ScriptWindow: frm = new frmScript(); frm.Icon = Properties.Resources.Script; break; case DebugWindow.ScriptWindow: frm = new frmScript(); frm.Icon = Properties.Resources.Script; break;
case DebugWindow.RegisterViewer: frm = new frmRegisterViewer(); frm.Icon = Properties.Resources.RegisterIcon; break; case DebugWindow.RegisterViewer: frm = new frmRegisterViewer(); frm.Icon = Properties.Resources.RegisterIcon; break;
case DebugWindow.Profiler: frm = new frmProfiler(); frm.Icon = Properties.Resources.PerfTracker; break;
} }
if(_openedWindows.Count == 0) { if(_openedWindows.Count == 0) {
@ -120,6 +121,7 @@ namespace Mesen.GUI.Debugger
case DebugWindow.GsuDebugger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmDebugger) && ((frmDebugger)form).CpuType == CpuType.Gsu); case DebugWindow.GsuDebugger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmDebugger) && ((frmDebugger)form).CpuType == CpuType.Gsu);
case DebugWindow.TraceLogger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmTraceLogger)); case DebugWindow.TraceLogger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmTraceLogger));
case DebugWindow.EventViewer: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmEventViewer)); case DebugWindow.EventViewer: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmEventViewer));
case DebugWindow.Profiler: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmProfiler));
} }
return null; return null;
@ -166,6 +168,7 @@ namespace Mesen.GUI.Debugger
SpriteViewer, SpriteViewer,
EventViewer, EventViewer,
ScriptWindow, ScriptWindow,
RegisterViewer RegisterViewer,
Profiler
} }
} }

View file

@ -39,6 +39,10 @@ namespace Mesen.GUI.Debugger
_lastUpdate = _timer.ElapsedTicks; _lastUpdate = _timer.ElapsedTicks;
_window.RefreshData(); _window.RefreshData();
((Form)_window).BeginInvoke((Action)(() => { ((Form)_window).BeginInvoke((Action)(() => {
if(((Form)_window).IsDisposed) {
return;
}
_window.RefreshViewer(); _window.RefreshViewer();
//Limit FPS to 3x time it takes for a single update (rough estimate), and cap based on requested fps. //Limit FPS to 3x time it takes for a single update (rough estimate), and cap based on requested fps.

View file

@ -0,0 +1,171 @@
namespace Mesen.GUI.Debugger.Controls
{
partial class ctrlProfiler
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.btnReset = new System.Windows.Forms.Button();
this.lstFunctions = new Mesen.GUI.Controls.DoubleBufferedListView();
this.colFunction = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.colExclusiveTimePercent = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.Controls.Add(this.btnReset, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.lstFunctions, 0, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(673, 307);
this.tableLayoutPanel1.TabIndex = 3;
//
// btnReset
//
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnReset.Location = new System.Drawing.Point(595, 281);
this.btnReset.Name = "btnReset";
this.btnReset.Size = new System.Drawing.Size(75, 23);
this.btnReset.TabIndex = 5;
this.btnReset.Text = "Reset Counts";
this.btnReset.UseVisualStyleBackColor = true;
this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
//
// lstFunctions
//
this.lstFunctions.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.colFunction,
this.columnHeader4,
this.columnHeader3,
this.columnHeader5,
this.columnHeader2,
this.colExclusiveTimePercent,
this.columnHeader1,
this.columnHeader6,
this.columnHeader7});
this.tableLayoutPanel1.SetColumnSpan(this.lstFunctions, 2);
this.lstFunctions.Dock = System.Windows.Forms.DockStyle.Fill;
this.lstFunctions.FullRowSelect = true;
this.lstFunctions.GridLines = true;
this.lstFunctions.HideSelection = false;
this.lstFunctions.Location = new System.Drawing.Point(0, 0);
this.lstFunctions.Margin = new System.Windows.Forms.Padding(0);
this.lstFunctions.Name = "lstFunctions";
this.lstFunctions.Size = new System.Drawing.Size(673, 278);
this.lstFunctions.TabIndex = 7;
this.lstFunctions.UseCompatibleStateImageBehavior = false;
this.lstFunctions.View = System.Windows.Forms.View.Details;
this.lstFunctions.VirtualMode = true;
this.lstFunctions.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lstFunctions_ColumnClick);
this.lstFunctions.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.lstFunctions_RetrieveVirtualItem);
this.lstFunctions.DoubleClick += new System.EventHandler(this.lstFunctions_DoubleClick);
//
// colFunction
//
this.colFunction.Text = "Function (Entry Addr)";
this.colFunction.Width = 120;
//
// columnHeader4
//
this.columnHeader4.Text = "Call Count";
//
// columnHeader3
//
this.columnHeader3.Text = "Inclusive Time (Cyc)";
this.columnHeader3.Width = 79;
//
// columnHeader5
//
this.columnHeader5.Text = "Inclusive Time (%)";
this.columnHeader5.Width = 70;
//
// columnHeader2
//
this.columnHeader2.Text = "Exclusive Time (Cyc)";
this.columnHeader2.Width = 72;
//
// colExclusiveTimePercent
//
this.colExclusiveTimePercent.Text = "Exclusive Time (%)";
this.colExclusiveTimePercent.Width = 57;
//
// columnHeader1
//
this.columnHeader1.Text = "Avg. Cycles";
//
// columnHeader6
//
this.columnHeader6.Text = "Min. Cycles";
//
// columnHeader7
//
this.columnHeader7.Text = "Max. Cycles";
//
// ctrlProfiler
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tableLayoutPanel1);
this.Margin = new System.Windows.Forms.Padding(0);
this.Name = "ctrlProfiler";
this.Size = new System.Drawing.Size(673, 307);
this.tableLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Button btnReset;
private Mesen.GUI.Controls.DoubleBufferedListView lstFunctions;
private System.Windows.Forms.ColumnHeader colFunction;
private System.Windows.Forms.ColumnHeader columnHeader4;
private System.Windows.Forms.ColumnHeader columnHeader2;
private System.Windows.Forms.ColumnHeader columnHeader3;
private System.Windows.Forms.ColumnHeader columnHeader5;
private System.Windows.Forms.ColumnHeader colExclusiveTimePercent;
private System.Windows.Forms.ColumnHeader columnHeader1;
private System.Windows.Forms.ColumnHeader columnHeader6;
private System.Windows.Forms.ColumnHeader columnHeader7;
}
}

View file

@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using Mesen.GUI.Controls;
using Mesen.GUI.Debugger.Labels;
namespace Mesen.GUI.Debugger.Controls
{
public partial class ctrlProfiler : BaseControl
{
private ProfiledFunction[] _newData = new ProfiledFunction[0];
private ProfiledFunction[] _functions = new ProfiledFunction[0];
UInt64 _exclusiveTotal = 0;
private int _sortColumn = 5;
private bool _sortOrder = true;
public CpuType CpuType { get; internal set; }
public ctrlProfiler()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
if(!designMode) {
lstFunctions.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
int newWidth = Math.Max(colFunction.Width * 2, 250);
columnHeader7.Width -= (newWidth - colFunction.Width) + 30;
colFunction.Width = newWidth;
}
}
public void RefreshData()
{
_newData = DebugApi.GetProfilerData(this.CpuType);
}
public void RefreshList()
{
_functions = _newData;
_exclusiveTotal = 0;
foreach(ProfiledFunction func in _functions) {
_exclusiveTotal += func.ExclusiveCycles;
}
lstFunctions.BeginUpdate();
int? topItemIndex = lstFunctions.TopItem?.Index;
int selectedIndex = lstFunctions.SelectedIndices.Count > 0 ? lstFunctions.SelectedIndices[0] : -1;
Array.Sort(_functions, new ListComparer(this, _sortColumn, _sortOrder));
lstFunctions.VirtualListSize = _functions.Length;
if(topItemIndex.HasValue) {
lstFunctions.TopItem = lstFunctions.Items[topItemIndex.Value];
}
if(selectedIndex >= 0 && _functions.Length > selectedIndex) {
lstFunctions.Items[selectedIndex].Selected = true;
lstFunctions.Items[selectedIndex].Focused = true;
}
lstFunctions.EndUpdate();
}
private void btnReset_Click(object sender, EventArgs e)
{
DebugApi.ResetProfiler(this.CpuType);
lstFunctions.Items.Clear();
RefreshData();
}
private void lstFunctions_ColumnClick(object sender, ColumnClickEventArgs e)
{
if(_sortColumn == e.Column) {
_sortOrder = !_sortOrder;
} else {
_sortColumn = e.Column;
_sortOrder = e.Column == 0 ? false : true;
}
RefreshList();
}
private void lstFunctions_DoubleClick(object sender, EventArgs e)
{
if(lstFunctions.SelectedIndices.Count > 0) {
AddressInfo relativeAddress = DebugApi.GetRelativeAddress(_functions[lstFunctions.SelectedIndices[0]].Address);
if(relativeAddress.Address >= 0) {
frmDebugger debugger = DebugWindowManager.OpenDebugger(this.CpuType);
debugger.GoToAddress(relativeAddress.Address);
}
}
}
private void lstFunctions_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = GetListItem(_functions[e.ItemIndex]);
}
private ListViewItem GetListItem(ProfiledFunction func)
{
ListViewItem item = new ListViewItem(GetFunctionName(func));
item.Selected = false;
item.Focused = false;
item.SubItems.Add(GetColumnContent(func, 1).ToString());
item.SubItems.Add(GetColumnContent(func, 2).ToString());
item.SubItems.Add(((double)GetColumnContent(func, 3)).ToString("0.00"));
item.SubItems.Add(GetColumnContent(func, 4).ToString());
item.SubItems.Add(((double)GetColumnContent(func, 5)).ToString("0.00"));
item.SubItems.Add(GetColumnContent(func, 6).ToString());
item.SubItems.Add((UInt64)GetColumnContent(func, 7) == UInt64.MaxValue ? "n/a" : GetColumnContent(func, 7).ToString());
item.SubItems.Add((UInt64)GetColumnContent(func, 8) == 0 ? "n/a" : GetColumnContent(func, 8).ToString());
return item;
}
private string GetFunctionName(ProfiledFunction func)
{
int hexCount = this.CpuType == CpuType.Spc ? 4 : 6;
string functionName;
if(func.Address.Address == -1) {
functionName = "[Reset]";
} else {
CodeLabel label = LabelManager.GetLabel((UInt32)func.Address.Address, func.Address.Type);
switch(func.Address.Type) {
case SnesMemoryType.PrgRom: functionName = "PRG: $"; break;
case SnesMemoryType.Register: functionName = "REG: $"; break;
case SnesMemoryType.SaveRam: functionName = "SRAM: $"; break;
case SnesMemoryType.WorkRam: functionName = "WRAM: $"; break;
case SnesMemoryType.SpcRam: functionName = "SPC: $"; break;
case SnesMemoryType.SpcRom: functionName = "SPC ROM: $"; break;
case SnesMemoryType.Sa1InternalRam: functionName = "IRAM: $"; break;
default: throw new Exception("Unsupported type");
}
functionName += func.Address.Address.ToString("X" + hexCount.ToString());
if(label != null) {
functionName = label.Label + " (" + functionName + ")";
}
}
return functionName;
}
private object GetColumnContent(ProfiledFunction func, int columnIndex)
{
switch(columnIndex) {
case 0: return GetFunctionName(func);
case 1: return func.CallCount;
case 2: return func.InclusiveCycles;
case 3: return (double)func.InclusiveCycles / _exclusiveTotal * 100;
case 4: return func.ExclusiveCycles;
case 5: return (double)func.ExclusiveCycles / _exclusiveTotal * 100;
case 6: return func.CallCount == 0 ? 0 : (func.InclusiveCycles / func.CallCount);
case 7: return func.MinCycles;
case 8: return func.MaxCycles;
}
throw new Exception("Invalid column index");
}
private class ListComparer : IComparer<ProfiledFunction>
{
private int _columnIndex;
private bool _sortOrder;
private ctrlProfiler _profiler;
public ListComparer(ctrlProfiler profiler, int columnIndex, bool sortOrder)
{
_profiler = profiler;
_columnIndex = columnIndex;
_sortOrder = sortOrder;
}
public int Compare(ProfiledFunction x, ProfiledFunction y)
{
if(_columnIndex == 0) {
if(_sortOrder) {
return String.Compare(_profiler.GetFunctionName(y), _profiler.GetFunctionName(x));
} else {
return String.Compare(_profiler.GetFunctionName(x), _profiler.GetFunctionName(y));
}
} else {
IComparable columnValueY = (IComparable)_profiler.GetColumnContent(x, _columnIndex);
IComparable columnValueX = (IComparable)_profiler.GetColumnContent(y, _columnIndex);
if(_sortOrder) {
return columnValueX.CompareTo(columnValueY);
} else {
return columnValueY.CompareTo(columnValueX);
}
}
}
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,119 @@
namespace Mesen.GUI.Debugger
{
partial class frmProfiler
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
_refreshManager?.Dispose();
_notifListener?.Dispose();
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.ctrlProfiler = new Mesen.GUI.Debugger.Controls.ctrlProfiler();
this.tabMain = new System.Windows.Forms.TabControl();
this.tpgCpu = new System.Windows.Forms.TabPage();
this.tpgSpc = new System.Windows.Forms.TabPage();
this.ctrlProfilerSpc = new Mesen.GUI.Debugger.Controls.ctrlProfiler();
this.tabMain.SuspendLayout();
this.tpgCpu.SuspendLayout();
this.tpgSpc.SuspendLayout();
this.SuspendLayout();
//
// ctrlProfiler
//
this.ctrlProfiler.Dock = System.Windows.Forms.DockStyle.Fill;
this.ctrlProfiler.Location = new System.Drawing.Point(0, 0);
this.ctrlProfiler.Margin = new System.Windows.Forms.Padding(0);
this.ctrlProfiler.Name = "ctrlProfiler";
this.ctrlProfiler.Size = new System.Drawing.Size(657, 359);
this.ctrlProfiler.TabIndex = 1;
//
// tabMain
//
this.tabMain.Controls.Add(this.tpgCpu);
this.tabMain.Controls.Add(this.tpgSpc);
this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabMain.Location = new System.Drawing.Point(0, 0);
this.tabMain.Name = "tabMain";
this.tabMain.SelectedIndex = 0;
this.tabMain.Size = new System.Drawing.Size(665, 385);
this.tabMain.TabIndex = 2;
this.tabMain.SelectedIndexChanged += new System.EventHandler(this.tabMain_SelectedIndexChanged);
//
// tpgCpu
//
this.tpgCpu.Controls.Add(this.ctrlProfiler);
this.tpgCpu.Location = new System.Drawing.Point(4, 22);
this.tpgCpu.Margin = new System.Windows.Forms.Padding(0);
this.tpgCpu.Name = "tpgCpu";
this.tpgCpu.Size = new System.Drawing.Size(657, 359);
this.tpgCpu.TabIndex = 0;
this.tpgCpu.Text = "CPU";
this.tpgCpu.UseVisualStyleBackColor = true;
//
// tpgSpc
//
this.tpgSpc.Controls.Add(this.ctrlProfilerSpc);
this.tpgSpc.Location = new System.Drawing.Point(4, 22);
this.tpgSpc.Margin = new System.Windows.Forms.Padding(0);
this.tpgSpc.Name = "tpgSpc";
this.tpgSpc.Size = new System.Drawing.Size(657, 359);
this.tpgSpc.TabIndex = 1;
this.tpgSpc.Text = "SPC";
this.tpgSpc.UseVisualStyleBackColor = true;
//
// ctrlProfilerSpc
//
this.ctrlProfilerSpc.Dock = System.Windows.Forms.DockStyle.Fill;
this.ctrlProfilerSpc.Location = new System.Drawing.Point(0, 0);
this.ctrlProfilerSpc.Margin = new System.Windows.Forms.Padding(0);
this.ctrlProfilerSpc.Name = "ctrlProfilerSpc";
this.ctrlProfilerSpc.Size = new System.Drawing.Size(657, 359);
this.ctrlProfilerSpc.TabIndex = 2;
//
// frmProfiler
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(665, 385);
this.Controls.Add(this.tabMain);
this.Name = "frmProfiler";
this.Text = "Performance Profiler";
this.Controls.SetChildIndex(this.tabMain, 0);
this.tabMain.ResumeLayout(false);
this.tpgCpu.ResumeLayout(false);
this.tpgSpc.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Controls.ctrlProfiler ctrlProfiler;
private System.Windows.Forms.TabControl tabMain;
private System.Windows.Forms.TabPage tpgCpu;
private System.Windows.Forms.TabPage tpgSpc;
private Controls.ctrlProfiler ctrlProfilerSpc;
}
}

View file

@ -0,0 +1,126 @@
using Mesen.GUI.Config;
using Mesen.GUI.Debugger.Controls;
using Mesen.GUI.Forms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Debugger
{
public partial class frmProfiler : BaseForm, IRefresh
{
private ctrlProfiler ctrlProfilerCoprocessor = new ctrlProfiler();
private WindowRefreshManager _refreshManager;
private NotificationListener _notifListener;
private TabPage _selectedTab;
private int _frameCount = 0;
public ctrlScanlineCycleSelect ScanlineCycleSelect => null;
public frmProfiler()
{
InitializeComponent();
if(!DesignMode) {
ProfilerConfig cfg = ConfigManager.Config.Debug.Profiler;
RestoreLocation(cfg.WindowLocation, cfg.WindowSize);
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
ctrlProfiler.CpuType = CpuType.Cpu;
ctrlProfilerSpc.CpuType = CpuType.Spc;
_selectedTab = tpgCpu;
UpdateAvailableTabs();
RefreshData();
RefreshViewer();
_refreshManager = new WindowRefreshManager(this);
_refreshManager.AutoRefresh = true;
_refreshManager.AutoRefreshSpeed = RefreshSpeed.Low;
_notifListener = new NotificationListener();
_notifListener.OnNotification += OnNotificationReceived;
}
private void OnNotificationReceived(NotificationEventArgs e)
{
switch(e.NotificationType) {
case ConsoleNotificationType.GameLoaded:
_selectedTab = tpgCpu;
this.BeginInvoke((Action)(() => {
UpdateAvailableTabs();
}));
break;
}
}
public void RefreshData()
{
if(_selectedTab == tpgCpu) {
ctrlProfiler.RefreshData();
} else if(_selectedTab == tpgSpc) {
ctrlProfilerSpc.RefreshData();
} else {
ctrlProfilerCoprocessor.RefreshData();
}
}
public void RefreshViewer()
{
if(_selectedTab == tpgCpu) {
ctrlProfiler.RefreshList();
} else if(_selectedTab == tpgSpc) {
ctrlProfilerSpc.RefreshList();
} else {
ctrlProfilerCoprocessor.RefreshList();
}
}
private void UpdateAvailableTabs()
{
if(tabMain.Controls.Count > 2) {
tabMain.Controls.RemoveAt(2);
}
tabMain.SelectedTab = tpgCpu;
_selectedTab = tpgCpu;
if(EmuApi.GetRomInfo().CoprocessorType == CoprocessorType.SA1) {
TabPage coprocessorTab = new TabPage(ResourceHelper.GetEnumText(CoprocessorType.SA1));
tabMain.TabPages.Add(coprocessorTab);
coprocessorTab.Controls.Add(ctrlProfilerCoprocessor);
ctrlProfilerCoprocessor.Dock = DockStyle.Fill;
ctrlProfilerCoprocessor.CpuType = CpuType.Sa1;
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
ProfilerConfig cfg = ConfigManager.Config.Debug.Profiler;
cfg.WindowSize = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Size : this.Size;
cfg.WindowLocation = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Location : this.Location;
ConfigManager.ApplyChanges();
}
private void tabMain_SelectedIndexChanged(object sender, EventArgs e)
{
_selectedTab = tabMain.SelectedTab;
RefreshData();
RefreshViewer();
}
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -48,7 +48,7 @@ namespace Mesen.GUI.Debugger
GetMember(nameof(DebuggerShortcutsConfig.OpenEventViewer)), GetMember(nameof(DebuggerShortcutsConfig.OpenEventViewer)),
GetMember(nameof(DebuggerShortcutsConfig.OpenMemoryTools)), GetMember(nameof(DebuggerShortcutsConfig.OpenMemoryTools)),
GetMember(nameof(DebuggerShortcutsConfig.OpenRegisterViewer)), GetMember(nameof(DebuggerShortcutsConfig.OpenRegisterViewer)),
//GetMember(nameof(DebuggerShortcutsConfig.OpenProfiler)), GetMember(nameof(DebuggerShortcutsConfig.OpenProfiler)),
GetMember(nameof(DebuggerShortcutsConfig.OpenScriptWindow)), GetMember(nameof(DebuggerShortcutsConfig.OpenScriptWindow)),
GetMember(nameof(DebuggerShortcutsConfig.OpenTraceLogger)), GetMember(nameof(DebuggerShortcutsConfig.OpenTraceLogger)),
//GetMember(nameof(DebuggerShortcutsConfig.OpenWatchWindow)), //GetMember(nameof(DebuggerShortcutsConfig.OpenWatchWindow)),

View file

@ -18,6 +18,7 @@ namespace Mesen.GUI.Debugger
private NotificationListener _notifListener; private NotificationListener _notifListener;
private CpuType _cpuType; private CpuType _cpuType;
private bool _firstBreak = true; private bool _firstBreak = true;
private int _destAddress = -1;
public CpuType CpuType { get { return _cpuType; } } public CpuType CpuType { get { return _cpuType; } }
@ -331,7 +332,11 @@ namespace Mesen.GUI.Debugger
public void GoToAddress(int address) public void GoToAddress(int address)
{ {
ctrlDisassemblyView.ScrollToAddress((uint)address); if(_firstBreak) {
_destAddress = address;
} else {
ctrlDisassemblyView.ScrollToAddress((uint)address);
}
} }
private void GoToAddress() private void GoToAddress()
@ -468,10 +473,15 @@ namespace Mesen.GUI.Debugger
this.BeginInvoke((MethodInvoker)(() => { this.BeginInvoke((MethodInvoker)(() => {
ProcessBreakEvent(evt, state, activeAddress); ProcessBreakEvent(evt, state, activeAddress);
if(_firstBreak && !ConfigManager.Config.Debug.Debugger.BreakOnOpen) { if(_firstBreak) {
DebugApi.ResumeExecution(); _firstBreak = false;
if(!ConfigManager.Config.Debug.Debugger.BreakOnOpen) {
DebugApi.ResumeExecution();
}
if(_destAddress >= 0) {
GoToAddress(_destAddress);
}
} }
_firstBreak = false;
})); }));
break; break;
} }

View file

@ -117,6 +117,19 @@
this.mnuPlayMovie = new System.Windows.Forms.ToolStripMenuItem(); this.mnuPlayMovie = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRecordMovie = new System.Windows.Forms.ToolStripMenuItem(); this.mnuRecordMovie = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStopMovie = new System.Windows.Forms.ToolStripMenuItem(); this.mnuStopMovie = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlay = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStartServer = new System.Windows.Forms.ToolStripMenuItem();
this.mnuConnect = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlaySelectController = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer1 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer2 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer3 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer4 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer5 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuNetPlaySpectator = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.mnuProfile = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem25 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripMenuItem25 = new System.Windows.Forms.ToolStripSeparator();
this.mnuSoundRecorder = new System.Windows.Forms.ToolStripMenuItem(); this.mnuSoundRecorder = new System.Windows.Forms.ToolStripMenuItem();
this.mnuWaveRecord = new System.Windows.Forms.ToolStripMenuItem(); this.mnuWaveRecord = new System.Windows.Forms.ToolStripMenuItem();
@ -158,19 +171,7 @@
this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem(); this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem();
this.pnlRenderer = new System.Windows.Forms.Panel(); this.pnlRenderer = new System.Windows.Forms.Panel();
this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames(); this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames();
this.mnuNetPlay = new System.Windows.Forms.ToolStripMenuItem(); this.mnuProfiler = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStartServer = new System.Windows.Forms.ToolStripMenuItem();
this.mnuConnect = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlaySelectController = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer1 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer2 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer3 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer4 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer5 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuNetPlaySpectator = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.mnuProfile = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMain.SuspendLayout(); this.mnuMain.SuspendLayout();
this.pnlRenderer.SuspendLayout(); this.pnlRenderer.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
@ -840,7 +841,7 @@
// //
this.mnuPlayMovie.Image = global::Mesen.GUI.Properties.Resources.MediaPlay; this.mnuPlayMovie.Image = global::Mesen.GUI.Properties.Resources.MediaPlay;
this.mnuPlayMovie.Name = "mnuPlayMovie"; this.mnuPlayMovie.Name = "mnuPlayMovie";
this.mnuPlayMovie.Size = new System.Drawing.Size(152, 22); this.mnuPlayMovie.Size = new System.Drawing.Size(120, 22);
this.mnuPlayMovie.Text = "Play..."; this.mnuPlayMovie.Text = "Play...";
this.mnuPlayMovie.Click += new System.EventHandler(this.mnuPlayMovie_Click); this.mnuPlayMovie.Click += new System.EventHandler(this.mnuPlayMovie_Click);
// //
@ -848,7 +849,7 @@
// //
this.mnuRecordMovie.Image = global::Mesen.GUI.Properties.Resources.Record; this.mnuRecordMovie.Image = global::Mesen.GUI.Properties.Resources.Record;
this.mnuRecordMovie.Name = "mnuRecordMovie"; this.mnuRecordMovie.Name = "mnuRecordMovie";
this.mnuRecordMovie.Size = new System.Drawing.Size(152, 22); this.mnuRecordMovie.Size = new System.Drawing.Size(120, 22);
this.mnuRecordMovie.Text = "Record..."; this.mnuRecordMovie.Text = "Record...";
this.mnuRecordMovie.Click += new System.EventHandler(this.mnuRecordMovie_Click); this.mnuRecordMovie.Click += new System.EventHandler(this.mnuRecordMovie_Click);
// //
@ -856,10 +857,101 @@
// //
this.mnuStopMovie.Image = global::Mesen.GUI.Properties.Resources.MediaStop; this.mnuStopMovie.Image = global::Mesen.GUI.Properties.Resources.MediaStop;
this.mnuStopMovie.Name = "mnuStopMovie"; this.mnuStopMovie.Name = "mnuStopMovie";
this.mnuStopMovie.Size = new System.Drawing.Size(152, 22); this.mnuStopMovie.Size = new System.Drawing.Size(120, 22);
this.mnuStopMovie.Text = "Stop"; this.mnuStopMovie.Text = "Stop";
this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click); this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click);
// //
// mnuNetPlay
//
this.mnuNetPlay.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuStartServer,
this.mnuConnect,
this.mnuNetPlaySelectController,
this.toolStripSeparator2,
this.mnuProfile});
this.mnuNetPlay.Image = global::Mesen.GUI.Properties.Resources.Network;
this.mnuNetPlay.Name = "mnuNetPlay";
this.mnuNetPlay.Size = new System.Drawing.Size(182, 22);
this.mnuNetPlay.Text = "Net Play";
//
// mnuStartServer
//
this.mnuStartServer.Name = "mnuStartServer";
this.mnuStartServer.Size = new System.Drawing.Size(168, 22);
this.mnuStartServer.Text = "Start Server";
//
// mnuConnect
//
this.mnuConnect.Name = "mnuConnect";
this.mnuConnect.Size = new System.Drawing.Size(168, 22);
this.mnuConnect.Text = "Connect to Server";
//
// mnuNetPlaySelectController
//
this.mnuNetPlaySelectController.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuNetPlayPlayer1,
this.mnuNetPlayPlayer2,
this.mnuNetPlayPlayer3,
this.mnuNetPlayPlayer4,
this.mnuNetPlayPlayer5,
this.toolStripSeparator1,
this.mnuNetPlaySpectator});
this.mnuNetPlaySelectController.Name = "mnuNetPlaySelectController";
this.mnuNetPlaySelectController.Size = new System.Drawing.Size(168, 22);
this.mnuNetPlaySelectController.Text = "Select Controller";
//
// mnuNetPlayPlayer1
//
this.mnuNetPlayPlayer1.Name = "mnuNetPlayPlayer1";
this.mnuNetPlayPlayer1.Size = new System.Drawing.Size(124, 22);
this.mnuNetPlayPlayer1.Text = "Player 1";
//
// mnuNetPlayPlayer2
//
this.mnuNetPlayPlayer2.Name = "mnuNetPlayPlayer2";
this.mnuNetPlayPlayer2.Size = new System.Drawing.Size(124, 22);
this.mnuNetPlayPlayer2.Text = "Player 2";
//
// mnuNetPlayPlayer3
//
this.mnuNetPlayPlayer3.Name = "mnuNetPlayPlayer3";
this.mnuNetPlayPlayer3.Size = new System.Drawing.Size(124, 22);
this.mnuNetPlayPlayer3.Text = "Player 3";
//
// mnuNetPlayPlayer4
//
this.mnuNetPlayPlayer4.Name = "mnuNetPlayPlayer4";
this.mnuNetPlayPlayer4.Size = new System.Drawing.Size(124, 22);
this.mnuNetPlayPlayer4.Text = "Player 4";
//
// mnuNetPlayPlayer5
//
this.mnuNetPlayPlayer5.Name = "mnuNetPlayPlayer5";
this.mnuNetPlayPlayer5.Size = new System.Drawing.Size(124, 22);
this.mnuNetPlayPlayer5.Text = "Player 5";
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(121, 6);
//
// mnuNetPlaySpectator
//
this.mnuNetPlaySpectator.Name = "mnuNetPlaySpectator";
this.mnuNetPlaySpectator.Size = new System.Drawing.Size(124, 22);
this.mnuNetPlaySpectator.Text = "Spectator";
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(165, 6);
//
// mnuProfile
//
this.mnuProfile.Name = "mnuProfile";
this.mnuProfile.Size = new System.Drawing.Size(168, 22);
this.mnuProfile.Text = "Configure Profile";
//
// toolStripMenuItem25 // toolStripMenuItem25
// //
this.toolStripMenuItem25.Name = "toolStripMenuItem25"; this.toolStripMenuItem25.Name = "toolStripMenuItem25";
@ -990,6 +1082,7 @@
this.mnuDebugger, this.mnuDebugger,
this.mnuEventViewer, this.mnuEventViewer,
this.mnuMemoryTools, this.mnuMemoryTools,
this.mnuProfiler,
this.mnuRegisterViewer, this.mnuRegisterViewer,
this.mnuScriptWindow, this.mnuScriptWindow,
this.mnuTraceLogger, this.mnuTraceLogger,
@ -1012,101 +1105,101 @@
// //
this.mnuDebugger.Image = global::Mesen.GUI.Properties.Resources.Debugger; this.mnuDebugger.Image = global::Mesen.GUI.Properties.Resources.Debugger;
this.mnuDebugger.Name = "mnuDebugger"; this.mnuDebugger.Name = "mnuDebugger";
this.mnuDebugger.Size = new System.Drawing.Size(155, 22); this.mnuDebugger.Size = new System.Drawing.Size(183, 22);
this.mnuDebugger.Text = "Debugger"; this.mnuDebugger.Text = "Debugger";
// //
// mnuEventViewer // mnuEventViewer
// //
this.mnuEventViewer.Image = global::Mesen.GUI.Properties.Resources.NesEventViewer; this.mnuEventViewer.Image = global::Mesen.GUI.Properties.Resources.NesEventViewer;
this.mnuEventViewer.Name = "mnuEventViewer"; this.mnuEventViewer.Name = "mnuEventViewer";
this.mnuEventViewer.Size = new System.Drawing.Size(155, 22); this.mnuEventViewer.Size = new System.Drawing.Size(183, 22);
this.mnuEventViewer.Text = "Event Viewer"; this.mnuEventViewer.Text = "Event Viewer";
// //
// mnuMemoryTools // mnuMemoryTools
// //
this.mnuMemoryTools.Image = global::Mesen.GUI.Properties.Resources.CheatCode; this.mnuMemoryTools.Image = global::Mesen.GUI.Properties.Resources.CheatCode;
this.mnuMemoryTools.Name = "mnuMemoryTools"; this.mnuMemoryTools.Name = "mnuMemoryTools";
this.mnuMemoryTools.Size = new System.Drawing.Size(155, 22); this.mnuMemoryTools.Size = new System.Drawing.Size(183, 22);
this.mnuMemoryTools.Text = "Memory Tools"; this.mnuMemoryTools.Text = "Memory Tools";
// //
// mnuRegisterViewer // mnuRegisterViewer
// //
this.mnuRegisterViewer.Image = global::Mesen.GUI.Properties.Resources.RegisterIcon; this.mnuRegisterViewer.Image = global::Mesen.GUI.Properties.Resources.RegisterIcon;
this.mnuRegisterViewer.Name = "mnuRegisterViewer"; this.mnuRegisterViewer.Name = "mnuRegisterViewer";
this.mnuRegisterViewer.Size = new System.Drawing.Size(155, 22); this.mnuRegisterViewer.Size = new System.Drawing.Size(183, 22);
this.mnuRegisterViewer.Text = "Register Viewer"; this.mnuRegisterViewer.Text = "Register Viewer";
// //
// mnuScriptWindow // mnuScriptWindow
// //
this.mnuScriptWindow.Image = global::Mesen.GUI.Properties.Resources.Script; this.mnuScriptWindow.Image = global::Mesen.GUI.Properties.Resources.Script;
this.mnuScriptWindow.Name = "mnuScriptWindow"; this.mnuScriptWindow.Name = "mnuScriptWindow";
this.mnuScriptWindow.Size = new System.Drawing.Size(155, 22); this.mnuScriptWindow.Size = new System.Drawing.Size(183, 22);
this.mnuScriptWindow.Text = "Script Window"; this.mnuScriptWindow.Text = "Script Window";
// //
// mnuTraceLogger // mnuTraceLogger
// //
this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow;
this.mnuTraceLogger.Name = "mnuTraceLogger"; this.mnuTraceLogger.Name = "mnuTraceLogger";
this.mnuTraceLogger.Size = new System.Drawing.Size(155, 22); this.mnuTraceLogger.Size = new System.Drawing.Size(183, 22);
this.mnuTraceLogger.Text = "Trace Logger"; this.mnuTraceLogger.Text = "Trace Logger";
// //
// toolStripMenuItem12 // toolStripMenuItem12
// //
this.toolStripMenuItem12.Name = "toolStripMenuItem12"; this.toolStripMenuItem12.Name = "toolStripMenuItem12";
this.toolStripMenuItem12.Size = new System.Drawing.Size(152, 6); this.toolStripMenuItem12.Size = new System.Drawing.Size(180, 6);
// //
// mnuTilemapViewer // mnuTilemapViewer
// //
this.mnuTilemapViewer.Image = global::Mesen.GUI.Properties.Resources.VideoOptions; this.mnuTilemapViewer.Image = global::Mesen.GUI.Properties.Resources.VideoOptions;
this.mnuTilemapViewer.Name = "mnuTilemapViewer"; this.mnuTilemapViewer.Name = "mnuTilemapViewer";
this.mnuTilemapViewer.Size = new System.Drawing.Size(155, 22); this.mnuTilemapViewer.Size = new System.Drawing.Size(183, 22);
this.mnuTilemapViewer.Text = "Tilemap Viewer"; this.mnuTilemapViewer.Text = "Tilemap Viewer";
// //
// mnuTileViewer // mnuTileViewer
// //
this.mnuTileViewer.Image = global::Mesen.GUI.Properties.Resources.VerticalLayout; this.mnuTileViewer.Image = global::Mesen.GUI.Properties.Resources.VerticalLayout;
this.mnuTileViewer.Name = "mnuTileViewer"; this.mnuTileViewer.Name = "mnuTileViewer";
this.mnuTileViewer.Size = new System.Drawing.Size(155, 22); this.mnuTileViewer.Size = new System.Drawing.Size(183, 22);
this.mnuTileViewer.Text = "Tile Viewer"; this.mnuTileViewer.Text = "Tile Viewer";
// //
// mnuSpriteViewer // mnuSpriteViewer
// //
this.mnuSpriteViewer.Image = global::Mesen.GUI.Properties.Resources.PerfTracker; this.mnuSpriteViewer.Image = global::Mesen.GUI.Properties.Resources.PerfTracker;
this.mnuSpriteViewer.Name = "mnuSpriteViewer"; this.mnuSpriteViewer.Name = "mnuSpriteViewer";
this.mnuSpriteViewer.Size = new System.Drawing.Size(155, 22); this.mnuSpriteViewer.Size = new System.Drawing.Size(183, 22);
this.mnuSpriteViewer.Text = "Sprite Viewer"; this.mnuSpriteViewer.Text = "Sprite Viewer";
// //
// mnuPaletteViewer // mnuPaletteViewer
// //
this.mnuPaletteViewer.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; this.mnuPaletteViewer.Image = global::Mesen.GUI.Properties.Resources.VideoFilter;
this.mnuPaletteViewer.Name = "mnuPaletteViewer"; this.mnuPaletteViewer.Name = "mnuPaletteViewer";
this.mnuPaletteViewer.Size = new System.Drawing.Size(155, 22); this.mnuPaletteViewer.Size = new System.Drawing.Size(183, 22);
this.mnuPaletteViewer.Text = "Palette Viewer"; this.mnuPaletteViewer.Text = "Palette Viewer";
// //
// toolStripMenuItem22 // toolStripMenuItem22
// //
this.toolStripMenuItem22.Name = "toolStripMenuItem22"; this.toolStripMenuItem22.Name = "toolStripMenuItem22";
this.toolStripMenuItem22.Size = new System.Drawing.Size(152, 6); this.toolStripMenuItem22.Size = new System.Drawing.Size(180, 6);
// //
// mnuSpcDebugger // mnuSpcDebugger
// //
this.mnuSpcDebugger.Image = global::Mesen.GUI.Properties.Resources.SpcDebugger; this.mnuSpcDebugger.Image = global::Mesen.GUI.Properties.Resources.SpcDebugger;
this.mnuSpcDebugger.Name = "mnuSpcDebugger"; this.mnuSpcDebugger.Name = "mnuSpcDebugger";
this.mnuSpcDebugger.Size = new System.Drawing.Size(155, 22); this.mnuSpcDebugger.Size = new System.Drawing.Size(183, 22);
this.mnuSpcDebugger.Text = "SPC Debugger"; this.mnuSpcDebugger.Text = "SPC Debugger";
// //
// mnuSa1Debugger // mnuSa1Debugger
// //
this.mnuSa1Debugger.Image = global::Mesen.GUI.Properties.Resources.Sa1Debugger; this.mnuSa1Debugger.Image = global::Mesen.GUI.Properties.Resources.Sa1Debugger;
this.mnuSa1Debugger.Name = "mnuSa1Debugger"; this.mnuSa1Debugger.Name = "mnuSa1Debugger";
this.mnuSa1Debugger.Size = new System.Drawing.Size(155, 22); this.mnuSa1Debugger.Size = new System.Drawing.Size(183, 22);
this.mnuSa1Debugger.Text = "SA-1 Debugger"; this.mnuSa1Debugger.Text = "SA-1 Debugger";
// //
// mnuGsuDebugger // mnuGsuDebugger
// //
this.mnuGsuDebugger.Image = global::Mesen.GUI.Properties.Resources.GsuDebugger; this.mnuGsuDebugger.Image = global::Mesen.GUI.Properties.Resources.GsuDebugger;
this.mnuGsuDebugger.Name = "mnuGsuDebugger"; this.mnuGsuDebugger.Name = "mnuGsuDebugger";
this.mnuGsuDebugger.Size = new System.Drawing.Size(155, 22); this.mnuGsuDebugger.Size = new System.Drawing.Size(183, 22);
this.mnuGsuDebugger.Text = "GSU Debugger"; this.mnuGsuDebugger.Text = "GSU Debugger";
// //
// mnuHelp // mnuHelp
@ -1177,96 +1270,12 @@
this.ctrlRecentGames.TabIndex = 1; this.ctrlRecentGames.TabIndex = 1;
this.ctrlRecentGames.Visible = false; this.ctrlRecentGames.Visible = false;
// //
// mnuNetPlay // mnuProfiler
// //
this.mnuNetPlay.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuProfiler.Image = global::Mesen.GUI.Properties.Resources.PerfTracker;
this.mnuStartServer, this.mnuProfiler.Name = "mnuProfiler";
this.mnuConnect, this.mnuProfiler.Size = new System.Drawing.Size(183, 22);
this.mnuNetPlaySelectController, this.mnuProfiler.Text = "Performance Profiler";
this.toolStripSeparator2,
this.mnuProfile});
this.mnuNetPlay.Image = global::Mesen.GUI.Properties.Resources.Network;
this.mnuNetPlay.Name = "mnuNetPlay";
this.mnuNetPlay.Size = new System.Drawing.Size(182, 22);
this.mnuNetPlay.Text = "Net Play";
//
// mnuStartServer
//
this.mnuStartServer.Name = "mnuStartServer";
this.mnuStartServer.Size = new System.Drawing.Size(168, 22);
this.mnuStartServer.Text = "Start Server";
//
// mnuConnect
//
this.mnuConnect.Name = "mnuConnect";
this.mnuConnect.Size = new System.Drawing.Size(168, 22);
this.mnuConnect.Text = "Connect to Server";
//
// mnuNetPlaySelectController
//
this.mnuNetPlaySelectController.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuNetPlayPlayer1,
this.mnuNetPlayPlayer2,
this.mnuNetPlayPlayer3,
this.mnuNetPlayPlayer4,
this.mnuNetPlayPlayer5,
this.toolStripSeparator1,
this.mnuNetPlaySpectator});
this.mnuNetPlaySelectController.Name = "mnuNetPlaySelectController";
this.mnuNetPlaySelectController.Size = new System.Drawing.Size(168, 22);
this.mnuNetPlaySelectController.Text = "Select Controller";
//
// mnuNetPlayPlayer1
//
this.mnuNetPlayPlayer1.Name = "mnuNetPlayPlayer1";
this.mnuNetPlayPlayer1.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer1.Text = "Player 1";
//
// mnuNetPlayPlayer2
//
this.mnuNetPlayPlayer2.Name = "mnuNetPlayPlayer2";
this.mnuNetPlayPlayer2.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer2.Text = "Player 2";
//
// mnuNetPlayPlayer3
//
this.mnuNetPlayPlayer3.Name = "mnuNetPlayPlayer3";
this.mnuNetPlayPlayer3.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer3.Text = "Player 3";
//
// mnuNetPlayPlayer4
//
this.mnuNetPlayPlayer4.Name = "mnuNetPlayPlayer4";
this.mnuNetPlayPlayer4.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer4.Text = "Player 4";
//
// mnuNetPlayPlayer5
//
this.mnuNetPlayPlayer5.Name = "mnuNetPlayPlayer5";
this.mnuNetPlayPlayer5.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer5.Text = "Player 5";
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(149, 6);
//
// mnuNetPlaySpectator
//
this.mnuNetPlaySpectator.Name = "mnuNetPlaySpectator";
this.mnuNetPlaySpectator.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlaySpectator.Text = "Spectator";
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(165, 6);
//
// mnuProfile
//
this.mnuProfile.Name = "mnuProfile";
this.mnuProfile.Size = new System.Drawing.Size(168, 22);
this.mnuProfile.Text = "Configure Profile";
// //
// frmMain // frmMain
// //
@ -1437,5 +1446,6 @@
private System.Windows.Forms.ToolStripMenuItem mnuNetPlaySpectator; private System.Windows.Forms.ToolStripMenuItem mnuNetPlaySpectator;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripMenuItem mnuProfile; private System.Windows.Forms.ToolStripMenuItem mnuProfile;
} private System.Windows.Forms.ToolStripMenuItem mnuProfiler;
}
} }

View file

@ -288,6 +288,7 @@ namespace Mesen.GUI.Forms
mnuTraceLogger.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenTraceLogger)); mnuTraceLogger.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenTraceLogger));
mnuScriptWindow.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenScriptWindow)); mnuScriptWindow.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenScriptWindow));
mnuRegisterViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenRegisterViewer)); mnuRegisterViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenRegisterViewer));
mnuProfiler.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenProfiler));
mnuNoneFilter.Click += (s, e) => { _shortcuts.SetVideoFilter(VideoFilterType.None); }; mnuNoneFilter.Click += (s, e) => { _shortcuts.SetVideoFilter(VideoFilterType.None); };
mnuNtscFilter.Click += (s, e) => { _shortcuts.SetVideoFilter(VideoFilterType.NTSC); }; mnuNtscFilter.Click += (s, e) => { _shortcuts.SetVideoFilter(VideoFilterType.NTSC); };
@ -348,6 +349,7 @@ namespace Mesen.GUI.Forms
mnuEventViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.EventViewer); }; mnuEventViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.EventViewer); };
mnuScriptWindow.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.ScriptWindow); }; mnuScriptWindow.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.ScriptWindow); };
mnuRegisterViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.RegisterViewer); }; mnuRegisterViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.RegisterViewer); };
mnuProfiler.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.Profiler); };
mnuTestRun.Click += (s, e) => { RomTestHelper.RunTest(); }; mnuTestRun.Click += (s, e) => { RomTestHelper.RunTest(); };
mnuTestRecord.Click += (s, e) => { RomTestHelper.RecordTest(); }; mnuTestRecord.Click += (s, e) => { RomTestHelper.RecordTest(); };
@ -439,6 +441,7 @@ namespace Mesen.GUI.Forms
mnuPaletteViewer.Enabled = running; mnuPaletteViewer.Enabled = running;
mnuEventViewer.Enabled = running; mnuEventViewer.Enabled = running;
mnuRegisterViewer.Enabled = running; mnuRegisterViewer.Enabled = running;
mnuProfiler.Enabled = running;
} }
private void ResizeRecentGames() private void ResizeRecentGames()

View file

@ -130,6 +130,19 @@ namespace Mesen.GUI
return callstack; return callstack;
} }
[DllImport(DllPath)] public static extern void ResetProfiler(CpuType type);
[DllImport(DllPath, EntryPoint = "GetProfilerData")] private static extern void GetProfilerDataWrapper(CpuType type, [In, Out]ProfiledFunction[] profilerData, ref UInt32 functionCount);
public static ProfiledFunction[] GetProfilerData(CpuType type)
{
ProfiledFunction[] profilerData = new ProfiledFunction[100000];
UInt32 functionCount = 0;
DebugApi.GetProfilerDataWrapper(type, profilerData, ref functionCount);
Array.Resize(ref profilerData, (int)functionCount);
return profilerData;
}
[DllImport(DllPath, EntryPoint = "GetMemoryAccessStamps")] private static extern void GetMemoryAccessStampsWrapper(UInt32 offset, UInt32 length, SnesMemoryType type, MemoryOperationType operationType, [In,Out]UInt64[] stamps); [DllImport(DllPath, EntryPoint = "GetMemoryAccessStamps")] private static extern void GetMemoryAccessStampsWrapper(UInt32 offset, UInt32 length, SnesMemoryType type, MemoryOperationType operationType, [In,Out]UInt64[] stamps);
public static UInt64[] GetMemoryAccessStamps(UInt32 offset, UInt32 length, SnesMemoryType type, MemoryOperationType operationType) public static UInt64[] GetMemoryAccessStamps(UInt32 offset, UInt32 length, SnesMemoryType type, MemoryOperationType operationType)
{ {
@ -444,6 +457,7 @@ namespace Mesen.GUI
public UInt32 Source; public UInt32 Source;
public UInt32 Target; public UInt32 Target;
public UInt32 Return; public UInt32 Return;
public AddressInfo AbsReturn;
public StackFrameFlags Flags; public StackFrameFlags Flags;
}; };
@ -532,4 +546,14 @@ namespace Mesen.GUI
IndexMode8 = 0x10, IndexMode8 = 0x10,
MemoryMode8 = 0x20, MemoryMode8 = 0x20,
} }
public struct ProfiledFunction
{
public UInt64 ExclusiveCycles;
public UInt64 InclusiveCycles;
public UInt64 CallCount;
public UInt64 MinCycles;
public UInt64 MaxCycles;
public AddressInfo Address;
};
} }

View file

@ -272,6 +272,7 @@
<Compile Include="Debugger\Code\IDisassemblyManager.cs" /> <Compile Include="Debugger\Code\IDisassemblyManager.cs" />
<Compile Include="Debugger\Config\DebuggerShortcutsConfig.cs" /> <Compile Include="Debugger\Config\DebuggerShortcutsConfig.cs" />
<Compile Include="Debugger\Config\DebuggerInfo.cs" /> <Compile Include="Debugger\Config\DebuggerInfo.cs" />
<Compile Include="Debugger\Config\ProfilerConfig.cs" />
<Compile Include="Debugger\Config\ScriptWindowConfig.cs" /> <Compile Include="Debugger\Config\ScriptWindowConfig.cs" />
<Compile Include="Debugger\Config\SpriteViewerConfig.cs" /> <Compile Include="Debugger\Config\SpriteViewerConfig.cs" />
<Compile Include="Debugger\Config\RegisterViewerConfig.cs" /> <Compile Include="Debugger\Config\RegisterViewerConfig.cs" />
@ -379,6 +380,18 @@
</Compile> </Compile>
<Compile Include="Debugger\PpuViewer\SpriteInfo.cs" /> <Compile Include="Debugger\PpuViewer\SpriteInfo.cs" />
<Compile Include="Debugger\PpuViewer\WindowRefreshManager.cs" /> <Compile Include="Debugger\PpuViewer\WindowRefreshManager.cs" />
<Compile Include="Debugger\Profiler\ctrlProfiler.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Debugger\Profiler\ctrlProfiler.Designer.cs">
<DependentUpon>ctrlProfiler.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Profiler\frmProfiler.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\Profiler\frmProfiler.Designer.cs">
<DependentUpon>frmProfiler.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Scripts\FastColoredTextBox\AutocompleteItem.cs" /> <Compile Include="Debugger\Scripts\FastColoredTextBox\AutocompleteItem.cs" />
<Compile Include="Debugger\Scripts\FastColoredTextBox\AutocompleteMenu.cs"> <Compile Include="Debugger\Scripts\FastColoredTextBox\AutocompleteMenu.cs">
<SubType>UserControl</SubType> <SubType>UserControl</SubType>
@ -1084,6 +1097,12 @@
<EmbeddedResource Include="Debugger\HexBox\HexBox.resx"> <EmbeddedResource Include="Debugger\HexBox\HexBox.resx">
<DependentUpon>HexBox.cs</DependentUpon> <DependentUpon>HexBox.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Debugger\Profiler\ctrlProfiler.resx">
<DependentUpon>ctrlProfiler.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Profiler\frmProfiler.resx">
<DependentUpon>frmProfiler.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Scripts\FastColoredTextBox\FastColoredTextBox.resx"> <EmbeddedResource Include="Debugger\Scripts\FastColoredTextBox\FastColoredTextBox.resx">
<DependentUpon>FastColoredTextBox.cs</DependentUpon> <DependentUpon>FastColoredTextBox.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>