mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 22:00:05 +00:00 
			
		
		
		
	Merge pull request #4847 from zhaowenlan1779/ipc-debugger
core, citra_qt: IPC Recorder
This commit is contained in:
		
						commit
						223bfc9a2a
					
				
					 23 changed files with 1112 additions and 15 deletions
				
			
		|  | @ -134,6 +134,8 @@ add_library(core STATIC | |||
|     hle/kernel/hle_ipc.h | ||||
|     hle/kernel/ipc.cpp | ||||
|     hle/kernel/ipc.h | ||||
|     hle/kernel/ipc_debugger/recorder.cpp | ||||
|     hle/kernel/ipc_debugger/recorder.h | ||||
|     hle/kernel/kernel.cpp | ||||
|     hle/kernel/kernel.h | ||||
|     hle/kernel/memory.cpp | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/kernel/ipc_debugger/recorder.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| 
 | ||||
|  | @ -107,6 +108,13 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr | |||
| 
 | ||||
|     std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin()); | ||||
| 
 | ||||
|     const bool should_record = kernel.GetIPCRecorder().IsEnabled(); | ||||
| 
 | ||||
|     std::vector<u32> untranslated_cmdbuf; | ||||
|     if (should_record) { | ||||
|         untranslated_cmdbuf = std::vector<u32>{src_cmdbuf, src_cmdbuf + command_size}; | ||||
|     } | ||||
| 
 | ||||
|     std::size_t i = untranslated_size; | ||||
|     while (i < command_size) { | ||||
|         u32 descriptor = cmd_buf[i] = src_cmdbuf[i]; | ||||
|  | @ -160,6 +168,12 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (should_record) { | ||||
|         std::vector<u32> translated_cmdbuf{cmd_buf.begin(), cmd_buf.begin() + command_size}; | ||||
|         kernel.GetIPCRecorder().SetRequestInfo(SharedFrom(thread), std::move(untranslated_cmdbuf), | ||||
|                                                std::move(translated_cmdbuf)); | ||||
|     } | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
|  | @ -173,6 +187,13 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, | |||
| 
 | ||||
|     std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf); | ||||
| 
 | ||||
|     const bool should_record = kernel.GetIPCRecorder().IsEnabled(); | ||||
| 
 | ||||
|     std::vector<u32> untranslated_cmdbuf; | ||||
|     if (should_record) { | ||||
|         untranslated_cmdbuf = std::vector<u32>{cmd_buf.begin(), cmd_buf.begin() + command_size}; | ||||
|     } | ||||
| 
 | ||||
|     std::size_t i = untranslated_size; | ||||
|     while (i < command_size) { | ||||
|         u32 descriptor = dst_cmdbuf[i] = cmd_buf[i]; | ||||
|  | @ -225,6 +246,12 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (should_record) { | ||||
|         std::vector<u32> translated_cmdbuf{dst_cmdbuf, dst_cmdbuf + command_size}; | ||||
|         kernel.GetIPCRecorder().SetReplyInfo(SharedFrom(thread), std::move(untranslated_cmdbuf), | ||||
|                                              std::move(translated_cmdbuf)); | ||||
|     } | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
|  | @ -233,6 +260,12 @@ MappedBuffer& HLERequestContext::GetMappedBuffer(u32 id_from_cmdbuf) { | |||
|     return request_mapped_buffers[id_from_cmdbuf]; | ||||
| } | ||||
| 
 | ||||
| void HLERequestContext::ReportUnimplemented() const { | ||||
|     if (kernel.GetIPCRecorder().IsEnabled()) { | ||||
|         kernel.GetIPCRecorder().SetHLEUnimplemented(SharedFrom(thread)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, const Process& process, u32 descriptor, | ||||
|                            VAddr address, u32 id) | ||||
|     : memory(&memory), id(id), address(address), process(&process) { | ||||
|  |  | |||
|  | @ -234,6 +234,9 @@ public: | |||
|     /// Writes data from this context back to the requesting process/thread.
 | ||||
|     ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process) const; | ||||
| 
 | ||||
|     /// Reports an unimplemented function.
 | ||||
|     void ReportUnimplemented() const; | ||||
| 
 | ||||
| private: | ||||
|     KernelSystem& kernel; | ||||
|     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/ipc.h" | ||||
| #include "core/hle/kernel/ipc_debugger/recorder.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
|  | @ -16,7 +17,8 @@ | |||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ResultCode TranslateCommandBuffer(Memory::MemorySystem& memory, std::shared_ptr<Thread> src_thread, | ||||
| ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySystem& memory, | ||||
|                                   std::shared_ptr<Thread> src_thread, | ||||
|                                   std::shared_ptr<Thread> dst_thread, VAddr src_address, | ||||
|                                   VAddr dst_address, | ||||
|                                   std::vector<MappedBufferContext>& mapped_buffer_context, | ||||
|  | @ -37,6 +39,13 @@ ResultCode TranslateCommandBuffer(Memory::MemorySystem& memory, std::shared_ptr< | |||
|     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | ||||
|     memory.ReadBlock(*src_process, src_address, cmd_buf.data(), command_size * sizeof(u32)); | ||||
| 
 | ||||
|     const bool should_record = kernel.GetIPCRecorder().IsEnabled(); | ||||
| 
 | ||||
|     std::vector<u32> untranslated_cmdbuf; | ||||
|     if (should_record) { | ||||
|         untranslated_cmdbuf = std::vector<u32>{cmd_buf.begin(), cmd_buf.begin() + command_size}; | ||||
|     } | ||||
| 
 | ||||
|     std::size_t i = untranslated_size; | ||||
|     while (i < command_size) { | ||||
|         u32 descriptor = cmd_buf[i]; | ||||
|  | @ -218,6 +227,17 @@ ResultCode TranslateCommandBuffer(Memory::MemorySystem& memory, std::shared_ptr< | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (should_record) { | ||||
|         std::vector<u32> translated_cmdbuf{cmd_buf.begin(), cmd_buf.begin() + command_size}; | ||||
|         if (reply) { | ||||
|             kernel.GetIPCRecorder().SetReplyInfo(dst_thread, std::move(untranslated_cmdbuf), | ||||
|                                                  std::move(translated_cmdbuf)); | ||||
|         } else { | ||||
|             kernel.GetIPCRecorder().SetRequestInfo(src_thread, std::move(untranslated_cmdbuf), | ||||
|                                                    std::move(translated_cmdbuf), dst_thread); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     memory.WriteBlock(*dst_process, dst_address, cmd_buf.data(), command_size * sizeof(u32)); | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ class MemorySystem; | |||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class KernelSystem; | ||||
| 
 | ||||
| struct MappedBufferContext { | ||||
|     IPC::MappedBufferPermissions permissions; | ||||
|     u32 size; | ||||
|  | @ -27,7 +29,8 @@ struct MappedBufferContext { | |||
| }; | ||||
| 
 | ||||
| /// Performs IPC command buffer translation from one process to another.
 | ||||
| ResultCode TranslateCommandBuffer(Memory::MemorySystem& memory, std::shared_ptr<Thread> src_thread, | ||||
| ResultCode TranslateCommandBuffer(KernelSystem& system, Memory::MemorySystem& memory, | ||||
|                                   std::shared_ptr<Thread> src_thread, | ||||
|                                   std::shared_ptr<Thread> dst_thread, VAddr src_address, | ||||
|                                   VAddr dst_address, | ||||
|                                   std::vector<MappedBufferContext>& mapped_buffer_context, | ||||
|  |  | |||
							
								
								
									
										165
									
								
								src/core/hle/kernel/ipc_debugger/recorder.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								src/core/hle/kernel/ipc_debugger/recorder.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,165 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/ipc_debugger/recorder.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/server_port.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace IPCDebugger { | ||||
| 
 | ||||
| namespace { | ||||
| ObjectInfo GetObjectInfo(const Kernel::Object* object) { | ||||
|     if (object == nullptr) { | ||||
|         return {}; | ||||
|     } | ||||
|     return {object->GetTypeName(), object->GetName(), static_cast<int>(object->GetObjectId())}; | ||||
| } | ||||
| 
 | ||||
| ObjectInfo GetObjectInfo(const Kernel::Thread* thread) { | ||||
|     if (thread == nullptr) { | ||||
|         return {}; | ||||
|     } | ||||
|     return {thread->GetTypeName(), thread->GetName(), static_cast<int>(thread->GetThreadId())}; | ||||
| } | ||||
| 
 | ||||
| ObjectInfo GetObjectInfo(const Kernel::Process* process) { | ||||
|     if (process == nullptr) { | ||||
|         return {}; | ||||
|     } | ||||
|     return {process->GetTypeName(), process->GetName(), static_cast<int>(process->process_id)}; | ||||
| } | ||||
| } // namespace
 | ||||
| 
 | ||||
| Recorder::Recorder() = default; | ||||
| Recorder::~Recorder() = default; | ||||
| 
 | ||||
| bool Recorder::IsEnabled() const { | ||||
|     return enabled.load(std::memory_order_relaxed); | ||||
| } | ||||
| 
 | ||||
| void Recorder::RegisterRequest(const std::shared_ptr<Kernel::ClientSession>& client_session, | ||||
|                                const std::shared_ptr<Kernel::Thread>& client_thread) { | ||||
|     const u32 thread_id = client_thread->GetThreadId(); | ||||
| 
 | ||||
|     RequestRecord record = {/* id */ ++record_count, | ||||
|                             /* status */ RequestStatus::Sent, | ||||
|                             /* client_process */ GetObjectInfo(client_thread->owner_process), | ||||
|                             /* client_thread */ GetObjectInfo(client_thread.get()), | ||||
|                             /* client_session */ GetObjectInfo(client_session.get()), | ||||
|                             /* client_port */ GetObjectInfo(client_session->parent->port.get()), | ||||
|                             /* server_process */ {}, | ||||
|                             /* server_thread */ {}, | ||||
|                             /* server_session */ GetObjectInfo(client_session->parent->server)}; | ||||
|     record_map.insert_or_assign(thread_id, std::make_unique<RequestRecord>(record)); | ||||
|     client_session_map.insert_or_assign(thread_id, client_session); | ||||
| 
 | ||||
|     InvokeCallbacks(record); | ||||
| } | ||||
| 
 | ||||
| void Recorder::SetRequestInfo(const std::shared_ptr<Kernel::Thread>& client_thread, | ||||
|                               std::vector<u32> untranslated_cmdbuf, | ||||
|                               std::vector<u32> translated_cmdbuf, | ||||
|                               const std::shared_ptr<Kernel::Thread>& server_thread) { | ||||
|     const u32 thread_id = client_thread->GetThreadId(); | ||||
|     if (!record_map.count(thread_id)) { | ||||
|         // This is possible when the recorder is enabled after application started
 | ||||
|         LOG_ERROR(Kernel, "No request is assoicated with the thread"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto& record = *record_map[thread_id]; | ||||
|     record.status = RequestStatus::Handling; | ||||
|     record.untranslated_request_cmdbuf = std::move(untranslated_cmdbuf); | ||||
|     record.translated_request_cmdbuf = std::move(translated_cmdbuf); | ||||
| 
 | ||||
|     if (server_thread) { | ||||
|         record.server_process = GetObjectInfo(server_thread->owner_process); | ||||
|         record.server_thread = GetObjectInfo(server_thread.get()); | ||||
|     } else { | ||||
|         record.is_hle = true; | ||||
|     } | ||||
| 
 | ||||
|     // Function name
 | ||||
|     ASSERT_MSG(client_session_map.count(thread_id), "Client session is missing"); | ||||
|     const auto& client_session = client_session_map[thread_id]; | ||||
|     if (client_session->parent->port && | ||||
|         client_session->parent->port->GetServerPort()->hle_handler) { | ||||
| 
 | ||||
|         record.function_name = std::dynamic_pointer_cast<Service::ServiceFrameworkBase>( | ||||
|                                    client_session->parent->port->GetServerPort()->hle_handler) | ||||
|                                    ->GetFunctionName(record.untranslated_request_cmdbuf[0]); | ||||
|     } | ||||
|     client_session_map.erase(thread_id); | ||||
| 
 | ||||
|     InvokeCallbacks(record); | ||||
| } | ||||
| 
 | ||||
| void Recorder::SetReplyInfo(const std::shared_ptr<Kernel::Thread>& client_thread, | ||||
|                             std::vector<u32> untranslated_cmdbuf, | ||||
|                             std::vector<u32> translated_cmdbuf) { | ||||
|     const u32 thread_id = client_thread->GetThreadId(); | ||||
|     if (!record_map.count(thread_id)) { | ||||
|         // This is possible when the recorder is enabled after application started
 | ||||
|         LOG_ERROR(Kernel, "No request is assoicated with the thread"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto& record = *record_map[thread_id]; | ||||
|     if (record.status != RequestStatus::HLEUnimplemented) { | ||||
|         record.status = RequestStatus::Handled; | ||||
|     } | ||||
| 
 | ||||
|     record.untranslated_reply_cmdbuf = std::move(untranslated_cmdbuf); | ||||
|     record.translated_reply_cmdbuf = std::move(translated_cmdbuf); | ||||
|     InvokeCallbacks(record); | ||||
| 
 | ||||
|     record_map.erase(thread_id); | ||||
| } | ||||
| 
 | ||||
| void Recorder::SetHLEUnimplemented(const std::shared_ptr<Kernel::Thread>& client_thread) { | ||||
|     const u32 thread_id = client_thread->GetThreadId(); | ||||
|     if (!record_map.count(thread_id)) { | ||||
|         // This is possible when the recorder is enabled after application started
 | ||||
|         LOG_ERROR(Kernel, "No request is assoicated with the thread"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto& record = *record_map[thread_id]; | ||||
|     record.status = RequestStatus::HLEUnimplemented; | ||||
| } | ||||
| 
 | ||||
| CallbackHandle Recorder::BindCallback(CallbackType callback) { | ||||
|     std::unique_lock lock(callback_mutex); | ||||
|     CallbackHandle handle = std::make_shared<CallbackType>(callback); | ||||
|     callbacks.emplace(handle); | ||||
|     return handle; | ||||
| } | ||||
| 
 | ||||
| void Recorder::UnbindCallback(const CallbackHandle& handle) { | ||||
|     std::unique_lock lock(callback_mutex); | ||||
|     callbacks.erase(handle); | ||||
| } | ||||
| 
 | ||||
| void Recorder::InvokeCallbacks(const RequestRecord& request) { | ||||
|     { | ||||
|         std::shared_lock lock(callback_mutex); | ||||
|         for (const auto& iter : callbacks) { | ||||
|             (*iter)(request); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Recorder::SetEnabled(bool enabled_) { | ||||
|     enabled.store(enabled_, std::memory_order_relaxed); | ||||
| } | ||||
| 
 | ||||
| } // namespace IPCDebugger
 | ||||
							
								
								
									
										129
									
								
								src/core/hle/kernel/ipc_debugger/recorder.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/core/hle/kernel/ipc_debugger/recorder.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,129 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <functional> | ||||
| #include <memory> | ||||
| #include <set> | ||||
| #include <shared_mutex> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| class ClientSession; | ||||
| class Thread; | ||||
| } // namespace Kernel
 | ||||
| 
 | ||||
| namespace IPCDebugger { | ||||
| 
 | ||||
| /**
 | ||||
|  * Record of a kernel object, for debugging purposes. | ||||
|  */ | ||||
| struct ObjectInfo { | ||||
|     std::string type; | ||||
|     std::string name; | ||||
|     int id = -1; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Status of a request. | ||||
|  */ | ||||
| enum class RequestStatus { | ||||
|     Invalid,          ///< Invalid status
 | ||||
|     Sent,             ///< The request is sent to the kernel and is waiting to be handled
 | ||||
|     Handling,         ///< The request is being handled
 | ||||
|     Handled,          ///< The request is handled with reply sent
 | ||||
|     HLEUnimplemented, ///< The request is unimplemented by HLE, and unhandled
 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Record of an IPC request. | ||||
|  */ | ||||
| struct RequestRecord { | ||||
|     int id; | ||||
|     RequestStatus status = RequestStatus::Invalid; | ||||
|     ObjectInfo client_process; | ||||
|     ObjectInfo client_thread; | ||||
|     ObjectInfo client_session; | ||||
|     ObjectInfo client_port;    // Not available for portless
 | ||||
|     ObjectInfo server_process; // Only available for LLE requests
 | ||||
|     ObjectInfo server_thread;  // Only available for LLE requests
 | ||||
|     ObjectInfo server_session; | ||||
|     std::string function_name; // Not available for LLE or portless
 | ||||
|     bool is_hle = false; | ||||
|     // Request info is only available when status is not `Invalid` or `Sent`
 | ||||
|     std::vector<u32> untranslated_request_cmdbuf; | ||||
|     std::vector<u32> translated_request_cmdbuf; | ||||
|     // Reply info is only available when status is `Handled`
 | ||||
|     std::vector<u32> untranslated_reply_cmdbuf; | ||||
|     std::vector<u32> translated_reply_cmdbuf; | ||||
| }; | ||||
| 
 | ||||
| using CallbackType = std::function<void(const RequestRecord&)>; | ||||
| using CallbackHandle = std::shared_ptr<CallbackType>; | ||||
| 
 | ||||
| class Recorder { | ||||
| public: | ||||
|     explicit Recorder(); | ||||
|     ~Recorder(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns whether the recorder is enabled. | ||||
|      */ | ||||
|     bool IsEnabled() const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Registers a request into the recorder. The request is then assoicated with the client thread. | ||||
|      */ | ||||
|     void RegisterRequest(const std::shared_ptr<Kernel::ClientSession>& client_session, | ||||
|                          const std::shared_ptr<Kernel::Thread>& client_thread); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sets the request information of the request record associated with the client thread. | ||||
|      * When the server thread is empty, the request will be considered HLE. | ||||
|      */ | ||||
|     void SetRequestInfo(const std::shared_ptr<Kernel::Thread>& client_thread, | ||||
|                         std::vector<u32> untranslated_cmdbuf, std::vector<u32> translated_cmdbuf, | ||||
|                         const std::shared_ptr<Kernel::Thread>& server_thread = {}); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sets the reply information of the request record assoicated with the client thread. | ||||
|      * The request is then unlinked from the client thread. | ||||
|      */ | ||||
|     void SetReplyInfo(const std::shared_ptr<Kernel::Thread>& client_thread, | ||||
|                       std::vector<u32> untranslated_cmdbuf, std::vector<u32> translated_cmdbuf); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set the status of a record to HLEUnimplemented. | ||||
|      */ | ||||
|     void SetHLEUnimplemented(const std::shared_ptr<Kernel::Thread>& client_thread); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set the status of the debugger (enabled/disabled). | ||||
|      */ | ||||
|     void SetEnabled(bool enabled); | ||||
| 
 | ||||
|     CallbackHandle BindCallback(CallbackType callback); | ||||
|     void UnbindCallback(const CallbackHandle& handle); | ||||
| 
 | ||||
| private: | ||||
|     void InvokeCallbacks(const RequestRecord& request); | ||||
| 
 | ||||
|     std::unordered_map<u32, std::unique_ptr<RequestRecord>> record_map; | ||||
|     int record_count{}; | ||||
| 
 | ||||
|     // Temporary client session map for function name handling
 | ||||
|     std::unordered_map<u32, std::shared_ptr<Kernel::ClientSession>> client_session_map; | ||||
| 
 | ||||
|     std::atomic_bool enabled{false}; | ||||
| 
 | ||||
|     std::set<CallbackHandle> callbacks; | ||||
|     mutable std::shared_mutex callback_mutex; | ||||
| }; | ||||
| 
 | ||||
| } // namespace IPCDebugger
 | ||||
|  | @ -5,6 +5,7 @@ | |||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/config_mem.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/ipc_debugger/recorder.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
|  | @ -25,6 +26,7 @@ KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, | |||
|     resource_limits = std::make_unique<ResourceLimitList>(*this); | ||||
|     thread_manager = std::make_unique<ThreadManager>(*this); | ||||
|     timer_manager = std::make_unique<TimerManager>(timing); | ||||
|     ipc_recorder = std::make_unique<IPCDebugger::Recorder>(); | ||||
| } | ||||
| 
 | ||||
| /// Shutdown the kernel
 | ||||
|  | @ -87,6 +89,14 @@ const SharedPage::Handler& KernelSystem::GetSharedPageHandler() const { | |||
|     return *shared_page_handler; | ||||
| } | ||||
| 
 | ||||
| IPCDebugger::Recorder& KernelSystem::GetIPCRecorder() { | ||||
|     return *ipc_recorder; | ||||
| } | ||||
| 
 | ||||
| const IPCDebugger::Recorder& KernelSystem::GetIPCRecorder() const { | ||||
|     return *ipc_recorder; | ||||
| } | ||||
| 
 | ||||
| void KernelSystem::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { | ||||
|     named_ports.emplace(std::move(name), std::move(port)); | ||||
| } | ||||
|  |  | |||
|  | @ -32,6 +32,10 @@ namespace Core { | |||
| class Timing; | ||||
| } | ||||
| 
 | ||||
| namespace IPCDebugger { | ||||
| class Recorder; | ||||
| } | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class AddressArbiter; | ||||
|  | @ -222,6 +226,9 @@ public: | |||
|     SharedPage::Handler& GetSharedPageHandler(); | ||||
|     const SharedPage::Handler& GetSharedPageHandler() const; | ||||
| 
 | ||||
|     IPCDebugger::Recorder& GetIPCRecorder(); | ||||
|     const IPCDebugger::Recorder& GetIPCRecorder() const; | ||||
| 
 | ||||
|     MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); | ||||
| 
 | ||||
|     void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); | ||||
|  | @ -274,6 +281,8 @@ private: | |||
| 
 | ||||
|     std::unique_ptr<ConfigMem::Handler> config_mem_handler; | ||||
|     std::unique_ptr<SharedPage::Handler> shared_page_handler; | ||||
| 
 | ||||
|     std::unique_ptr<IPCDebugger::Recorder> ipc_recorder; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ | |||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/ipc.h" | ||||
| #include "core/hle/kernel/ipc_debugger/recorder.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
|  | @ -387,7 +388,13 @@ ResultCode SVC::SendSyncRequest(Handle handle) { | |||
| 
 | ||||
|     system.PrepareReschedule(); | ||||
| 
 | ||||
|     return session->SendSyncRequest(SharedFrom(kernel.GetThreadManager().GetCurrentThread())); | ||||
|     auto thread = SharedFrom(kernel.GetThreadManager().GetCurrentThread()); | ||||
| 
 | ||||
|     if (kernel.GetIPCRecorder().IsEnabled()) { | ||||
|         kernel.GetIPCRecorder().RegisterRequest(session, thread); | ||||
|     } | ||||
| 
 | ||||
|     return session->SendSyncRequest(thread); | ||||
| } | ||||
| 
 | ||||
| /// Close a handle
 | ||||
|  | @ -593,7 +600,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle | |||
|     } | ||||
| } | ||||
| 
 | ||||
| static ResultCode ReceiveIPCRequest(Memory::MemorySystem& memory, | ||||
| static ResultCode ReceiveIPCRequest(Kernel::KernelSystem& kernel, Memory::MemorySystem& memory, | ||||
|                                     std::shared_ptr<ServerSession> server_session, | ||||
|                                     std::shared_ptr<Thread> thread) { | ||||
|     if (server_session->parent->client == nullptr) { | ||||
|  | @ -603,9 +610,9 @@ static ResultCode ReceiveIPCRequest(Memory::MemorySystem& memory, | |||
|     VAddr target_address = thread->GetCommandBufferAddress(); | ||||
|     VAddr source_address = server_session->currently_handling->GetCommandBufferAddress(); | ||||
| 
 | ||||
|     ResultCode translation_result = | ||||
|         TranslateCommandBuffer(memory, server_session->currently_handling, thread, source_address, | ||||
|                                target_address, server_session->mapped_buffer_context, false); | ||||
|     ResultCode translation_result = TranslateCommandBuffer( | ||||
|         kernel, memory, server_session->currently_handling, thread, source_address, target_address, | ||||
|         server_session->mapped_buffer_context, false); | ||||
| 
 | ||||
|     // If a translation error occurred, immediately resume the client thread.
 | ||||
|     if (translation_result.IsError()) { | ||||
|  | @ -670,9 +677,9 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co | |||
|         VAddr source_address = thread->GetCommandBufferAddress(); | ||||
|         VAddr target_address = request_thread->GetCommandBufferAddress(); | ||||
| 
 | ||||
|         ResultCode translation_result = | ||||
|             TranslateCommandBuffer(memory, SharedFrom(thread), request_thread, source_address, | ||||
|                                    target_address, session->mapped_buffer_context, true); | ||||
|         ResultCode translation_result = TranslateCommandBuffer( | ||||
|             kernel, memory, SharedFrom(thread), request_thread, source_address, target_address, | ||||
|             session->mapped_buffer_context, true); | ||||
| 
 | ||||
|         // Note: The real kernel seems to always panic if the Server->Client buffer translation
 | ||||
|         // fails for whatever reason.
 | ||||
|  | @ -707,7 +714,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co | |||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         auto server_session = static_cast<ServerSession*>(object); | ||||
|         return ReceiveIPCRequest(memory, SharedFrom(server_session), SharedFrom(thread)); | ||||
|         return ReceiveIPCRequest(kernel, memory, SharedFrom(server_session), SharedFrom(thread)); | ||||
|     } | ||||
| 
 | ||||
|     // No objects were ready to be acquired, prepare to suspend the thread.
 | ||||
|  | @ -723,9 +730,9 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co | |||
| 
 | ||||
|     thread->wait_objects = std::move(objects); | ||||
| 
 | ||||
|     thread->wakeup_callback = [& memory = this->memory](ThreadWakeupReason reason, | ||||
|                                                         std::shared_ptr<Thread> thread, | ||||
|                                                         std::shared_ptr<WaitObject> object) { | ||||
|     thread->wakeup_callback = [& kernel = this->kernel, &memory = this->memory]( | ||||
|                                   ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | ||||
|                                   std::shared_ptr<WaitObject> object) { | ||||
|         ASSERT(thread->status == ThreadStatus::WaitSynchAny); | ||||
|         ASSERT(reason == ThreadWakeupReason::Signal); | ||||
| 
 | ||||
|  | @ -733,7 +740,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co | |||
| 
 | ||||
|         if (object->GetHandleType() == HandleType::ServerSession) { | ||||
|             auto server_session = DynamicObjectCast<ServerSession>(object); | ||||
|             result = ReceiveIPCRequest(memory, server_session, thread); | ||||
|             result = ReceiveIPCRequest(kernel, memory, server_session, thread); | ||||
|         } | ||||
| 
 | ||||
|         thread->SetWaitSynchronizationResult(result); | ||||
|  |  | |||
|  | @ -171,6 +171,7 @@ void ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) | |||
|     auto itr = handlers.find(header_code); | ||||
|     const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; | ||||
|     if (info == nullptr || info->handler_callback == nullptr) { | ||||
|         context.ReportUnimplemented(); | ||||
|         return ReportUnimplementedFunction(context.CommandBuffer(), info); | ||||
|     } | ||||
| 
 | ||||
|  | @ -179,6 +180,14 @@ void ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) | |||
|     handler_invoker(this, info->handler_callback, context); | ||||
| } | ||||
| 
 | ||||
| std::string ServiceFrameworkBase::GetFunctionName(u32 header) const { | ||||
|     if (!handlers.count(header)) { | ||||
|         return ""; | ||||
|     } | ||||
| 
 | ||||
|     return handlers.at(header).name; | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Module interface
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -64,6 +64,9 @@ public: | |||
| 
 | ||||
|     void HandleSyncRequest(Kernel::HLERequestContext& context) override; | ||||
| 
 | ||||
|     /// Retrieves name of a function based on the header code. For IPC Recorder.
 | ||||
|     std::string GetFunctionName(u32 header) const; | ||||
| 
 | ||||
| protected: | ||||
|     /// Member-function pointer type of SyncRequest handlers.
 | ||||
|     template <typename Self> | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( | |||
| 
 | ||||
|     auto [server_port, client_port] = system.Kernel().CreatePortPair(max_sessions, name); | ||||
| 
 | ||||
|     registered_services_inverse.emplace(client_port->GetObjectId(), name); | ||||
|     registered_services.emplace(std::move(name), std::move(client_port)); | ||||
|     return MakeResult(std::move(server_port)); | ||||
| } | ||||
|  | @ -65,4 +66,12 @@ ResultVal<std::shared_ptr<Kernel::ClientSession>> ServiceManager::ConnectToServi | |||
|     return client_port->Connect(); | ||||
| } | ||||
| 
 | ||||
| std::string ServiceManager::GetServiceNameByPortId(u32 port) const { | ||||
|     if (registered_services_inverse.count(port)) { | ||||
|         return registered_services_inverse.at(port); | ||||
|     } | ||||
| 
 | ||||
|     return ""; | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::SM
 | ||||
|  |  | |||
|  | @ -51,6 +51,8 @@ public: | |||
|                                                                    unsigned int max_sessions); | ||||
|     ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); | ||||
|     ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); | ||||
|     // For IPC Recorder
 | ||||
|     std::string GetServiceNameByPortId(u32 port) const; | ||||
| 
 | ||||
|     template <typename T> | ||||
|     std::shared_ptr<T> GetService(const std::string& service_name) const { | ||||
|  | @ -74,6 +76,10 @@ private: | |||
| 
 | ||||
|     /// Map of registered services, retrieved using GetServicePort or ConnectToService.
 | ||||
|     std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services; | ||||
| 
 | ||||
|     // For IPC Recorder
 | ||||
|     /// client port Object id -> service name
 | ||||
|     std::unordered_map<u32, std::string> registered_services_inverse; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Service::SM
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue