1 // Copyright 2019 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #pragma once 6 7 #include <atomic> 8 #include <functional> 9 #include <memory> 10 #include <set> 11 #include <shared_mutex> 12 #include <string> 13 #include <unordered_map> 14 #include <vector> 15 #include "common/common_types.h" 16 17 namespace Kernel { 18 class ClientSession; 19 class Thread; 20 } // namespace Kernel 21 22 namespace IPCDebugger { 23 24 /** 25 * Record of a kernel object, for debugging purposes. 26 */ 27 struct ObjectInfo { 28 std::string type; 29 std::string name; 30 int id = -1; 31 }; 32 33 /** 34 * Status of a request. 35 */ 36 enum class RequestStatus { 37 Invalid, ///< Invalid status 38 Sent, ///< The request is sent to the kernel and is waiting to be handled 39 Handling, ///< The request is being handled 40 Handled, ///< The request is handled with reply sent 41 HLEUnimplemented, ///< The request is unimplemented by HLE, and unhandled 42 }; 43 44 /** 45 * Record of an IPC request. 46 */ 47 struct RequestRecord { 48 int id; 49 RequestStatus status = RequestStatus::Invalid; 50 ObjectInfo client_process; 51 ObjectInfo client_thread; 52 ObjectInfo client_session; 53 ObjectInfo client_port; // Not available for portless 54 ObjectInfo server_process; // Only available for LLE requests 55 ObjectInfo server_thread; // Only available for LLE requests 56 ObjectInfo server_session; 57 std::string function_name; // Not available for LLE or portless 58 bool is_hle = false; 59 // Request info is only available when status is not `Invalid` or `Sent` 60 std::vector<u32> untranslated_request_cmdbuf; 61 std::vector<u32> translated_request_cmdbuf; 62 // Reply info is only available when status is `Handled` 63 std::vector<u32> untranslated_reply_cmdbuf; 64 std::vector<u32> translated_reply_cmdbuf; 65 }; 66 67 using CallbackType = std::function<void(const RequestRecord&)>; 68 using CallbackHandle = std::shared_ptr<CallbackType>; 69 70 class Recorder { 71 public: 72 explicit Recorder(); 73 ~Recorder(); 74 75 /** 76 * Returns whether the recorder is enabled. 77 */ 78 bool IsEnabled() const; 79 80 /** 81 * Registers a request into the recorder. The request is then assoicated with the client thread. 82 */ 83 void RegisterRequest(const std::shared_ptr<Kernel::ClientSession>& client_session, 84 const std::shared_ptr<Kernel::Thread>& client_thread); 85 86 /** 87 * Sets the request information of the request record associated with the client thread. 88 * When the server thread is empty, the request will be considered HLE. 89 */ 90 void SetRequestInfo(const std::shared_ptr<Kernel::Thread>& client_thread, 91 std::vector<u32> untranslated_cmdbuf, std::vector<u32> translated_cmdbuf, 92 const std::shared_ptr<Kernel::Thread>& server_thread = {}); 93 94 /** 95 * Sets the reply information of the request record assoicated with the client thread. 96 * The request is then unlinked from the client thread. 97 */ 98 void SetReplyInfo(const std::shared_ptr<Kernel::Thread>& client_thread, 99 std::vector<u32> untranslated_cmdbuf, std::vector<u32> translated_cmdbuf); 100 101 /** 102 * Set the status of a record to HLEUnimplemented. 103 */ 104 void SetHLEUnimplemented(const std::shared_ptr<Kernel::Thread>& client_thread); 105 106 /** 107 * Set the status of the debugger (enabled/disabled). 108 */ 109 void SetEnabled(bool enabled); 110 111 CallbackHandle BindCallback(CallbackType callback); 112 void UnbindCallback(const CallbackHandle& handle); 113 114 private: 115 void InvokeCallbacks(const RequestRecord& request); 116 117 std::unordered_map<u32, std::unique_ptr<RequestRecord>> record_map; 118 int record_count{}; 119 120 // Temporary client session map for function name handling 121 std::unordered_map<u32, std::shared_ptr<Kernel::ClientSession>> client_session_map; 122 123 std::atomic_bool enabled{false}; 124 125 std::set<CallbackHandle> callbacks; 126 mutable std::shared_mutex callback_mutex; 127 }; 128 129 } // namespace IPCDebugger 130