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