From 2c71ec70527abd091d69f1fdd30aaf95d815214a Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 25 Oct 2014 18:02:26 +0200 Subject: Pica/DebugUtils: Add breakpoint functionality. --- src/video_core/debug_utils/debug_utils.h | 133 +++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) (limited to 'src/video_core/debug_utils/debug_utils.h') diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index b1558cfa..26b26e22 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -5,13 +5,146 @@ #pragma once #include +#include +#include +#include #include +#include #include #include "video_core/pica.h" namespace Pica { +class DebugContext { +public: + enum class Event { + FirstEvent = 0, + + CommandLoaded = FirstEvent, + CommandProcessed, + IncomingPrimitiveBatch, + FinishedPrimitiveBatch, + + NumEvents + }; + + /** + * Inherit from this class to be notified of events registered to some debug context. + * Most importantly this is used for our debugger GUI. + * + * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods. + * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access + * @todo Evaluate an alternative interface, in which there is only one managing observer and multiple child observers running (by design) on the same thread. + */ + class BreakPointObserver { + public: + /// Constructs the object such that it observes events of the given DebugContext. + BreakPointObserver(std::shared_ptr debug_context) : context_weak(debug_context) { + std::unique_lock lock(debug_context->breakpoint_mutex); + debug_context->breakpoint_observers.push_back(this); + } + + virtual ~BreakPointObserver() { + auto context = context_weak.lock(); + if (context) { + std::unique_lock lock(context->breakpoint_mutex); + context->breakpoint_observers.remove(this); + + // If we are the last observer to be destroyed, tell the debugger context that + // it is free to continue. In particular, this is required for a proper Citra + // shutdown, when the emulation thread is waiting at a breakpoint. + if (context->breakpoint_observers.empty()) + context->Resume(); + } + } + + /** + * Action to perform when a breakpoint was reached. + * @param event Type of event which triggered the breakpoint + * @param data Optional data pointer (if unused, this is a nullptr) + * @note This function will perform nothing unless it is overridden in the child class. + */ + virtual void OnPicaBreakPointHit(Event, void*) { + } + + /** + * Action to perform when emulation is resumed from a breakpoint. + * @note This function will perform nothing unless it is overridden in the child class. + */ + virtual void OnPicaResume() { + } + + protected: + /** + * Weak context pointer. This need not be valid, so when requesting a shared_ptr via + * context_weak.lock(), always compare the result against nullptr. + */ + std::weak_ptr context_weak; + }; + + /** + * Simple structure defining a breakpoint state + */ + struct BreakPoint { + bool enabled = false; + }; + + /** + * Static constructor used to create a shared_ptr of a DebugContext. + */ + static std::shared_ptr Construct() { + return std::shared_ptr(new DebugContext); + } + + /** + * Used by the emulation core when a given event has happened. If a breakpoint has been set + * for this event, OnEvent calls the event handlers of the registered breakpoint observers. + * The current thread then is halted until Resume() is called from another thread (or until + * emulation is stopped). + * @param event Event which has happened + * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called. + */ + void OnEvent(Event event, void* data); + + /** + * Resume from the current breakpoint. + * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe. + */ + void Resume(); + + /** + * Delete all set breakpoints and resume emulation. + */ + void ClearBreakpoints() { + breakpoints.clear(); + Resume(); + } + + // TODO: Evaluate if access to these members should be hidden behind a public interface. + std::map breakpoints; + Event active_breakpoint; + bool at_breakpoint = false; + +private: + /** + * Private default constructor to make sure people always construct this through Construct() + * instead. + */ + DebugContext() = default; + + /// Mutex protecting current breakpoint state and the observer list. + std::mutex breakpoint_mutex; + + /// Used by OnEvent to wait for resumption. + std::condition_variable resume_from_breakpoint; + + /// List of registered observers + std::list breakpoint_observers; +}; + +extern std::shared_ptr g_debug_context; // TODO: Get rid of this global + namespace DebugUtils { // Simple utility class for dumping geometry data to an OBJ file -- cgit v1.2.3