debug_utils.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <array>
  6. #include <condition_variable>
  7. #include <list>
  8. #include <map>
  9. #include <memory>
  10. #include <mutex>
  11. #include <vector>
  12. #include "common/vector_math.h"
  13. #include "core/tracer/recorder.h"
  14. #include "video_core/pica.h"
  15. #include "video_core/shader/shader.h"
  16. namespace Pica {
  17. class DebugContext {
  18. public:
  19. enum class Event {
  20. FirstEvent = 0,
  21. PicaCommandLoaded = FirstEvent,
  22. PicaCommandProcessed,
  23. IncomingPrimitiveBatch,
  24. FinishedPrimitiveBatch,
  25. VertexLoaded,
  26. IncomingDisplayTransfer,
  27. GSPCommandProcessed,
  28. BufferSwapped,
  29. NumEvents
  30. };
  31. /**
  32. * Inherit from this class to be notified of events registered to some debug context.
  33. * Most importantly this is used for our debugger GUI.
  34. *
  35. * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
  36. * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access
  37. * @todo Evaluate an alternative interface, in which there is only one managing observer and 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) : context_weak(debug_context) {
  43. std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
  44. debug_context->breakpoint_observers.push_back(this);
  45. }
  46. virtual ~BreakPointObserver() {
  47. auto context = context_weak.lock();
  48. if (context) {
  49. std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
  50. context->breakpoint_observers.remove(this);
  51. // If we are the last observer to be destroyed, tell the debugger context that
  52. // it is free to continue. In particular, this is required for a proper Citra
  53. // shutdown, when the emulation thread is waiting at a breakpoint.
  54. if (context->breakpoint_observers.empty())
  55. context->Resume();
  56. }
  57. }
  58. /**
  59. * Action to perform when a breakpoint was reached.
  60. * @param event Type of event which triggered the breakpoint
  61. * @param data Optional data pointer (if unused, this is a nullptr)
  62. * @note This function will perform nothing unless it is overridden in the child class.
  63. */
  64. virtual void OnPicaBreakPointHit(Event, void*) {
  65. }
  66. /**
  67. * Action to perform when emulation is resumed from a breakpoint.
  68. * @note This function will perform nothing unless it is overridden in the child class.
  69. */
  70. virtual void OnPicaResume() {
  71. }
  72. protected:
  73. /**
  74. * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
  75. * context_weak.lock(), always compare the result against nullptr.
  76. */
  77. std::weak_ptr<DebugContext> context_weak;
  78. };
  79. /**
  80. * Simple structure defining a breakpoint state
  81. */
  82. struct BreakPoint {
  83. bool enabled = false;
  84. };
  85. /**
  86. * Static constructor used to create a shared_ptr of a DebugContext.
  87. */
  88. static std::shared_ptr<DebugContext> Construct() {
  89. return std::shared_ptr<DebugContext>(new DebugContext);
  90. }
  91. /**
  92. * Used by the emulation core when a given event has happened. If a breakpoint has been set
  93. * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
  94. * The current thread then is halted until Resume() is called from another thread (or until
  95. * emulation is stopped).
  96. * @param event Event which has happened
  97. * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called.
  98. */
  99. void OnEvent(Event event, void* data) {
  100. // This check is left in the header to allow the compiler to inline it.
  101. if (!breakpoints[(int)event].enabled)
  102. return;
  103. // For the rest of event handling, call a separate function.
  104. DoOnEvent(event, data);
  105. }
  106. void DoOnEvent(Event event, void *data);
  107. /**
  108. * Resume from the current breakpoint.
  109. * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe.
  110. */
  111. void Resume();
  112. /**
  113. * Delete all set breakpoints and resume emulation.
  114. */
  115. void ClearBreakpoints() {
  116. for (auto &bp : breakpoints) {
  117. bp.enabled = false;
  118. }
  119. Resume();
  120. }
  121. // TODO: Evaluate if access to these members should be hidden behind a public interface.
  122. std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
  123. Event active_breakpoint;
  124. bool at_breakpoint = false;
  125. std::shared_ptr<CiTrace::Recorder> recorder = nullptr;
  126. private:
  127. /**
  128. * Private default constructor to make sure people always construct this through Construct()
  129. * instead.
  130. */
  131. DebugContext() = default;
  132. /// Mutex protecting current breakpoint state and the observer list.
  133. std::mutex breakpoint_mutex;
  134. /// Used by OnEvent to wait for resumption.
  135. std::condition_variable resume_from_breakpoint;
  136. /// List of registered observers
  137. std::list<BreakPointObserver*> breakpoint_observers;
  138. };
  139. extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
  140. namespace DebugUtils {
  141. #define PICA_DUMP_TEXTURES 0
  142. #define PICA_LOG_TEV 0
  143. void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
  144. const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
  145. // Utility class to log Pica commands.
  146. struct PicaTrace {
  147. struct Write {
  148. u16 cmd_id;
  149. u16 mask;
  150. u32 value;
  151. };
  152. std::vector<Write> writes;
  153. };
  154. void StartPicaTracing();
  155. bool IsPicaTracing();
  156. void OnPicaRegWrite(PicaTrace::Write write);
  157. std::unique_ptr<PicaTrace> FinishPicaTracing();
  158. struct TextureInfo {
  159. PAddr physical_address;
  160. int width;
  161. int height;
  162. int stride;
  163. Pica::Regs::TextureFormat format;
  164. static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config,
  165. const Pica::Regs::TextureFormat& format);
  166. };
  167. /**
  168. * Lookup texel located at the given coordinates and return an RGBA vector of its color.
  169. * @param source Source pointer to read data from
  170. * @param s,t Texture coordinates to read from
  171. * @param info TextureInfo object describing the texture setup
  172. * @param disable_alpha This is used for debug widgets which use this method to display textures without providing a good way to visualize alpha by themselves. If true, this will return 255 for the alpha component, and either drop the information entirely or store it in an "unused" color channel.
  173. * @todo Eventually we should get rid of the disable_alpha parameter.
  174. */
  175. const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info,
  176. bool disable_alpha = false);
  177. void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
  178. void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages);
  179. } // namespace
  180. } // namespace