summaryrefslogtreecommitdiff
path: root/src/video_core/debug_utils
diff options
context:
space:
mode:
authorGravatar Tony Wasserka2014-10-25 18:02:26 +0200
committerGravatar Tony Wasserka2014-12-09 16:37:34 +0100
commit2c71ec70527abd091d69f1fdd30aaf95d815214a (patch)
treed0e1f48b048f6d3ef801d0562bfcfe0fa11de6f1 /src/video_core/debug_utils
parentcitra-qt: Polish the pica tracing widget. (diff)
downloadyuzu-2c71ec70527abd091d69f1fdd30aaf95d815214a.tar.gz
yuzu-2c71ec70527abd091d69f1fdd30aaf95d815214a.tar.xz
yuzu-2c71ec70527abd091d69f1fdd30aaf95d815214a.zip
Pica/DebugUtils: Add breakpoint functionality.
Diffstat (limited to 'src/video_core/debug_utils')
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp43
-rw-r--r--src/video_core/debug_utils/debug_utils.h133
2 files changed, 176 insertions, 0 deletions
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 8a5f11424..11f87d988 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -3,6 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <condition_variable>
7#include <list>
6#include <map> 8#include <map>
7#include <fstream> 9#include <fstream>
8#include <mutex> 10#include <mutex>
@@ -12,6 +14,7 @@
12#include <png.h> 14#include <png.h>
13#endif 15#endif
14 16
17#include "common/log.h"
15#include "common/file_util.h" 18#include "common/file_util.h"
16 19
17#include "video_core/pica.h" 20#include "video_core/pica.h"
@@ -20,6 +23,46 @@
20 23
21namespace Pica { 24namespace Pica {
22 25
26void DebugContext::OnEvent(Event event, void* data) {
27 if (!breakpoints[event].enabled)
28 return;
29
30 {
31 std::unique_lock<std::mutex> lock(breakpoint_mutex);
32
33 // TODO: Should stop the CPU thread here once we multithread emulation.
34
35 active_breakpoint = event;
36 at_breakpoint = true;
37
38 // Tell all observers that we hit a breakpoint
39 for (auto& breakpoint_observer : breakpoint_observers) {
40 breakpoint_observer->OnPicaBreakPointHit(event, data);
41 }
42
43 // Wait until another thread tells us to Resume()
44 resume_from_breakpoint.wait(lock, [&]{ return !at_breakpoint; });
45 }
46}
47
48void DebugContext::Resume() {
49 {
50 std::unique_lock<std::mutex> lock(breakpoint_mutex);
51
52 // Tell all observers that we are about to resume
53 for (auto& breakpoint_observer : breakpoint_observers) {
54 breakpoint_observer->OnPicaResume();
55 }
56
57 // Resume the waiting thread (i.e. OnEvent())
58 at_breakpoint = false;
59 }
60
61 resume_from_breakpoint.notify_one();
62}
63
64std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
65
23namespace DebugUtils { 66namespace DebugUtils {
24 67
25void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { 68void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) {
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index b1558cfae..26b26e22f 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -5,13 +5,146 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <condition_variable>
9#include <list>
10#include <map>
8#include <memory> 11#include <memory>
12#include <mutex>
9#include <vector> 13#include <vector>
10 14
11#include "video_core/pica.h" 15#include "video_core/pica.h"
12 16
13namespace Pica { 17namespace Pica {
14 18
19class DebugContext {
20public:
21 enum class Event {
22 FirstEvent = 0,
23
24 CommandLoaded = FirstEvent,
25 CommandProcessed,
26 IncomingPrimitiveBatch,
27 FinishedPrimitiveBatch,
28
29 NumEvents
30 };
31
32 /**
33 * Inherit from this class to be notified of events registered to some debug context.
34 * Most importantly this is used for our debugger GUI.
35 *
36 * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
37 * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access
38 * @todo Evaluate an alternative interface, in which there is only one managing observer and multiple child observers running (by design) on the same thread.
39 */
40 class BreakPointObserver {
41 public:
42 /// Constructs the object such that it observes events of the given DebugContext.
43 BreakPointObserver(std::shared_ptr<DebugContext> debug_context) : context_weak(debug_context) {
44 std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
45 debug_context->breakpoint_observers.push_back(this);
46 }
47
48 virtual ~BreakPointObserver() {
49 auto context = context_weak.lock();
50 if (context) {
51 std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
52 context->breakpoint_observers.remove(this);
53
54 // If we are the last observer to be destroyed, tell the debugger context that
55 // it is free to continue. In particular, this is required for a proper Citra
56 // shutdown, when the emulation thread is waiting at a breakpoint.
57 if (context->breakpoint_observers.empty())
58 context->Resume();
59 }
60 }
61
62 /**
63 * Action to perform when a breakpoint was reached.
64 * @param event Type of event which triggered the breakpoint
65 * @param data Optional data pointer (if unused, this is a nullptr)
66 * @note This function will perform nothing unless it is overridden in the child class.
67 */
68 virtual void OnPicaBreakPointHit(Event, void*) {
69 }
70
71 /**
72 * Action to perform when emulation is resumed from a breakpoint.
73 * @note This function will perform nothing unless it is overridden in the child class.
74 */
75 virtual void OnPicaResume() {
76 }
77
78 protected:
79 /**
80 * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
81 * context_weak.lock(), always compare the result against nullptr.
82 */
83 std::weak_ptr<DebugContext> context_weak;
84 };
85
86 /**
87 * Simple structure defining a breakpoint state
88 */
89 struct BreakPoint {
90 bool enabled = false;
91 };
92
93 /**
94 * Static constructor used to create a shared_ptr of a DebugContext.
95 */
96 static std::shared_ptr<DebugContext> Construct() {
97 return std::shared_ptr<DebugContext>(new DebugContext);
98 }
99
100 /**
101 * Used by the emulation core when a given event has happened. If a breakpoint has been set
102 * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
103 * The current thread then is halted until Resume() is called from another thread (or until
104 * emulation is stopped).
105 * @param event Event which has happened
106 * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called.
107 */
108 void OnEvent(Event event, void* data);
109
110 /**
111 * Resume from the current breakpoint.
112 * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe.
113 */
114 void Resume();
115
116 /**
117 * Delete all set breakpoints and resume emulation.
118 */
119 void ClearBreakpoints() {
120 breakpoints.clear();
121 Resume();
122 }
123
124 // TODO: Evaluate if access to these members should be hidden behind a public interface.
125 std::map<Event, BreakPoint> breakpoints;
126 Event active_breakpoint;
127 bool at_breakpoint = false;
128
129private:
130 /**
131 * Private default constructor to make sure people always construct this through Construct()
132 * instead.
133 */
134 DebugContext() = default;
135
136 /// Mutex protecting current breakpoint state and the observer list.
137 std::mutex breakpoint_mutex;
138
139 /// Used by OnEvent to wait for resumption.
140 std::condition_variable resume_from_breakpoint;
141
142 /// List of registered observers
143 std::list<BreakPointObserver*> breakpoint_observers;
144};
145
146extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
147
15namespace DebugUtils { 148namespace DebugUtils {
16 149
17// Simple utility class for dumping geometry data to an OBJ file 150// Simple utility class for dumping geometry data to an OBJ file