From 6145b3b72c959772466baf06a60bd7cb1ed7171b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 1 Nov 2021 17:18:13 -0700 Subject: [PATCH] dmnt2: detect thread name, add monitor get mapping(s), increase buffer sizes --- .../source/kern_k_memory_block_manager.cpp | 3 +- .../os_thread_manager_impl.os.horizon.cpp | 20 +++ .../osdbg_thread_local_region.os.horizon.hpp | 6 +- .../source/osdbg/osdbg_thread.cpp | 4 +- .../dmnt.gen2/source/dmnt2_debug_process.cpp | 20 +++ .../dmnt.gen2/source/dmnt2_debug_process.hpp | 8 +- .../dmnt.gen2/source/dmnt2_gdb_packet_io.hpp | 2 +- .../source/dmnt2_gdb_server_impl.cpp | 152 ++++++++++++++++-- 8 files changed, 196 insertions(+), 19 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_memory_block_manager.cpp b/libraries/libmesosphere/source/kern_k_memory_block_manager.cpp index c10643c18..ea0baf8a5 100644 --- a/libraries/libmesosphere/source/kern_k_memory_block_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_block_manager.cpp @@ -19,7 +19,7 @@ namespace ams::kern { namespace { - constexpr std::tuple MemoryStateNames[] = { + constexpr const std::pair MemoryStateNames[] = { {KMemoryState_Free , "----- Free -----"}, {KMemoryState_Io , "Io "}, {KMemoryState_Static , "Static "}, @@ -41,6 +41,7 @@ namespace ams::kern { {KMemoryState_Kernel , "Kernel "}, {KMemoryState_GeneratedCode , "GeneratedCode "}, {KMemoryState_CodeOut , "CodeOut "}, + {KMemoryState_Coverage , "Coverage "}, }; constexpr const char *GetMemoryStateName(KMemoryState state) { diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp index 986dbaf77..2bce07e5c 100644 --- a/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp @@ -52,6 +52,26 @@ namespace ams::os::impl { /* Get the thread impl object from libnx. */ ThreadImpl *thread_impl = ::threadGetSelf(); + /* Hack around libnx's main thread, to ensure stratosphere thread type consistency. */ + { + auto *tlr = reinterpret_cast(svc::GetThreadLocalRegion()); + for (size_t i = sizeof(svc::ThreadLocalRegion) / sizeof(uintptr_t); i > 0; --i) { + if (auto *candidate = reinterpret_cast(tlr[i - 1]); candidate == thread_impl) { + ThreadImpl *embedded_thread = std::addressof(main_thread->thread_impl_storage); + + *embedded_thread = *thread_impl; + + if (embedded_thread->next) { + embedded_thread->next->prev_next = std::addressof(embedded_thread->next); + } + + thread_impl = embedded_thread; + tlr[i-1] = reinterpret_cast(thread_impl); + break; + } + } + } + /* Get the thread priority. */ s32 horizon_priority; R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle)); diff --git a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp index c77f3222c..b29d39ab6 100644 --- a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp +++ b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp @@ -54,7 +54,7 @@ namespace ams::osdbg::impl { static_assert(AMS_OFFSETOF(ThreadLocalRegionIlp32, tls) == 0x1C0); struct LibnxThreadVars { - static constexpr u32 Magic = util::FourCC<'!','T','V','$'>::Code; + static constexpr u32 Magic = util::ReverseFourCC<'!','T','V','$'>::Code; u32 magic; ::Handle handle; @@ -69,11 +69,11 @@ namespace ams::osdbg::impl { volatile u16 disable_counter; volatile u16 interrupt_flag; u32 reserved0; - u64 tls[(0x1E0 - 0x108) / sizeof(u64)]; + u64 tls[(0x200 - sizeof(LibnxThreadVars) - 0x108) / sizeof(u64)]; LibnxThreadVars thread_vars; }; static_assert(sizeof(ThreadLocalRegionLibnx) == sizeof(svc::ThreadLocalRegion)); - static_assert(AMS_OFFSETOF(ThreadLocalRegionLibnx, thread_vars) == 0x1E0); + static_assert(AMS_OFFSETOF(ThreadLocalRegionLibnx, thread_vars) == 0x200 - sizeof(LibnxThreadVars)); struct LibnxThreadEntryArgs { u64 t; diff --git a/libraries/libstratosphere/source/osdbg/osdbg_thread.cpp b/libraries/libstratosphere/source/osdbg/osdbg_thread.cpp index 6eb28015e..81f69ad5e 100644 --- a/libraries/libstratosphere/source/osdbg/osdbg_thread.cpp +++ b/libraries/libstratosphere/source/osdbg/osdbg_thread.cpp @@ -50,9 +50,9 @@ namespace ams::osdbg { } else { /* Special-case libnx threads. */ if (thread_info->_thread_type_type == ThreadTypeType_Libnx) { - util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%p", reinterpret_cast(thread_info->_thread_type)); + util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_%p", reinterpret_cast(thread_info->_thread_type)); } else { - util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%p", reinterpret_cast(thread_info->_thread_type)); + util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_%p", reinterpret_cast(thread_info->_thread_type)); } return ResultSuccess(); diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp index 036de75ce..cd590efa3 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp @@ -593,5 +593,25 @@ namespace ams::dmnt { return ResultSuccess(); } + void DebugProcess::GetThreadName(char *dst, u64 thread_id) const { + for (size_t i = 0; i < ThreadCountMax; ++i) { + if (m_thread_valid[i] && m_thread_ids[i] == thread_id) { + if (R_FAILED(osdbg::GetThreadName(dst, std::addressof(m_thread_infos[i])))) { + if (m_thread_infos[i]._thread_type != 0) { + if (m_thread_infos[i]._thread_type_type == osdbg::ThreadTypeType_Libnx) { + util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_%p", reinterpret_cast(m_thread_infos[i]._thread_type)); + } else { + util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_%p", reinterpret_cast(m_thread_infos[i]._thread_type)); + } + } else { + break; + } + } + return; + } + } + + util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_ID=%lu", thread_id); + } } \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp index d8a698f96..0f8685ee2 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp @@ -50,6 +50,7 @@ namespace ams::dmnt { u64 m_last_thread_id{}; u64 m_thread_id_override{}; u64 m_continue_thread_id{}; + u64 m_preferred_debug_break_thread_id{}; GdbSignal m_last_signal{}; bool m_stepping{false}; bool m_use_hardware_single_step{false}; @@ -107,13 +108,16 @@ namespace ams::dmnt { u64 GetLastThreadId(); u64 GetThreadIdOverride() { this->GetLastThreadId(); return m_thread_id_override; } + u64 GetPreferredDebuggerBreakThreadId() { return m_preferred_debug_break_thread_id; } + void SetLastThreadId(u64 tid) { m_last_thread_id = tid; m_thread_id_override = tid; } void SetThreadIdOverride(u64 tid) { - m_thread_id_override = tid; + m_thread_id_override = tid; + m_preferred_debug_break_thread_id = tid; } void SetDebugBreaked() { @@ -174,6 +178,8 @@ namespace ams::dmnt { void GetBranchTarget(svc::ThreadContext &ctx, u64 thread_id, u64 ¤t_pc, u64 &target); Result CollectModules(); + + void GetThreadName(char *dst, u64 thread_id) const; private: Result Start(); diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp index 00ca5700c..50f782fb5 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp @@ -19,7 +19,7 @@ namespace ams::dmnt { - static constexpr size_t GdbPacketBufferSize = 16_KB; + static constexpr size_t GdbPacketBufferSize = 32_KB; class GdbPacketIo { private: diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp index f19eb81a1..36b390efb 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp @@ -265,6 +265,57 @@ namespace ams::dmnt { "\t\n" "\n"; + constexpr const char * const MemoryStateNames[svc::MemoryState_Coverage + 1] = { + "----- Free -----", /* svc::MemoryState_Free */ + "Io ", /* svc::MemoryState_Io */ + "Static ", /* svc::MemoryState_Static */ + "Code ", /* svc::MemoryState_Code */ + "CodeData ", /* svc::MemoryState_CodeData */ + "Normal ", /* svc::MemoryState_Normal */ + "Shared ", /* svc::MemoryState_Shared */ + "Alias ", /* svc::MemoryState_Alias */ + "AliasCode ", /* svc::MemoryState_AliasCode */ + "AliasCodeData ", /* svc::MemoryState_AliasCodeData */ + "Ipc ", /* svc::MemoryState_Ipc */ + "Stack ", /* svc::MemoryState_Stack */ + "ThreadLocal ", /* svc::MemoryState_ThreadLocal */ + "Transfered ", /* svc::MemoryState_Transfered */ + "SharedTransfered", /* svc::MemoryState_SharedTransfered */ + "SharedCode ", /* svc::MemoryState_SharedCode */ + "Inaccessible ", /* svc::MemoryState_Inaccessible */ + "NonSecureIpc ", /* svc::MemoryState_NonSecureIpc */ + "NonDeviceIpc ", /* svc::MemoryState_NonDeviceIpc */ + "Kernel ", /* svc::MemoryState_Kernel */ + "GeneratedCode ", /* svc::MemoryState_GeneratedCode */ + "CodeOut ", /* svc::MemoryState_CodeOut */ + "Coverage ", /* svc::MemoryState_Coverage */ + }; + + constexpr const char *GetMemoryStateName(svc::MemoryState state) { + if (static_cast(state) < util::size(MemoryStateNames)) { + return MemoryStateNames[static_cast(state)]; + } else { + return "Unknown "; + } + } + + constexpr const char *GetMemoryPermissionString(const svc::MemoryInfo &info) { + if (info.state == svc::MemoryState_Free) { + return " "; + } else { + switch (info.permission) { + case svc::MemoryPermission_ReadExecute: + return "r-x"; + case svc::MemoryPermission_Read: + return "r--"; + case svc::MemoryPermission_ReadWrite: + return "rw-"; + default: + return "---"; + } + } + } + bool ParsePrefix(char *&packet, const char *prefix) { const auto len = std::strlen(prefix); if (std::strncmp(packet, prefix, len) == 0) { @@ -765,7 +816,7 @@ namespace ams::dmnt { } constinit os::SdkMutex g_annex_buffer_lock; - constinit char g_annex_buffer[0x8000]; + constinit char g_annex_buffer[2 * GdbPacketBufferSize]; enum AnnexBufferContents { AnnexBufferContents_Invalid, @@ -1001,10 +1052,18 @@ namespace ams::dmnt { break; case svc::DebugException_DebuggerBreak: { - AMS_DMNT2_GDB_LOG_DEBUG("DebuggerBreak %lx, last=%lx\n", thread_id, m_debug_process.GetLastThreadId()); signal = GdbSignal_Interrupt; - thread_id = m_debug_process.GetLastThreadId(); + + thread_id = m_debug_process.GetPreferredDebuggerBreakThreadId(); + svc::ThreadContext ctx; + if (thread_id == 0 || thread_id == static_cast(-1) || R_FAILED(m_debug_process.GetThreadContext(std::addressof(ctx), thread_id, svc::ThreadContextFlag_Control))) { + thread_id = m_debug_process.GetLastThreadId(); + } + + AMS_DMNT2_GDB_LOG_DEBUG("DebuggerBreak %lx, last=%lx\n", thread_id, m_debug_process.GetLastThreadId()); + m_debug_process.SetLastThreadId(thread_id); + m_debug_process.SetThreadIdOverride(thread_id); } break; case svc::DebugException_UndefinedInstruction: @@ -1873,6 +1932,8 @@ namespace ams::dmnt { char *command = reinterpret_cast(m_buffer); if (ParsePrefix(command, "help")) { SetReply(m_buffer, "get info\n" + "get mappings\n" + "get mapping {address}\n" "wait application\n" "wait {program id}\n" "wait homebrew\n"); @@ -1888,10 +1949,10 @@ namespace ams::dmnt { "Hbl: %d\n" "Layout:\n", m_process_id.value, m_debug_process.GetProcessName(), m_debug_process.GetProgramLocation().program_id.value, m_debug_process.IsApplication(), m_debug_process.GetOverrideStatus().IsHbl()); - AppendReply(m_buffer, " Alias: 0x%010lx - 0x%010lx\n" - " Heap: 0x%010lx - 0x%010lx\n" - " Aslr: 0x%010lx - 0x%010lx\n" - " Stack: 0x%010lx - 0x%010lx\n" + AppendReply(m_buffer, " Alias: 0x%010lx - 0x%010lx\n" + " Heap: 0x%010lx - 0x%010lx\n" + " Aslr: 0x%010lx - 0x%010lx\n" + " Stack: 0x%010lx - 0x%010lx\n" "Modules:\n", m_debug_process.GetAliasRegionAddress(), m_debug_process.GetAliasRegionAddress() + m_debug_process.GetAliasRegionSize() - 1, m_debug_process.GetHeapRegionAddress(), m_debug_process.GetHeapRegionAddress() + m_debug_process.GetHeapRegionSize() - 1, m_debug_process.GetAslrRegionAddress(), m_debug_process.GetAslrRegionAddress() + m_debug_process.GetAslrRegionSize() - 1, @@ -1902,11 +1963,75 @@ namespace ams::dmnt { const char *module_name = m_debug_process.GetModuleName(i); const auto name_len = std::strlen(module_name); if (name_len < 5 || (std::strcmp(module_name + name_len - 4, ".elf") != 0 && std::strcmp(module_name + name_len - 4, ".nss") != 0)) { - AppendReply(m_buffer, " 0x%010lx - 0x%010lx %s.elf\n", m_debug_process.GetModuleBaseAddress(i), m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1, module_name); + AppendReply(m_buffer, " 0x%010lx - 0x%010lx %s.elf\n", m_debug_process.GetModuleBaseAddress(i), m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1, module_name); } else { - AppendReply(m_buffer, " 0x%010lx - 0x%010lx %s\n", m_debug_process.GetModuleBaseAddress(i), m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1, module_name); + AppendReply(m_buffer, " 0x%010lx - 0x%010lx %s\n", m_debug_process.GetModuleBaseAddress(i), m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1, module_name); } } + } else if (ParsePrefix(command, "get mappings")) { + if (!this->HasDebugProcess()) { + SetReply(m_buffer, "Not attached.\n"); + return; + } + + SetReply(m_buffer, "Mappings:\n"); + + uintptr_t cur_addr = 0; + while (true) { + /* Get mapping. */ + svc::MemoryInfo mem_info; + if (R_FAILED(m_debug_process.QueryMemory(std::addressof(mem_info), cur_addr))) { + break; + } + + if (mem_info.state != svc::MemoryState_Inaccessible || mem_info.base_address + mem_info.size - 1 != std::numeric_limits::max()) { + const char *state = GetMemoryStateName(mem_info.state); + const char *perm = GetMemoryPermissionString(mem_info); + + const char l = (mem_info.attribute & svc::MemoryAttribute_Locked) ? 'L' : '-'; + const char i = (mem_info.attribute & svc::MemoryAttribute_IpcLocked) ? 'I' : '-'; + const char d = (mem_info.attribute & svc::MemoryAttribute_DeviceShared) ? 'D' : '-'; + const char u = (mem_info.attribute & svc::MemoryAttribute_Uncached) ? 'U' : '-'; + + AppendReply(m_buffer, " 0x%010lx - 0x%010lx %s %s %c%c%c%c [%d, %d]\n", mem_info.base_address, mem_info.base_address + mem_info.size - 1, perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count); + } + + /* Advance. */ + const uintptr_t next_address = mem_info.base_address + mem_info.size; + if (next_address <= cur_addr) { + break; + } + + cur_addr = next_address; + } + + } else if (ParsePrefix(command, "get mapping ")) { + if (!this->HasDebugProcess()) { + SetReply(m_buffer, "Not attached.\n"); + return; + } + + /* Allow optional "0x" prefix. */ + ParsePrefix(command, "0x"); + + /* Decode address. */ + const u64 address = DecodeHex(command); + + /* Get mapping. */ + svc::MemoryInfo mem_info; + if (R_FAILED(m_debug_process.QueryMemory(std::addressof(mem_info), address))) { + SetReply(m_buffer, "0x%016lx: No mapping.\n", address); + } + + const char *state = GetMemoryStateName(mem_info.state); + const char *perm = GetMemoryPermissionString(mem_info); + + const char l = (mem_info.attribute & svc::MemoryAttribute_Locked) ? 'L' : '-'; + const char i = (mem_info.attribute & svc::MemoryAttribute_IpcLocked) ? 'I' : '-'; + const char d = (mem_info.attribute & svc::MemoryAttribute_DeviceShared) ? 'D' : '-'; + const char u = (mem_info.attribute & svc::MemoryAttribute_Uncached) ? 'U' : '-'; + + SetReply(m_buffer, "0x%010lx - 0x%010lx %s %s %c%c%c%c [%d, %d]\n", mem_info.base_address, mem_info.base_address + mem_info.size - 1, perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count); } else if (ParsePrefix(command, "wait application") || ParsePrefix(command, "wait app")) { /* Wait for an application process. */ { @@ -2163,8 +2288,13 @@ namespace ams::dmnt { u32 core = 0; m_debug_process.GetThreadCurrentCore(std::addressof(core), thread_ids[i]); - /* TODO: `name=\"%s\"`? */ - AppendReply(g_annex_buffer, "", m_process_id.value, thread_ids[i], core); + /* Get the thread name. */ + char name[os::ThreadNameLengthMax + 1]; + m_debug_process.GetThreadName(name, thread_ids[i]); + name[sizeof(name) - 1] = '\x00'; + + /* Set the thread entry */ + AppendReply(g_annex_buffer, "", m_process_id.value, thread_ids[i], core, name); } }