mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-14 17:16:36 +00:00
dmnt2: detect thread name, add monitor get mapping(s), increase buffer sizes
This commit is contained in:
parent
95ef9da873
commit
ea61ac9ea6
8 changed files with 196 additions and 19 deletions
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
namespace ams::dmnt {
|
||||
|
||||
static constexpr size_t GdbPacketBufferSize = 16_KB;
|
||||
static constexpr size_t GdbPacketBufferSize = 32_KB;
|
||||
|
||||
class GdbPacketIo {
|
||||
private:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue