debug_utils.h 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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. #include "video_core/pica.h"
  19. namespace CiTrace {
  20. class Recorder;
  21. }
  22. namespace Pica {
  23. namespace Shader {
  24. struct ShaderSetup;
  25. }
  26. class DebugContext {
  27. public:
  28. enum class Event {
  29. FirstEvent = 0,
  30. PicaCommandLoaded = FirstEvent,
  31. PicaCommandProcessed,
  32. IncomingPrimitiveBatch,
  33. FinishedPrimitiveBatch,
  34. VertexShaderInvocation,
  35. IncomingDisplayTransfer,
  36. GSPCommandProcessed,
  37. BufferSwapped,
  38. NumEvents
  39. };
  40. /**
  41. * Inherit from this class to be notified of events registered to some debug context.
  42. * Most importantly this is used for our debugger GUI.
  43. *
  44. * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
  45. * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
  46. * access
  47. * @todo Evaluate an alternative interface, in which there is only one managing observer and
  48. * multiple child observers running (by design) on the same thread.
  49. */
  50. class BreakPointObserver {
  51. public:
  52. /// Constructs the object such that it observes events of the given DebugContext.
  53. BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
  54. : context_weak(debug_context) {
  55. std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
  56. debug_context->breakpoint_observers.push_back(this);
  57. }
  58. virtual ~BreakPointObserver() {
  59. auto context = context_weak.lock();
  60. if (context) {
  61. std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
  62. context->breakpoint_observers.remove(this);
  63. // If we are the last observer to be destroyed, tell the debugger context that
  64. // it is free to continue. In particular, this is required for a proper Citra
  65. // shutdown, when the emulation thread is waiting at a breakpoint.
  66. if (context->breakpoint_observers.empty())
  67. context->Resume();
  68. }
  69. }
  70. /**
  71. * Action to perform when a breakpoint was reached.
  72. * @param event Type of event which triggered the breakpoint
  73. * @param data Optional data pointer (if unused, this is a nullptr)
  74. * @note This function will perform nothing unless it is overridden in the child class.
  75. */
  76. virtual void OnPicaBreakPointHit(Event, void*) {}
  77. /**
  78. * Action to perform when emulation is resumed from a breakpoint.
  79. * @note This function will perform nothing unless it is overridden in the child class.
  80. */
  81. virtual void OnPicaResume() {}
  82. protected:
  83. /**
  84. * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
  85. * context_weak.lock(), always compare the result against nullptr.
  86. */
  87. std::weak_ptr<DebugContext> context_weak;
  88. };
  89. /**
  90. * Simple structure defining a breakpoint state
  91. */
  92. struct BreakPoint {
  93. bool enabled = false;
  94. };
  95. /**
  96. * Static constructor used to create a shared_ptr of a DebugContext.
  97. */
  98. static std::shared_ptr<DebugContext> Construct() {
  99. return std::shared_ptr<DebugContext>(new DebugContext);
  100. }
  101. /**
  102. * Used by the emulation core when a given event has happened. If a breakpoint has been set
  103. * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
  104. * The current thread then is halted until Resume() is called from another thread (or until
  105. * emulation is stopped).
  106. * @param event Event which has happened
  107. * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
  108. * Resume() is called.
  109. */
  110. void OnEvent(Event event, void* data) {
  111. // This check is left in the header to allow the compiler to inline it.
  112. if (!breakpoints[(int)event].enabled)
  113. return;
  114. // For the rest of event handling, call a separate function.
  115. DoOnEvent(event, data);
  116. }
  117. void DoOnEvent(Event event, void* data);
  118. /**
  119. * Resume from the current breakpoint.
  120. * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
  121. * Calling from any other thread is safe.
  122. */
  123. void Resume();
  124. /**
  125. * Delete all set breakpoints and resume emulation.
  126. */
  127. void ClearBreakpoints() {
  128. for (auto& bp : breakpoints) {
  129. bp.enabled = false;
  130. }
  131. Resume();
  132. }
  133. // TODO: Evaluate if access to these members should be hidden behind a public interface.
  134. std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
  135. Event active_breakpoint;
  136. bool at_breakpoint = false;
  137. std::shared_ptr<CiTrace::Recorder> recorder = nullptr;
  138. private:
  139. /**
  140. * Private default constructor to make sure people always construct this through Construct()
  141. * instead.
  142. */
  143. DebugContext() = default;
  144. /// Mutex protecting current breakpoint state and the observer list.
  145. std::mutex breakpoint_mutex;
  146. /// Used by OnEvent to wait for resumption.
  147. std::condition_variable resume_from_breakpoint;
  148. /// List of registered observers
  149. std::list<BreakPointObserver*> breakpoint_observers;
  150. };
  151. extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
  152. namespace DebugUtils {
  153. #define PICA_DUMP_TEXTURES 0
  154. #define PICA_LOG_TEV 0
  155. void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
  156. const Shader::ShaderSetup& setup,
  157. const Regs::VSOutputAttributes* output_attributes);
  158. // Utility class to log Pica commands.
  159. struct PicaTrace {
  160. struct Write {
  161. u16 cmd_id;
  162. u16 mask;
  163. u32 value;
  164. };
  165. std::vector<Write> writes;
  166. };
  167. extern bool g_is_pica_tracing;
  168. void StartPicaTracing();
  169. inline bool IsPicaTracing() {
  170. return g_is_pica_tracing;
  171. }
  172. void OnPicaRegWrite(PicaTrace::Write write);
  173. std::unique_ptr<PicaTrace> FinishPicaTracing();
  174. void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
  175. std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
  176. std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
  177. /// Dumps the Tev stage config to log at trace level
  178. void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages);
  179. /**
  180. * Used in the vertex loader to merge access records. TODO: Investigate if actually useful.
  181. */
  182. class MemoryAccessTracker {
  183. /// Combine overlapping and close ranges
  184. void SimplifyRanges() {
  185. for (auto it = ranges.begin(); it != ranges.end(); ++it) {
  186. // NOTE: We add 32 to the range end address to make sure "close" ranges are combined,
  187. // too
  188. auto it2 = std::next(it);
  189. while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) {
  190. it->second = std::max(it->second, it2->first + it2->second - it->first);
  191. it2 = ranges.erase(it2);
  192. }
  193. }
  194. }
  195. public:
  196. /// Record a particular memory access in the list
  197. void AddAccess(u32 paddr, u32 size) {
  198. // Create new range or extend existing one
  199. ranges[paddr] = std::max(ranges[paddr], size);
  200. // Simplify ranges...
  201. SimplifyRanges();
  202. }
  203. /// Map of accessed ranges (mapping start address to range size)
  204. std::map<u32, u32> ranges;
  205. };
  206. } // namespace
  207. } // namespace