diff --git a/libraries/libmesosphere/include/mesosphere.hpp b/libraries/libmesosphere/include/mesosphere.hpp index f5ff746ee..2c538e33c 100644 --- a/libraries/libmesosphere/include/mesosphere.hpp +++ b/libraries/libmesosphere/include/mesosphere.hpp @@ -54,6 +54,7 @@ #include #include #include +#include /* Miscellaneous objects. */ #include diff --git a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp index 6f026f721..dcda97f6e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp @@ -26,6 +26,7 @@ #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING #define MESOSPHERE_ENABLE_ASSERTIONS #define MESOSPHERE_ENABLE_DEBUG_PRINT +#define MESOSPHERE_ENABLE_KERNEL_STACK_USAGE #endif //#define MESOSPHERE_BUILD_FOR_TRACING diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_dump_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_dump_object.hpp new file mode 100644 index 000000000..ab7cf9a0a --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_dump_object.hpp @@ -0,0 +1,25 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::kern::KDumpObject { + + void DumpThread(); + void DumpThread(u64 thread_id); + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 20ea88ba8..ce3791061 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -533,6 +533,7 @@ namespace ams::kern { return this->termination_requested || this->GetRawState() == ThreadState_Terminated; } + size_t GetKernelStackUsage() const; public: /* Overridden parent functions. */ virtual u64 GetId() const override final { return this->GetThreadId(); } diff --git a/libraries/libmesosphere/source/kern_k_dump_object.cpp b/libraries/libmesosphere/source/kern_k_dump_object.cpp new file mode 100644 index 000000000..4aa24473b --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_dump_object.cpp @@ -0,0 +1,80 @@ +/* + * 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 . + */ +#include + +namespace ams::kern::KDumpObject { + + namespace { + + constexpr const char * const ThreadStates[] = { + [KThread::ThreadState_Initialized] = "Initialized", + [KThread::ThreadState_Waiting] = "Waiting", + [KThread::ThreadState_Runnable] = "Runnable", + [KThread::ThreadState_Terminated] = "Terminated", + }; + + void DumpThread(KThread *thread) { + if (KProcess *process = thread->GetOwnerProcess(); process != nullptr) { + MESOSPHERE_LOG("Thread ID=%5lu pid=%3lu %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu Run=%d Ideal=%d (%d) Affinity=%016lx (%016lx)\n", + thread->GetId(), process->GetId(), process->GetName(), thread->GetPriority(), ThreadStates[thread->GetState()], + thread->GetKernelStackUsage(), PageSize, thread->GetActiveCore(), thread->GetIdealVirtualCore(), thread->GetIdealPhysicalCore(), + thread->GetVirtualAffinityMask(), thread->GetAffinityMask().GetAffinityMask()); + + MESOSPHERE_LOG(" State: 0x%04x Suspend: 0x%04x Dpc: 0x%x\n", thread->GetRawState(), thread->GetSuspendFlags(), thread->GetDpc()); + + MESOSPHERE_LOG(" TLS: %p (%p)\n", GetVoidPointer(thread->GetThreadLocalRegionAddress()), thread->GetThreadLocalRegionHeapAddress()); + } else { + MESOSPHERE_LOG("Thread ID=%5lu pid=%3d %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu Run=%d Ideal=%d (%d) Affinity=%016lx (%016lx)\n", + thread->GetId(), -1, "(kernel)", thread->GetPriority(), ThreadStates[thread->GetState()], + thread->GetKernelStackUsage(), PageSize, thread->GetActiveCore(), thread->GetIdealVirtualCore(), thread->GetIdealPhysicalCore(), + thread->GetVirtualAffinityMask(), thread->GetAffinityMask().GetAffinityMask()); + } + } + + } + + void DumpThread() { + 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) { + DumpThread(static_cast(std::addressof(*it))); + } + } + + MESOSPHERE_LOG("\n"); + } + + void DumpThread(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(); }; + DumpThread(thread); + } + } + + MESOSPHERE_LOG("\n"); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index f6f327d74..ea1316e58 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -24,6 +24,15 @@ namespace ams::kern { return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast; } + void InitializeKernelStack(uintptr_t stack_top) { + #if defined(MESOSPHERE_ENABLE_KERNEL_STACK_USAGE) + const uintptr_t stack_bottom = stack_top - PageSize; + std::memset(reinterpret_cast(stack_bottom), 0xCC, PageSize - sizeof(KThread::StackParameters)); + #else + MESOSPHERE_UNUSED(stack_top); + #endif + } + void CleanupKernelStack(uintptr_t stack_top) { const uintptr_t stack_bottom = stack_top - PageSize; @@ -153,6 +162,11 @@ namespace ams::kern { this->resource_limit_release_hint = 0; this->cpu_time = 0; + /* Setup our kernel stack. */ + if (type != ThreadType_Main) { + InitializeKernelStack(reinterpret_cast(kern_stack_top)); + } + /* Clear our stack parameters. */ std::memset(static_cast(std::addressof(this->GetStackParameters())), 0, sizeof(StackParameters)); @@ -803,6 +817,26 @@ namespace ams::kern { KScheduler::OnThreadStateChanged(this, old_state); } + size_t KThread::GetKernelStackUsage() const { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(this->kernel_stack_top != nullptr); + + #if defined(MESOSPHERE_ENABLE_KERNEL_STACK_USAGE) + const u8 *stack = static_cast(this->kernel_stack_top) - PageSize; + + size_t i; + for (i = 0; i < PageSize; ++i) { + if (stack[i] != 0xCC) { + break; + } + } + + return PageSize - i; + #else + return 0; + #endif + } + Result KThread::SetActivity(ams::svc::ThreadActivity activity) { /* Lock ourselves. */ KScopedLightLock lk(this->activity_pause_lock); diff --git a/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp b/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp index 177f4a50d..c905f8017 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp @@ -22,19 +22,27 @@ namespace ams::kern::svc { namespace { void KernelDebug(ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) { - #ifdef ATMOSPHERE_BUILD_FOR_DEBUGGING + MESOSPHERE_UNUSED(kern_debug_type, arg0, arg1, arg2); + + #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING { - /* TODO: Implement Kernel Debugging. */ - } - #else - { - MESOSPHERE_UNUSED(kern_debug_type, arg0, arg1, arg2); + switch (kern_debug_type) { + case ams::svc::KernelDebugType_Thread: + if (arg0 == static_cast(-1)) { + KDumpObject::DumpThread(); + } else { + KDumpObject::DumpThread(arg0); + } + break; + default: + break; + } } #endif } void ChangeKernelTraceState(ams::svc::KernelTraceState kern_trace_state) { - #ifdef ATMOSPHERE_BUILD_FOR_DEBUGGING + #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING { switch (kern_trace_state) { case ams::svc::KernelTraceState_Enabled: diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 0630fc017..c5aff199c 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -470,7 +470,7 @@ namespace ams::svc { }; enum KernelDebugType : u32 { - /* TODO */ + KernelDebugType_Thread = 0, }; enum KernelTraceState : u32 {