dmnt2: detect thread name, add monitor get mapping(s), increase buffer sizes

This commit is contained in:
Michael Scire 2021-11-01 17:18:13 -07:00 committed by SciresM
parent aba7e4ca7d
commit 6145b3b72c
8 changed files with 196 additions and 19 deletions

View file

@ -19,7 +19,7 @@ namespace ams::kern {
namespace {
constexpr std::tuple<KMemoryState, const char *> MemoryStateNames[] = {
constexpr const std::pair<KMemoryState, const char *> 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) {

View file

@ -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<uintptr_t *>(svc::GetThreadLocalRegion());
for (size_t i = sizeof(svc::ThreadLocalRegion) / sizeof(uintptr_t); i > 0; --i) {
if (auto *candidate = reinterpret_cast<ThreadImpl *>(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<uintptr_t>(thread_impl);
break;
}
}
}
/* Get the thread priority. */
s32 horizon_priority;
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));

View file

@ -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;

View file

@ -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<void *>(thread_info->_thread_type));
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_%p", reinterpret_cast<void *>(thread_info->_thread_type));
} else {
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%p", reinterpret_cast<void *>(thread_info->_thread_type));
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_%p", reinterpret_cast<void *>(thread_info->_thread_type));
}
return ResultSuccess();

View file

@ -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<void *>(m_thread_infos[i]._thread_type));
} else {
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_%p", reinterpret_cast<void *>(m_thread_infos[i]._thread_type));
}
} else {
break;
}
}
return;
}
}
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_ID=%lu", thread_id);
}
}

View file

@ -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 &current_pc, u64 &target);
Result CollectModules();
void GetThreadName(char *dst, u64 thread_id) const;
private:
Result Start();

View file

@ -19,7 +19,7 @@
namespace ams::dmnt {
static constexpr size_t GdbPacketBufferSize = 16_KB;
static constexpr size_t GdbPacketBufferSize = 32_KB;
class GdbPacketIo {
private:

View file

@ -265,6 +265,57 @@ namespace ams::dmnt {
"\t<reg name=\"fpscr\" bitsize=\"32\" type=\"int\" group=\"float\"/>\n"
"</feature>\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<size_t>(state) < util::size(MemoryStateNames)) {
return MemoryStateNames[static_cast<size_t>(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<u64>(-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<char *>(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<u64>::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, "<thread id=\"p%lx.%lx\" core=\"%u\"/>", 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, "<thread id=\"p%lx.%lx\" core=\"%u\" name=\"%s\" />", m_process_id.value, thread_ids[i], core, name);
}
}