mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
kern: implement thread call stack debug
This commit is contained in:
parent
16e2f46aed
commit
af259eabda
11 changed files with 657 additions and 32 deletions
|
@ -44,6 +44,9 @@ namespace ams::kern::arch::arm64 {
|
|||
static uintptr_t GetProgramCounter(const KThread &thread);
|
||||
static void SetPreviousProgramCounter();
|
||||
|
||||
static void PrintRegister(KThread *thread = nullptr);
|
||||
static void PrintBacktrace(KThread *thread = nullptr);
|
||||
|
||||
static Result BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size);
|
||||
static Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value);
|
||||
|
||||
|
@ -61,8 +64,6 @@ namespace ams::kern::arch::arm64 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -273,6 +273,10 @@ namespace ams::kern::arch::arm64 {
|
|||
return this->page_table.GetHeapPhysicalAddress(address);
|
||||
}
|
||||
|
||||
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) const {
|
||||
return this->page_table.GetHeapVirtualAddress(address);
|
||||
}
|
||||
|
||||
KBlockInfoManager *GetBlockInfoManager() {
|
||||
return this->page_table.GetBlockInfoManager();
|
||||
}
|
||||
|
|
|
@ -22,4 +22,7 @@ namespace ams::kern::KDumpObject {
|
|||
void DumpThread();
|
||||
void DumpThread(u64 thread_id);
|
||||
|
||||
void DumpThreadCallStack();
|
||||
void DumpThreadCallStack(u64 thread_id);
|
||||
|
||||
}
|
||||
|
|
|
@ -49,11 +49,11 @@ namespace ams::kern {
|
|||
};
|
||||
|
||||
enum SuspendType : u32 {
|
||||
SuspendType_Process = 0,
|
||||
SuspendType_Thread = 1,
|
||||
SuspendType_Debug = 2,
|
||||
SuspendType_Unk3 = 3,
|
||||
SuspendType_Init = 4,
|
||||
SuspendType_Process = 0,
|
||||
SuspendType_Thread = 1,
|
||||
SuspendType_Debug = 2,
|
||||
SuspendType_Backtrace = 3,
|
||||
SuspendType_Init = 4,
|
||||
|
||||
SuspendType_Count,
|
||||
};
|
||||
|
@ -67,13 +67,13 @@ namespace ams::kern {
|
|||
ThreadState_SuspendShift = 4,
|
||||
ThreadState_Mask = (1 << ThreadState_SuspendShift) - 1,
|
||||
|
||||
ThreadState_ProcessSuspended = (1 << (SuspendType_Process + ThreadState_SuspendShift)),
|
||||
ThreadState_ThreadSuspended = (1 << (SuspendType_Thread + ThreadState_SuspendShift)),
|
||||
ThreadState_DebugSuspended = (1 << (SuspendType_Debug + ThreadState_SuspendShift)),
|
||||
ThreadState_Unk3Suspended = (1 << (SuspendType_Unk3 + ThreadState_SuspendShift)),
|
||||
ThreadState_InitSuspended = (1 << (SuspendType_Init + ThreadState_SuspendShift)),
|
||||
ThreadState_ProcessSuspended = (1 << (SuspendType_Process + ThreadState_SuspendShift)),
|
||||
ThreadState_ThreadSuspended = (1 << (SuspendType_Thread + ThreadState_SuspendShift)),
|
||||
ThreadState_DebugSuspended = (1 << (SuspendType_Debug + ThreadState_SuspendShift)),
|
||||
ThreadState_BacktraceSuspended = (1 << (SuspendType_Backtrace + ThreadState_SuspendShift)),
|
||||
ThreadState_InitSuspended = (1 << (SuspendType_Init + ThreadState_SuspendShift)),
|
||||
|
||||
ThreadState_SuspendFlagMask = ((1 << SuspendType_Count) - 1) << ThreadState_SuspendShift,
|
||||
ThreadState_SuspendFlagMask = ((1 << SuspendType_Count) - 1) << ThreadState_SuspendShift,
|
||||
};
|
||||
|
||||
enum DpcFlag : u32 {
|
||||
|
|
|
@ -384,4 +384,541 @@ namespace ams::kern::arch::arm64 {
|
|||
#undef MESOSPHERE_SET_HW_WATCH_POINT
|
||||
#undef MESOSPHERE_SET_HW_BREAK_POINT
|
||||
|
||||
void KDebug::PrintRegister(KThread *thread) {
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
|
||||
{
|
||||
/* Treat no thread as current thread. */
|
||||
if (thread == nullptr) {
|
||||
thread = GetCurrentThreadPointer();
|
||||
}
|
||||
|
||||
/* Get the exception context. */
|
||||
KExceptionContext *e_ctx = GetExceptionContext(thread);
|
||||
|
||||
/* Get the owner process. */
|
||||
if (auto *process = thread->GetOwnerProcess(); process != nullptr) {
|
||||
/* Lock the owner process. */
|
||||
KScopedLightLock state_lk(process->GetStateLock());
|
||||
KScopedLightLock list_lk(process->GetListLock());
|
||||
|
||||
/* Suspend all the process's threads. */
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto end = process->GetThreadList().end();
|
||||
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
|
||||
if (std::addressof(*it) != GetCurrentThreadPointer()) {
|
||||
it->RequestSuspend(KThread::SuspendType_Backtrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the registers. */
|
||||
MESOSPHERE_RELEASE_LOG("Registers\n");
|
||||
if ((e_ctx->psr & 0x10) == 0) {
|
||||
/* 64-bit thread. */
|
||||
for (auto i = 0; i < 31; ++i) {
|
||||
MESOSPHERE_RELEASE_LOG(" X[%2d]: 0x%016lx\n", i, e_ctx->x[i]);
|
||||
}
|
||||
MESOSPHERE_RELEASE_LOG(" SP: 0x%016lx\n", e_ctx->sp);
|
||||
MESOSPHERE_RELEASE_LOG(" PC: 0x%016lx\n", e_ctx->pc - sizeof(u32));
|
||||
MESOSPHERE_RELEASE_LOG(" PSR: 0x%08x\n", e_ctx->psr);
|
||||
MESOSPHERE_RELEASE_LOG(" TPIDR_EL0: 0x%016lx\n", e_ctx->tpidr);
|
||||
} else {
|
||||
/* 32-bit thread. */
|
||||
for (auto i = 0; i < 13; ++i) {
|
||||
MESOSPHERE_RELEASE_LOG(" R[%2d]: 0x%08x\n", i, static_cast<u32>(e_ctx->x[i]));
|
||||
}
|
||||
MESOSPHERE_RELEASE_LOG(" SP: 0x%08x\n", static_cast<u32>(e_ctx->x[13]));
|
||||
MESOSPHERE_RELEASE_LOG(" LR: 0x%08x\n", static_cast<u32>(e_ctx->x[14]));
|
||||
MESOSPHERE_RELEASE_LOG(" PC: 0x%08x\n", static_cast<u32>(e_ctx->pc) - static_cast<u32>((e_ctx->psr & 0x20) ? sizeof(u16) : sizeof(u32)));
|
||||
MESOSPHERE_RELEASE_LOG(" PSR: 0x%08x\n", e_ctx->psr);
|
||||
MESOSPHERE_RELEASE_LOG(" TPIDR: 0x%08x\n", static_cast<u32>(e_ctx->tpidr));
|
||||
}
|
||||
|
||||
/* Resume the threads that we suspended. */
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto end = process->GetThreadList().end();
|
||||
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
|
||||
if (std::addressof(*it) != GetCurrentThreadPointer()) {
|
||||
it->Resume(KThread::SuspendType_Backtrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
MESOSPHERE_UNUSED(thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
|
||||
namespace {
|
||||
|
||||
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
|
||||
const KMemoryRegion *cached = nullptr;
|
||||
return KMemoryLayout::IsHeapPhysicalAddress(cached, phys_addr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadValue(T *out, KProcess *process, uintptr_t address) {
|
||||
KPhysicalAddress phys_addr;
|
||||
KMemoryInfo mem_info;
|
||||
ams::svc::PageInfo page_info;
|
||||
|
||||
if (!util::IsAligned(address, sizeof(T))) {
|
||||
return false;
|
||||
}
|
||||
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), address))) {
|
||||
return false;
|
||||
}
|
||||
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
|
||||
return false;
|
||||
}
|
||||
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), address)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsHeapPhysicalAddress(phys_addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = *GetPointer<T>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetModuleName(char *dst, size_t dst_size, KProcess *process, uintptr_t base_address) {
|
||||
/* Locate .rodata. */
|
||||
KMemoryInfo mem_info;
|
||||
ams::svc::PageInfo page_info;
|
||||
KMemoryState mem_state = KMemoryState_None;
|
||||
|
||||
while (true) {
|
||||
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address))) {
|
||||
return false;
|
||||
}
|
||||
if (mem_state == KMemoryState_None) {
|
||||
mem_state = mem_info.GetState();
|
||||
if (mem_state != KMemoryState_Code && mem_state != KMemoryState_AliasCode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mem_info.GetState() != mem_state) {
|
||||
return false;
|
||||
}
|
||||
if (mem_info.GetPermission() == KMemoryPermission_UserRead) {
|
||||
break;
|
||||
}
|
||||
base_address = mem_info.GetEndAddress();
|
||||
}
|
||||
|
||||
/* Check that first value is 0. */
|
||||
u32 val;
|
||||
if (!ReadValue(std::addressof(val), process, base_address)) {
|
||||
return false;
|
||||
}
|
||||
if (val != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read the name length. */
|
||||
if (!ReadValue(std::addressof(val), process, base_address + sizeof(u32))) {
|
||||
return false;
|
||||
}
|
||||
if (!(0 < val && val < dst_size)) {
|
||||
return false;
|
||||
}
|
||||
const size_t name_len = val;
|
||||
|
||||
/* Read the name, one character at a time. */
|
||||
for (size_t i = 0; i < name_len; ++i) {
|
||||
if (!ReadValue(dst + i, process, base_address + 2 * sizeof(u32) + i)) {
|
||||
return false;
|
||||
}
|
||||
if (!(0 < dst[i] && dst[i] <= 0x7F)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* NULL-terminate. */
|
||||
dst[name_len] = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintAddress(uintptr_t address) {
|
||||
MESOSPHERE_RELEASE_LOG(" %p\n", reinterpret_cast<void *>(address));
|
||||
}
|
||||
|
||||
void PrintAddressWithModuleName(uintptr_t address, bool has_module_name, const char *module_name, uintptr_t base_address) {
|
||||
if (has_module_name) {
|
||||
MESOSPHERE_RELEASE_LOG(" %p [%10s + %8lx]\n", reinterpret_cast<void *>(address), module_name, address - base_address);
|
||||
} else {
|
||||
MESOSPHERE_RELEASE_LOG(" %p [%10lx + %8lx]\n", reinterpret_cast<void *>(address), base_address, address - base_address);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintAddressWithSymbol(uintptr_t address, bool has_module_name, const char *module_name, uintptr_t base_address, const char *symbol_name, uintptr_t func_address) {
|
||||
if (has_module_name) {
|
||||
MESOSPHERE_RELEASE_LOG(" %p [%10s + %8lx] (%s + %lx)\n", reinterpret_cast<void *>(address), module_name, address - base_address, symbol_name, address - func_address);
|
||||
} else {
|
||||
MESOSPHERE_RELEASE_LOG(" %p [%10lx + %8lx] (%s + %lx)\n", reinterpret_cast<void *>(address), base_address, address - base_address, symbol_name, address - func_address);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintCodeAddress(KProcess *process, uintptr_t address, bool is_lr = true) {
|
||||
/* Prepare to parse + print the address. */
|
||||
uintptr_t test_address = is_lr ? address - sizeof(u32) : address;
|
||||
uintptr_t base_address = address;
|
||||
uintptr_t dyn_address = 0;
|
||||
uintptr_t sym_tab = 0;
|
||||
uintptr_t str_tab = 0;
|
||||
size_t num_sym = 0;
|
||||
|
||||
u64 temp_64;
|
||||
u32 temp_32;
|
||||
|
||||
/* Locate the start of .text. */
|
||||
KMemoryInfo mem_info;
|
||||
ams::svc::PageInfo page_info;
|
||||
KMemoryState mem_state = KMemoryState_None;
|
||||
while (true) {
|
||||
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address))) {
|
||||
return PrintAddress(address);
|
||||
}
|
||||
if (mem_state == KMemoryState_None) {
|
||||
mem_state = mem_info.GetState();
|
||||
if (mem_state != KMemoryState_Code && mem_state != KMemoryState_AliasCode) {
|
||||
return PrintAddress(address);
|
||||
}
|
||||
} else if (mem_info.GetState() != mem_state) {
|
||||
return PrintAddress(address);
|
||||
}
|
||||
if (mem_info.GetPermission() != KMemoryPermission_UserReadExecute) {
|
||||
return PrintAddress(address);
|
||||
}
|
||||
base_address = mem_info.GetAddress();
|
||||
|
||||
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address - 1))) {
|
||||
return PrintAddress(address);
|
||||
}
|
||||
if (mem_info.GetState() != mem_state) {
|
||||
break;
|
||||
}
|
||||
if (mem_info.GetPermission() != KMemoryPermission_UserReadExecute) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the first instruction. */
|
||||
if (!ReadValue(std::addressof(temp_32), process, base_address)) {
|
||||
return PrintAddress(address);
|
||||
}
|
||||
|
||||
/* Get the module name. */
|
||||
char module_name[0x20];
|
||||
const bool has_module_name = GetModuleName(module_name, sizeof(module_name), process, base_address);
|
||||
|
||||
/* If the process is 32-bit, just print the module. */
|
||||
if (!process->Is64Bit()) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
|
||||
if (temp_32 == 0) {
|
||||
/* Module is dynamically loaded by rtld. */
|
||||
u32 mod_offset;
|
||||
if (!ReadValue(std::addressof(mod_offset), process, base_address + sizeof(u32))) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
if (!ReadValue(std::addressof(temp_32), process, base_address + mod_offset)) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
if (temp_32 != 0x30444F4D) { /* MOD0 */
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
if (!ReadValue(std::addressof(temp_32), process, base_address + mod_offset + sizeof(u32))) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
dyn_address = base_address + mod_offset + temp_32;
|
||||
} else if (temp_32 == 0x14000002) {
|
||||
/* Module embeds rtld. */
|
||||
if (!ReadValue(std::addressof(temp_32), process, base_address + 0x5C)) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
if (temp_32 != 0x94000002) { /* MOD0 */
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
if (!ReadValue(std::addressof(temp_32), process, base_address + 0x60)) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
dyn_address = base_address + 0x60 + temp_32;
|
||||
} else {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
|
||||
/* Locate tables inside .dyn. */
|
||||
for (size_t ofs = 0; /* ... */; ofs += 0x10) {
|
||||
/* Read the DynamicTag. */
|
||||
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs)) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
if (temp_64 == 0) {
|
||||
/* We're done parsing .dyn. */
|
||||
break;
|
||||
} else if (temp_64 == 4) {
|
||||
/* We found DT_HASH */
|
||||
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
/* Read nchain, to get the number of symbols. */
|
||||
if (!ReadValue(std::addressof(temp_32), process, base_address + temp_64 + sizeof(u32))) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
|
||||
num_sym = temp_32;
|
||||
} else if (temp_64 == 5) {
|
||||
/* We found DT_STRTAB */
|
||||
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
|
||||
str_tab = base_address + temp_64;
|
||||
} else if (temp_64 == 6) {
|
||||
/* We found DT_SYMTAB */
|
||||
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
|
||||
sym_tab = base_address + temp_64;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that we found all the tables. */
|
||||
if (!(sym_tab != 0 && str_tab != 0 && num_sym != 0)) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
|
||||
/* Try to locate an appropriate symbol. */
|
||||
for (size_t i = 0; i < num_sym; ++i) {
|
||||
/* Read the symbol from userspace. */
|
||||
struct {
|
||||
u32 st_name;
|
||||
u8 st_info;
|
||||
u8 st_other;
|
||||
u16 st_shndx;
|
||||
u64 st_value;
|
||||
u64 st_size;
|
||||
} sym;
|
||||
{
|
||||
u64 x[sizeof(sym) / sizeof(u64)];
|
||||
for (size_t j = 0; j < util::size(x); ++j) {
|
||||
if (!ReadValue(x + j, process, sym_tab + sizeof(sym) * i + sizeof(u64) * j)) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
}
|
||||
std::memcpy(std::addressof(sym), x, sizeof(sym));
|
||||
}
|
||||
|
||||
/* Check the symbol is valid/STT_FUNC. */
|
||||
if (sym.st_shndx == 0 || ((sym.st_shndx & 0xFF00) == 0xFF00)) {
|
||||
continue;
|
||||
}
|
||||
if ((sym.st_info & 0xF) != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check the address. */
|
||||
const uintptr_t func_start = base_address + sym.st_value;
|
||||
if (func_start <= test_address && test_address < func_start + sym.st_size) {
|
||||
/* Read the symbol name. */
|
||||
const uintptr_t sym_address = str_tab + sym.st_name;
|
||||
char sym_name[0x80];
|
||||
sym_name[util::size(sym_name) - 1] = 0;
|
||||
for (size_t j = 0; j < util::size(sym_name) - 1; ++j) {
|
||||
if (!ReadValue(sym_name + j, process, sym_address + j)) {
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
if (sym_name[j] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the symbol. */
|
||||
return PrintAddressWithSymbol(address, has_module_name, module_name, base_address, sym_name, func_start);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fall back to printing the module. */
|
||||
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void KDebug::PrintBacktrace(KThread *thread) {
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
|
||||
{
|
||||
/* Treat no thread as current thread. */
|
||||
if (thread == nullptr) {
|
||||
thread = GetCurrentThreadPointer();
|
||||
}
|
||||
|
||||
/* Get the exception context. */
|
||||
KExceptionContext *e_ctx = GetExceptionContext(thread);
|
||||
|
||||
/* Get the owner process. */
|
||||
if (auto *process = thread->GetOwnerProcess(); process != nullptr) {
|
||||
/* Lock the owner process. */
|
||||
KScopedLightLock state_lk(process->GetStateLock());
|
||||
KScopedLightLock list_lk(process->GetListLock());
|
||||
|
||||
/* Suspend all the process's threads. */
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto end = process->GetThreadList().end();
|
||||
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
|
||||
if (std::addressof(*it) != GetCurrentThreadPointer()) {
|
||||
it->RequestSuspend(KThread::SuspendType_Backtrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the backtrace. */
|
||||
MESOSPHERE_RELEASE_LOG("User Backtrace\n");
|
||||
if ((e_ctx->psr & 0x10) == 0) {
|
||||
/* 64-bit thread. */
|
||||
PrintCodeAddress(process, e_ctx->pc, false);
|
||||
PrintCodeAddress(process, e_ctx->x[30]);
|
||||
|
||||
/* Walk the stack frames. */
|
||||
uintptr_t fp = static_cast<uintptr_t>(e_ctx->x[29]);
|
||||
for (auto i = 0; i < 0x20 && fp != 0 && util::IsAligned(fp, 0x10); ++i) {
|
||||
/* Read the next frame. */
|
||||
struct {
|
||||
u64 fp;
|
||||
u64 lr;
|
||||
} stack_frame;
|
||||
{
|
||||
KMemoryInfo mem_info;
|
||||
ams::svc::PageInfo page_info;
|
||||
KPhysicalAddress phys_addr;
|
||||
|
||||
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), fp))) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
|
||||
break;
|
||||
}
|
||||
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), fp)) {
|
||||
break;
|
||||
}
|
||||
if (!IsHeapPhysicalAddress(phys_addr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
u64 *frame_ptr = GetPointer<u64>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
|
||||
stack_frame.fp = frame_ptr[0];
|
||||
stack_frame.lr = frame_ptr[1];
|
||||
}
|
||||
|
||||
/* Print and advance. */
|
||||
PrintCodeAddress(process, stack_frame.lr);
|
||||
fp = stack_frame.fp;
|
||||
}
|
||||
} else {
|
||||
/* 32-bit thread. */
|
||||
PrintCodeAddress(process, e_ctx->pc, false);
|
||||
PrintCodeAddress(process, e_ctx->x[14]);
|
||||
|
||||
/* Walk the stack frames. */
|
||||
uintptr_t fp = static_cast<uintptr_t>(e_ctx->x[11]);
|
||||
for (auto i = 0; i < 0x20 && fp != 0 && util::IsAligned(fp, 4); ++i) {
|
||||
/* Read the next frame. */
|
||||
struct {
|
||||
u32 fp;
|
||||
u32 lr;
|
||||
} stack_frame;
|
||||
{
|
||||
KMemoryInfo mem_info;
|
||||
ams::svc::PageInfo page_info;
|
||||
KPhysicalAddress phys_addr;
|
||||
|
||||
/* Read FP */
|
||||
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), fp))) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
|
||||
break;
|
||||
}
|
||||
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), fp)) {
|
||||
break;
|
||||
}
|
||||
if (!IsHeapPhysicalAddress(phys_addr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
stack_frame.fp = *GetPointer<u32>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
|
||||
|
||||
/* Read LR. */
|
||||
uintptr_t lr_ptr = (e_ctx->x[13] <= stack_frame.fp && stack_frame.fp < e_ctx->x[13] + PageSize) ? fp + 4 : fp - 4;
|
||||
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), lr_ptr))) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) {
|
||||
break;
|
||||
}
|
||||
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
|
||||
break;
|
||||
}
|
||||
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), lr_ptr)) {
|
||||
break;
|
||||
}
|
||||
if (!IsHeapPhysicalAddress(phys_addr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
stack_frame.lr = *GetPointer<u32>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
|
||||
}
|
||||
|
||||
/* Print and advance. */
|
||||
PrintCodeAddress(process, stack_frame.lr);
|
||||
fp = stack_frame.fp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resume the threads that we suspended. */
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto end = process->GetThreadList().end();
|
||||
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
|
||||
if (std::addressof(*it) != GetCurrentThreadPointer()) {
|
||||
it->Resume(KThread::SuspendType_Backtrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
MESOSPHERE_UNUSED(thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,19 @@ namespace ams::kern::KDumpObject {
|
|||
}
|
||||
}
|
||||
|
||||
void DumpThreadCallStack(KThread *thread) {
|
||||
if (KProcess *process = thread->GetOwnerProcess(); process != nullptr) {
|
||||
MESOSPHERE_LOG("Thread ID=%5lu pid=%3lu %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu\n",
|
||||
thread->GetId(), process->GetId(), process->GetName(), thread->GetPriority(), ThreadStates[thread->GetState()], thread->GetKernelStackUsage(), PageSize);
|
||||
|
||||
KDebug::PrintRegister(thread);
|
||||
KDebug::PrintBacktrace(thread);
|
||||
} else {
|
||||
MESOSPHERE_LOG("Thread ID=%5lu pid=%3d %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu\n",
|
||||
thread->GetId(), -1, "(kernel)", thread->GetPriority(), ThreadStates[thread->GetState()], thread->GetKernelStackUsage(), PageSize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DumpThread() {
|
||||
|
@ -77,4 +90,35 @@ namespace ams::kern::KDumpObject {
|
|||
MESOSPHERE_LOG("\n");
|
||||
}
|
||||
|
||||
void DumpThreadCallStack() {
|
||||
MESOSPHERE_LOG("Dump Thread\n");
|
||||
|
||||
{
|
||||
/* Lock the list. */
|
||||
KThread::ListAccessor accessor;
|
||||
const auto end = accessor.end();
|
||||
|
||||
/* Dump each thread. */
|
||||
for (auto it = accessor.begin(); it != end; ++it) {
|
||||
DumpThreadCallStack(static_cast<KThread *>(std::addressof(*it)));
|
||||
}
|
||||
}
|
||||
|
||||
MESOSPHERE_LOG("\n");
|
||||
}
|
||||
|
||||
void DumpThreadCallStack(u64 thread_id) {
|
||||
MESOSPHERE_LOG("Dump Thread\n");
|
||||
|
||||
{
|
||||
/* Find and dump the target thread. */
|
||||
if (KThread *thread = KThread::GetThreadFromId(thread_id); thread != nullptr) {
|
||||
ON_SCOPE_EXIT { thread->Close(); };
|
||||
DumpThreadCallStack(thread);
|
||||
}
|
||||
}
|
||||
|
||||
MESOSPHERE_LOG("\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -97,25 +97,20 @@ namespace ams::kern {
|
|||
/* Print the state. */
|
||||
MESOSPHERE_RELEASE_LOG("Core[%d] Current State:\n", core_id);
|
||||
|
||||
#ifdef ATMOSPHERE_ARCH_ARM64
|
||||
/* Print registers. */
|
||||
if (core_ctx != nullptr) {
|
||||
MESOSPHERE_RELEASE_LOG(" Registers:\n");
|
||||
for (size_t i = 0; i < util::size(core_ctx->x); ++i) {
|
||||
MESOSPHERE_RELEASE_LOG(" X[%02zx]: %p\n", i, reinterpret_cast<void *>(core_ctx->x[i]));
|
||||
}
|
||||
MESOSPHERE_RELEASE_LOG(" SP: %p\n", reinterpret_cast<void *>(core_ctx->x[30]));
|
||||
}
|
||||
/* Print registers and user backtrace. */
|
||||
KDebug::PrintRegister();
|
||||
KDebug::PrintBacktrace();
|
||||
|
||||
/* Print backtrace. */
|
||||
MESOSPHERE_RELEASE_LOG(" Backtrace:\n");
|
||||
#ifdef ATMOSPHERE_ARCH_ARM64
|
||||
/* Print kernel backtrace. */
|
||||
MESOSPHERE_RELEASE_LOG("Backtrace:\n");
|
||||
uintptr_t fp = core_ctx != nullptr ? core_ctx->x[29] : reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
|
||||
for (size_t i = 0; i < 32 && fp && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); i++) {
|
||||
struct {
|
||||
uintptr_t fp;
|
||||
uintptr_t lr;
|
||||
} *stack_frame = reinterpret_cast<decltype(stack_frame)>(fp);
|
||||
MESOSPHERE_RELEASE_LOG(" [%02zx]: %p\n", i, reinterpret_cast<void *>(stack_frame->lr));
|
||||
MESOSPHERE_RELEASE_LOG(" [%02zx]: %p\n", i, reinterpret_cast<void *>(stack_frame->lr));
|
||||
fp = stack_frame->fp;
|
||||
}
|
||||
#endif
|
||||
|
@ -137,7 +132,7 @@ namespace ams::kern {
|
|||
|
||||
}
|
||||
|
||||
NORETURN void PanicImpl(const char *file, int line, const char *format, ...) {
|
||||
NORETURN WEAK_SYMBOL void PanicImpl(const char *file, int line, const char *format, ...) {
|
||||
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
|
||||
/* Wait for it to be our turn to print. */
|
||||
WaitCoreTicket();
|
||||
|
@ -158,7 +153,7 @@ namespace ams::kern {
|
|||
StopSystem();
|
||||
}
|
||||
|
||||
NORETURN void PanicImpl() {
|
||||
NORETURN WEAK_SYMBOL void PanicImpl() {
|
||||
StopSystem();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,16 +21,18 @@ namespace ams::kern::svc {
|
|||
|
||||
namespace {
|
||||
|
||||
[[maybe_unused]] void PrintBreak(ams::svc::BreakReason break_reason) {
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
|
||||
void PrintBreak(ams::svc::BreakReason break_reason) {
|
||||
/* Print that break was called. */
|
||||
MESOSPHERE_RELEASE_LOG("%s: svc::Break(%d) was called, pid=%ld, tid=%ld\n", GetCurrentProcess().GetName(), static_cast<s32>(break_reason), GetCurrentProcess().GetId(), GetCurrentThread().GetId());
|
||||
|
||||
/* Print the current thread's registers. */
|
||||
/* TODO: KDebug::PrintRegisters(); */
|
||||
KDebug::PrintRegister();
|
||||
|
||||
/* Print a backtrace. */
|
||||
/* TODO: KDebug::PrintBacktrace(); */
|
||||
KDebug::PrintBacktrace();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
|
||||
/* Determine whether the break is only a notification. */
|
||||
|
@ -38,7 +40,7 @@ namespace ams::kern::svc {
|
|||
|
||||
/* If the break isn't a notification, print it. */
|
||||
if (!is_notification) {
|
||||
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
|
||||
PrintBreak(break_reason);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -34,6 +34,13 @@ namespace ams::kern::svc {
|
|||
KDumpObject::DumpThread(arg0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_ThreadCallStack:
|
||||
if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpThreadCallStack();
|
||||
} else {
|
||||
KDumpObject::DumpThreadCallStack(arg0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -470,7 +470,8 @@ namespace ams::svc {
|
|||
};
|
||||
|
||||
enum KernelDebugType : u32 {
|
||||
KernelDebugType_Thread = 0,
|
||||
KernelDebugType_Thread = 0,
|
||||
KernelDebugType_ThreadCallStack = 1,
|
||||
};
|
||||
|
||||
enum KernelTraceState : u32 {
|
||||
|
|
31
mesosphere/kernel_ldr/source/kern_loader_panic.cpp
Normal file
31
mesosphere/kernel_ldr/source/kern_loader_panic.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
/* This overrides the panic implementation from the kernel, to prevent linking debug print into kldr. */
|
||||
|
||||
NORETURN void PanicImpl(const char *file, int line, const char *format, ...) {
|
||||
MESOSPHERE_UNUSED(file, line, format);
|
||||
MESOSPHERE_INIT_ABORT();
|
||||
}
|
||||
|
||||
NORETURN void PanicImpl() {
|
||||
MESOSPHERE_INIT_ABORT();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue