2014-08-17 14:06:58 +02:00
|
|
|
// Copyright 2014 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2014-10-25 18:02:26 +02:00
|
|
|
#include <condition_variable>
|
2016-04-30 17:34:51 +02:00
|
|
|
#include <iterator>
|
2014-10-25 18:02:26 +02:00
|
|
|
#include <list>
|
2014-08-14 19:21:55 +02:00
|
|
|
#include <memory>
|
2014-10-25 18:02:26 +02:00
|
|
|
#include <mutex>
|
2016-04-30 17:34:51 +02:00
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
2014-08-17 14:06:58 +02:00
|
|
|
#include <vector>
|
2016-04-30 17:34:51 +02:00
|
|
|
#include "common/common_types.h"
|
2023-12-28 11:46:57 +01:00
|
|
|
#include "video_core/pica/regs_rasterizer.h"
|
2016-04-30 17:34:51 +02:00
|
|
|
|
|
|
|
namespace CiTrace {
|
|
|
|
class Recorder;
|
|
|
|
}
|
2014-08-17 14:06:58 +02:00
|
|
|
|
|
|
|
namespace Pica {
|
|
|
|
|
2023-12-28 11:46:57 +01:00
|
|
|
struct ShaderRegs;
|
2016-04-30 17:34:51 +02:00
|
|
|
struct ShaderSetup;
|
|
|
|
|
2014-10-25 18:02:26 +02:00
|
|
|
class DebugContext {
|
|
|
|
public:
|
|
|
|
enum class Event {
|
|
|
|
FirstEvent = 0,
|
|
|
|
|
2015-07-23 01:20:54 +02:00
|
|
|
PicaCommandLoaded = FirstEvent,
|
|
|
|
PicaCommandProcessed,
|
2014-10-25 18:02:26 +02:00
|
|
|
IncomingPrimitiveBatch,
|
|
|
|
FinishedPrimitiveBatch,
|
2016-05-04 10:21:51 +02:00
|
|
|
VertexShaderInvocation,
|
2015-07-23 01:20:54 +02:00
|
|
|
IncomingDisplayTransfer,
|
|
|
|
GSPCommandProcessed,
|
|
|
|
BufferSwapped,
|
2014-10-25 18:02:26 +02:00
|
|
|
|
|
|
|
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.
|
2016-09-18 02:38:01 +02:00
|
|
|
* @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.
|
2014-10-25 18:02:26 +02:00
|
|
|
*/
|
|
|
|
class BreakPointObserver {
|
|
|
|
public:
|
|
|
|
/// Constructs the object such that it observes events of the given DebugContext.
|
2016-09-18 02:38:01 +02:00
|
|
|
BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
|
|
|
|
: context_weak(debug_context) {
|
2019-04-01 18:29:59 +02:00
|
|
|
std::unique_lock lock{debug_context->breakpoint_mutex};
|
2014-10-25 18:02:26 +02:00
|
|
|
debug_context->breakpoint_observers.push_back(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~BreakPointObserver() {
|
|
|
|
auto context = context_weak.lock();
|
|
|
|
if (context) {
|
2019-04-01 18:29:59 +02:00
|
|
|
std::unique_lock lock(context->breakpoint_mutex);
|
2014-10-25 18:02:26 +02:00
|
|
|
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.
|
|
|
|
*/
|
2023-12-28 11:46:57 +01:00
|
|
|
virtual void OnPicaBreakPointHit(Event event, const void* data) {}
|
2014-10-25 18:02:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Action to perform when emulation is resumed from a breakpoint.
|
|
|
|
* @note This function will perform nothing unless it is overridden in the child class.
|
|
|
|
*/
|
2016-09-19 03:01:46 +02:00
|
|
|
virtual void OnPicaResume() {}
|
2014-10-25 18:02:26 +02:00
|
|
|
|
|
|
|
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<DebugContext> 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<DebugContext> Construct() {
|
|
|
|
return std::shared_ptr<DebugContext>(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
|
2016-09-18 02:38:01 +02:00
|
|
|
* @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
|
|
|
|
* Resume() is called.
|
2014-10-25 18:02:26 +02:00
|
|
|
*/
|
2023-12-28 11:46:57 +01:00
|
|
|
void OnEvent(Event event, const void* data) {
|
2016-04-24 14:19:49 +02:00
|
|
|
// This check is left in the header to allow the compiler to inline it.
|
|
|
|
if (!breakpoints[(int)event].enabled)
|
|
|
|
return;
|
|
|
|
// For the rest of event handling, call a separate function.
|
|
|
|
DoOnEvent(event, data);
|
|
|
|
}
|
|
|
|
|
2023-12-28 11:46:57 +01:00
|
|
|
void DoOnEvent(Event event, const void* data);
|
2014-10-25 18:02:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Resume from the current breakpoint.
|
2016-09-18 02:38:01 +02:00
|
|
|
* @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
|
|
|
|
* Calling from any other thread is safe.
|
2014-10-25 18:02:26 +02:00
|
|
|
*/
|
|
|
|
void Resume();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete all set breakpoints and resume emulation.
|
|
|
|
*/
|
|
|
|
void ClearBreakpoints() {
|
2016-09-18 02:38:01 +02:00
|
|
|
for (auto& bp : breakpoints) {
|
2016-04-24 14:19:49 +02:00
|
|
|
bp.enabled = false;
|
|
|
|
}
|
2014-10-25 18:02:26 +02:00
|
|
|
Resume();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Evaluate if access to these members should be hidden behind a public interface.
|
2016-04-24 14:19:49 +02:00
|
|
|
std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
|
2014-10-25 18:02:26 +02:00
|
|
|
Event active_breakpoint;
|
|
|
|
bool at_breakpoint = false;
|
|
|
|
|
2015-04-04 12:57:31 +02:00
|
|
|
std::shared_ptr<CiTrace::Recorder> recorder = nullptr;
|
|
|
|
|
2014-10-25 18:02:26 +02:00
|
|
|
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<BreakPointObserver*> breakpoint_observers;
|
|
|
|
};
|
|
|
|
|
|
|
|
extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
|
|
|
|
|
2014-08-17 14:06:58 +02:00
|
|
|
namespace DebugUtils {
|
|
|
|
|
2023-12-28 11:46:57 +01:00
|
|
|
void DumpShader(const std::string& filename, const ShaderRegs& config, const ShaderSetup& setup,
|
2017-01-28 05:16:36 +01:00
|
|
|
const RasterizerRegs::VSOutputAttributes* output_attributes);
|
2014-08-14 19:21:55 +02:00
|
|
|
|
|
|
|
// Utility class to log Pica commands.
|
|
|
|
struct PicaTrace {
|
2015-07-25 22:00:40 +02:00
|
|
|
struct Write {
|
|
|
|
u16 cmd_id;
|
|
|
|
u16 mask;
|
|
|
|
u32 value;
|
2014-08-14 19:21:55 +02:00
|
|
|
};
|
|
|
|
std::vector<Write> writes;
|
|
|
|
};
|
|
|
|
|
2016-12-15 07:01:24 +01:00
|
|
|
extern bool g_is_pica_tracing;
|
|
|
|
|
2014-08-14 19:21:55 +02:00
|
|
|
void StartPicaTracing();
|
2016-12-15 07:01:24 +01:00
|
|
|
inline bool IsPicaTracing() {
|
|
|
|
return g_is_pica_tracing;
|
|
|
|
}
|
2023-12-28 11:46:57 +01:00
|
|
|
void OnPicaRegWrite(u16 cmd_id, u16 mask, u32 value);
|
2014-08-14 19:21:55 +02:00
|
|
|
std::unique_ptr<PicaTrace> FinishPicaTracing();
|
|
|
|
|
2018-03-09 18:54:43 +01:00
|
|
|
} // namespace DebugUtils
|
2014-08-17 14:06:58 +02:00
|
|
|
|
2018-03-09 18:54:43 +01:00
|
|
|
} // namespace Pica
|