debug_utils.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <algorithm>
  6. #include <array>
  7. #include <condition_variable>
  8. #include <iterator>
  9. #include <list>
  10. #include <map>
  11. #include <memory>
  12. #include <mutex>
  13. #include <string>
  14. #include <utility>
  15. #include <vector>
  16. #include "common/common_types.h"
  17. #include "common/vector_math.h"
  18. namespace Tegra {
  19. class DebugContext {
  20. public:
  21. enum class Event {
  22. FirstEvent = 0,
  23. MaxwellCommandLoaded = FirstEvent,
  24. MaxwellCommandProcessed,
  25. IncomingPrimitiveBatch,
  26. FinishedPrimitiveBatch,
  27. NumEvents
  28. };
  29. /**
  30. * Inherit from this class to be notified of events registered to some debug context.
  31. * Most importantly this is used for our debugger GUI.
  32. *
  33. * To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods.
  34. * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
  35. * access
  36. * @todo Evaluate an alternative interface, in which there is only one managing observer and
  37. * multiple child observers running (by design) on the same thread.
  38. */
  39. class BreakPointObserver {
  40. public:
  41. /// Constructs the object such that it observes events of the given DebugContext.
  42. BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
  43. : 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. virtual ~BreakPointObserver() {
  48. auto context = context_weak.lock();
  49. if (context) {
  50. {
  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 yuzu
  56. // shutdown, when the emulation thread is waiting at a breakpoint.
  57. if (context->breakpoint_observers.empty())
  58. context->Resume();
  59. }
  60. }
  61. /**
  62. * Action to perform when a breakpoint was reached.
  63. * @param event Type of event which triggered the breakpoint
  64. * @param data Optional data pointer (if unused, this is a nullptr)
  65. * @note This function will perform nothing unless it is overridden in the child class.
  66. */
  67. virtual void OnMaxwellBreakPointHit(Event event, void* data) {}
  68. /**
  69. * Action to perform when emulation is resumed from a breakpoint.
  70. * @note This function will perform nothing unless it is overridden in the child class.
  71. */
  72. virtual void OnMaxwellResume() {}
  73. protected:
  74. /**
  75. * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
  76. * context_weak.lock(), always compare the result against nullptr.
  77. */
  78. std::weak_ptr<DebugContext> context_weak;
  79. };
  80. /**
  81. * Simple structure defining a breakpoint state
  82. */
  83. struct BreakPoint {
  84. bool enabled = false;
  85. };
  86. /**
  87. * Static constructor used to create a shared_ptr of a DebugContext.
  88. */
  89. static std::shared_ptr<DebugContext> Construct() {
  90. return std::shared_ptr<DebugContext>(new DebugContext);
  91. }
  92. /**
  93. * Used by the emulation core when a given event has happened. If a breakpoint has been set
  94. * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
  95. * The current thread then is halted until Resume() is called from another thread (or until
  96. * emulation is stopped).
  97. * @param event Event which has happened
  98. * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
  99. * Resume() is called.
  100. */
  101. void OnEvent(Event event, void* data) {
  102. // This check is left in the header to allow the compiler to inline it.
  103. if (!breakpoints[(int)event].enabled)
  104. return;
  105. // For the rest of event handling, call a separate function.
  106. DoOnEvent(event, data);
  107. }
  108. void DoOnEvent(Event event, void* data);
  109. /**
  110. * Resume from the current breakpoint.
  111. * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
  112. * Calling from any other thread is safe.
  113. */
  114. void Resume();
  115. /**
  116. * Delete all set breakpoints and resume emulation.
  117. */
  118. void ClearBreakpoints() {
  119. for (auto& bp : breakpoints) {
  120. bp.enabled = false;
  121. }
  122. Resume();
  123. }
  124. // TODO: Evaluate if access to these members should be hidden behind a public interface.
  125. std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
  126. Event active_breakpoint;
  127. bool at_breakpoint = false;
  128. private:
  129. /**
  130. * Private default constructor to make sure people always construct this through Construct()
  131. * instead.
  132. */
  133. DebugContext() = default;
  134. /// Mutex protecting current breakpoint state and the observer list.
  135. std::mutex breakpoint_mutex;
  136. /// Used by OnEvent to wait for resumption.
  137. std::condition_variable resume_from_breakpoint;
  138. /// List of registered observers
  139. std::list<BreakPointObserver*> breakpoint_observers;
  140. };
  141. } // namespace Tegra