Skip to content

Commit fbfb3a9

Browse files
malleozximk
andauthored
Scripting: Add debug module and toggle play function (#93)
* Scripting: Add debug module and toggle play function --------- Co-authored-by: xi <[email protected]>
1 parent 9147dec commit fbfb3a9

10 files changed

+469
-1
lines changed

Source/Core/Scripting/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ add_library(scripting
99
Python/PyScriptingBackend.h
1010
Python/Modules/controllermodule.cpp
1111
Python/Modules/controllermodule.h
12+
Python/Modules/debugmodule.cpp
13+
Python/Modules/debugmodule.h
1214
Python/Modules/doliomodule.cpp
1315
Python/Modules/doliomodule.h
1416
Python/Modules/dolphinmodule.cpp
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright 2023 Dolphin Emulator Project
2+
// Licensed under GPLv2+
3+
// Refer to the license.txt file included.
4+
5+
#include "debugmodule.h"
6+
7+
#include <Python.h>
8+
9+
#include "Core/Core.h"
10+
#include "Core/PowerPC/BreakPoints.h"
11+
#include "Core/PowerPC/PowerPC.h"
12+
#include "Core/System.h"
13+
#include "Scripting/Python/Utils/as_py_func.h"
14+
#include "Scripting/Python/Utils/module.h"
15+
16+
namespace PyScripting
17+
{
18+
19+
struct DebugModuleState
20+
{
21+
};
22+
23+
static PyObject* SetBreakpoint(PyObject* self, PyObject* args)
24+
{
25+
auto args_opt = Py::ParseTuple<u32>(args);
26+
if (!args_opt.has_value())
27+
return nullptr;
28+
u32 addr = std::get<0>(args_opt.value());
29+
30+
Core::System::GetInstance().GetPowerPC().GetBreakPoints().Add(addr, false);
31+
32+
// TODO: How can we inform the BreakpointWidget to update the list of breakpoints?
33+
Py_RETURN_NONE;
34+
}
35+
36+
static PyObject* RemoveBreakpoint(PyObject* self, PyObject* args)
37+
{
38+
auto args_opt = Py::ParseTuple<u32>(args);
39+
if (!args_opt.has_value())
40+
return nullptr;
41+
u32 addr = std::get<0>(args_opt.value());
42+
43+
Core::System::GetInstance().GetPowerPC().GetBreakPoints().Remove(addr);
44+
45+
Py_RETURN_NONE;
46+
}
47+
48+
// Take in a dictionary, because some of these parameters might not be specified
49+
static PyObject* SetMemoryBreakpoint(PyObject* self, PyObject* args)
50+
{
51+
PyObject* dict = PyTuple_GetItem(args, 0);
52+
if (!PyDict_Check(dict))
53+
return nullptr;
54+
55+
TMemCheck check;
56+
57+
// Did user specify a single address or do they want to breakpoint a range?
58+
PyObject* at_obj = PyDict_GetItemString(dict, "At");
59+
PyObject* start_obj = PyDict_GetItemString(dict, "Start");
60+
PyObject* end_obj = PyDict_GetItemString(dict, "End");
61+
if (at_obj)
62+
{
63+
u32 addr = PyLong_AsUnsignedLong(at_obj);
64+
check.start_address = addr;
65+
check.end_address = addr;
66+
check.is_ranged = false;
67+
}
68+
else if (start_obj && end_obj)
69+
{
70+
check.start_address = PyLong_AsUnsignedLong(start_obj);
71+
check.end_address = PyLong_AsUnsignedLong(end_obj);
72+
check.is_ranged = true;
73+
}
74+
75+
PyObject* break_read_obj = PyDict_GetItemString(dict, "BreakOnRead");
76+
if (break_read_obj)
77+
check.is_break_on_read = PyObject_IsTrue(break_read_obj);
78+
else
79+
check.is_break_on_read = true;
80+
81+
PyObject* break_write_obj = PyDict_GetItemString(dict, "BreakOnWrite");
82+
if (break_write_obj)
83+
check.is_break_on_write = PyObject_IsTrue(break_write_obj);
84+
else
85+
check.is_break_on_write = false;
86+
87+
PyObject* log_hit_obj = PyDict_GetItemString(dict, "LogOnHit");
88+
if (log_hit_obj)
89+
check.log_on_hit = PyObject_IsTrue(log_hit_obj);
90+
else
91+
check.log_on_hit = true;
92+
93+
PyObject* break_hit_obj = PyDict_GetItemString(dict, "BreakOnHit");
94+
if (break_hit_obj)
95+
check.break_on_hit = PyObject_IsTrue(break_hit_obj);
96+
else
97+
check.break_on_hit = true;
98+
99+
PyObject* expr_obj = PyDict_GetItemString(dict, "Condition");
100+
if (expr_obj)
101+
{
102+
const char* expr = PyUnicode_AsUTF8(expr_obj);
103+
check.condition = Expression::TryParse(std::string_view(expr));
104+
}
105+
else
106+
check.condition = std::nullopt;
107+
108+
Core::System::GetInstance().GetPowerPC().GetMemChecks().Add(std::move(check));
109+
110+
Py_RETURN_NONE;
111+
}
112+
113+
static PyObject* RemoveMemoryBreakpoint(PyObject* self, PyObject* args)
114+
{
115+
auto args_opt = Py::ParseTuple<u32>(args);
116+
if (!args_opt.has_value())
117+
return nullptr;
118+
u32 addr = std::get<0>(args_opt.value());
119+
120+
Core::System::GetInstance().GetPowerPC().GetMemChecks().Remove(addr);
121+
122+
Py_RETURN_NONE;
123+
}
124+
125+
static void SetupDebugModule(PyObject* module, DebugModuleState* state)
126+
{
127+
}
128+
129+
PyMODINIT_FUNC PyInit_debug()
130+
{
131+
static PyMethodDef methods[] = {
132+
{"set_breakpoint", SetBreakpoint, METH_VARARGS, ""},
133+
{"remove_breakpoint", RemoveBreakpoint, METH_VARARGS, ""},
134+
{"set_memory_breakpoint", SetMemoryBreakpoint, METH_VARARGS, ""},
135+
{"remove_memory_breakpoint", RemoveMemoryBreakpoint, METH_VARARGS, ""},
136+
137+
{nullptr, nullptr, 0, nullptr} // Sentinel
138+
};
139+
static PyModuleDef module_def =
140+
Py::MakeStatefulModuleDef<DebugModuleState, SetupDebugModule>("debug", methods);
141+
PyObject* def_obj = PyModuleDef_Init(&module_def);
142+
return def_obj;
143+
}
144+
145+
} // namespace PyScripting
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2023 Dolphin Emulator Project
2+
// Licensed under GPLv2+
3+
// Refer to the license.txt file included.
4+
5+
#pragma once
6+
7+
#include <Python.h>
8+
9+
namespace PyScripting
10+
{
11+
12+
PyMODINIT_FUNC PyInit_debug();
13+
14+
}

Source/Core/Scripting/Python/Modules/dolphinmodule.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ instead of:
3636
from dolphin.event import ...
3737
"""
3838
39+
import dolphin_debug as debug
3940
import dolphin_event as event
4041
import dolphin_memory as memory
4142
import dolphin_gui as gui
@@ -46,7 +47,7 @@ import dolphin_utils as utils
4647
4748
# defining __all__ let's people import everything
4849
# using a star-import: from dolphin import *
49-
__all__ = [event, memory, gui, savestate, controller, registers, utils]
50+
__all__ = [debug, event, memory, gui, savestate, controller, registers, utils]
5051
)";
5152
Py::Object result = Py::LoadPyCodeIntoModule(module, pycode);
5253
if (result.IsNull())

Source/Core/Scripting/Python/Modules/utilmodule.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "Core/Config/MainSettings.h"
99
#include "Core/Core.h"
1010
#include "Core/ConfigManager.h"
11+
#include "Core/System.h"
1112
#include "Scripting/Python/Utils/module.h"
1213
#include "Scripting/Python/Utils/as_py_func.h"
1314

@@ -101,6 +102,28 @@ static PyObject* open_file(PyObject* module, PyObject* args)
101102
return Py_BuildValue("s", filePath.c_str());
102103
}
103104

105+
static PyObject* toggle_play(PyObject* module, PyObject* args)
106+
{
107+
// Play/Pause can only be done on the host thread
108+
Core::QueueHostJob([]() {
109+
Core::State current_state = Core::GetState();
110+
111+
switch (current_state)
112+
{
113+
case Core::State::Paused:
114+
Core::SetState(Core::State::Running);
115+
break;
116+
case Core::State::Running:
117+
Core::SetState(Core::State::Paused);
118+
break;
119+
default:
120+
break;
121+
}
122+
});
123+
124+
Py_RETURN_NONE;
125+
}
126+
104127
static void setup_file_module(PyObject* module, FileState* state)
105128
{
106129
// I don't think we need anything here yet
@@ -119,6 +142,7 @@ PyMODINIT_FUNC PyInit_dol_utils()
119142
{"stop_audiodump", stop_audiodump, METH_NOARGS, ""},
120143
{"is_audiodumping", is_audiodumping, METH_NOARGS, ""},
121144
{"save_screenshot", (PyCFunction) save_screenshot, METH_VARARGS | METH_KEYWORDS, ""},
145+
{"toggle_play", toggle_play, METH_NOARGS, ""},
122146
{nullptr, nullptr, 0, nullptr}};
123147
static PyModuleDef module_def =
124148
Py::MakeStatefulModuleDef<FileState, setup_file_module>("dolphin_utils", methods);

Source/Core/Scripting/Python/PyScriptingBackend.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "Scripting/Python/coroutine.h"
1515
#include "Scripting/Python/Modules/controllermodule.h"
16+
#include "Scripting/Python/Modules/debugmodule.h"
1617
#include "Scripting/Python/Modules/doliomodule.h"
1718
#include "Scripting/Python/Modules/dolphinmodule.h"
1819
#include "Scripting/Python/Modules/eventmodule.h"
@@ -44,6 +45,8 @@ static PyThreadState* InitMainPythonInterpreter()
4445
ERROR_LOG_FMT(SCRIPTING, "failed to add dolio_stderr to builtins");
4546
if (PyImport_AppendInittab("dolphin_memory", PyInit_memory) == -1)
4647
ERROR_LOG_FMT(SCRIPTING, "failed to add dolphin_memory to builtins");
48+
if (PyImport_AppendInittab("dolphin_debug", PyInit_debug) == -1)
49+
ERROR_LOG_FMT(SCRIPTING, "failed to add dolphin_debug to builtins");
4750
if (PyImport_AppendInittab("dolphin_event", PyInit_event) == -1)
4851
ERROR_LOG_FMT(SCRIPTING, "failed to add dolphin_event to builtins");
4952
if (PyImport_AppendInittab("dolphin_gui", PyInit_gui) == -1)

Source/Core/Scripting/Scripting.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<ItemGroup>
3838
<ClCompile Include="Python\coroutine.cpp" />
3939
<ClCompile Include="Python\Modules\controllermodule.cpp" />
40+
<ClCompile Include="Python\Modules\debugmodule.cpp" />
4041
<ClCompile Include="Python\Modules\doliomodule.cpp" />
4142
<ClCompile Include="Python\Modules\dolphinmodule.cpp" />
4243
<ClCompile Include="Python\Modules\eventmodule.cpp" />
@@ -54,6 +55,7 @@
5455
<ClInclude Include="ScriptList.h" />
5556
<ClInclude Include="Python\coroutine.h" />
5657
<ClInclude Include="Python\Modules\controllermodule.h" />
58+
<ClInclude Include="Python\Modules\debugmodule.h" />
5759
<ClInclude Include="Python\Modules\doliomodule.h" />
5860
<ClInclude Include="Python\Modules\dolphinmodule.h" />
5961
<ClInclude Include="Python\Modules\eventmodule.h" />

Source/Core/Scripting/Scripting.vcxproj.filters

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
<ClCompile Include="Python\PyScriptingBackend.cpp">
66
<Filter>Python</Filter>
77
</ClCompile>
8+
<ClCompile Include="Python\Modules\debugmodule.cpp">
9+
<Filter>Python\Modules</Filter>
10+
</ClCompile>
811
<ClCompile Include="Python\Modules\doliomodule.cpp">
912
<Filter>Python\Modules</Filter>
1013
</ClCompile>
@@ -45,6 +48,12 @@
4548
<ClInclude Include="Python\coroutine.h">
4649
<Filter>Python</Filter>
4750
</ClInclude>
51+
<ClInclude Include="Python\Modules\controllermodule.h">
52+
<Filter>Python\Modules</Filter>
53+
</ClInclude>
54+
<ClInclude Include="Python\Modules\debugmodule.h">
55+
<Filter>Python\Modules</Filter>
56+
</ClInclude>
4857
<ClInclude Include="Python\Modules\doliomodule.h">
4958
<Filter>Python\Modules</Filter>
5059
</ClInclude>

0 commit comments

Comments
 (0)