From 4316fa8123786c4324b8a35ac5987af80519e00f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 25 Jul 2019 21:02:19 +0200 Subject: [PATCH] Kernel: Dump backtrace to debugger for DefaultSignalAction::DumpCore. This makes assertion failures generate backtraces again. Sorry to everyone who suffered from the lack of backtraces lately. :^) We share code with the /proc/PID/stack implementation. You can now get the current backtrace for a Thread via Thread::backtrace(), and all the traces for a Process via Process::backtrace(). --- Kernel/FileSystem/ProcFS.cpp | 32 +---------------------------- Kernel/Process.cpp | 12 +++++++++++ Kernel/Process.h | 2 ++ Kernel/Thread.cpp | 40 +++++++++++++++++++++++++++++++++++- Kernel/Thread.h | 8 ++++++++ 5 files changed, 62 insertions(+), 32 deletions(-) diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 9d781c0b68d..ce94ddf1bf5 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -314,37 +314,7 @@ ByteBuffer procfs$pid_stack(InodeIdentifier identifier) if (!handle) return {}; auto& process = handle->process(); - ProcessPagingScope paging_scope(process); - struct RecognizedSymbol { - u32 address; - const KSym* ksym; - }; - StringBuilder builder; - process.for_each_thread([&](Thread& thread) { - builder.appendf("Thread %d:\n", thread.tid()); - Vector recognized_symbols; - recognized_symbols.append({ thread.tss().eip, ksymbolicate(thread.tss().eip) }); - for (u32* stack_ptr = (u32*)thread.frame_ptr(); process.validate_read_from_kernel(VirtualAddress((u32)stack_ptr)); stack_ptr = (u32*)*stack_ptr) { - u32 retaddr = stack_ptr[1]; - recognized_symbols.append({ retaddr, ksymbolicate(retaddr) }); - } - - for (auto& symbol : recognized_symbols) { - if (!symbol.address) - break; - if (!symbol.ksym) { - builder.appendf("%p\n", symbol.address); - continue; - } - unsigned offset = symbol.address - symbol.ksym->address; - if (symbol.ksym->address == ksym_highest_address && offset > 4096) - builder.appendf("%p\n", symbol.address); - else - builder.appendf("%p %s +%u\n", symbol.address, symbol.ksym->name, offset); - } - return IterationDecision::Continue; - }); - return builder.to_byte_buffer(); + return process.backtrace(*handle).to_byte_buffer(); } ByteBuffer procfs$pid_regs(InodeIdentifier identifier) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index b0720f928ec..3e65b7b0b38 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -1279,6 +1279,7 @@ int Process::sys$kill(pid_t pid, int signal) ASSERT(pid != -1); } if (pid == m_pid) { + // FIXME: If we ignore this signal anyway, we don't need to block here, right? current->send_signal(signal, this); (void)current->block(Thread::SemiPermanentBlocker::Reason::Signal); return 0; @@ -2752,3 +2753,14 @@ int Process::sys$dbgputstr(const u8* characters, int length) IO::out8(0xe9, characters[i]); return 0; } + +String Process::backtrace(ProcessInspectionHandle& handle) const +{ + StringBuilder builder; + for_each_thread([&](Thread& thread) { + builder.appendf("Thread %d:\n", thread.tid()); + builder.append(thread.backtrace(handle)); + return IterationDecision::Continue; + }); + return builder.to_string(); +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 4e9a09d9730..7f38cb5e710 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -56,6 +56,8 @@ public: Ring3 = 3, }; + String backtrace(ProcessInspectionHandle&) const; + bool is_dead() const { return m_dead; } Thread::State state() const { return main_thread().state(); } diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 6616665ca6c..71194807c13 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -319,7 +320,12 @@ ShouldUnblockThread Thread::dispatch_signal(u8 signal) case DefaultSignalAction::Stop: set_state(Stopped); return ShouldUnblockThread::No; - case DefaultSignalAction::DumpCore: + case DefaultSignalAction::DumpCore: { + ProcessInspectionHandle handle(process()); + dbg() << "Dumping \"Core\" for " << process(); + dbg() << process().backtrace(handle); + } + [[fallthrough]]; case DefaultSignalAction::Terminate: m_process.terminate_due_to_signal(signal); return ShouldUnblockThread::No; @@ -543,3 +549,35 @@ void Thread::set_state(State new_state) Scheduler::update_state_for_thread(*this); } } + +String Thread::backtrace(ProcessInspectionHandle&) const +{ + auto& process = const_cast(this->process()); + ProcessPagingScope paging_scope(process); + struct RecognizedSymbol { + u32 address; + const KSym* ksym; + }; + StringBuilder builder; + Vector recognized_symbols; + recognized_symbols.append({ tss().eip, ksymbolicate(tss().eip) }); + for (u32* stack_ptr = (u32*)frame_ptr(); process.validate_read_from_kernel(VirtualAddress((u32)stack_ptr)); stack_ptr = (u32*)*stack_ptr) { + u32 retaddr = stack_ptr[1]; + recognized_symbols.append({ retaddr, ksymbolicate(retaddr) }); + } + + for (auto& symbol : recognized_symbols) { + if (!symbol.address) + break; + if (!symbol.ksym) { + builder.appendf("%p\n", symbol.address); + continue; + } + unsigned offset = symbol.address - symbol.ksym->address; + if (symbol.ksym->address == ksym_highest_address && offset > 4096) + builder.appendf("%p\n", symbol.address); + else + builder.appendf("%p %s +%u\n", symbol.address, symbol.ksym->name, offset); + } + return builder.to_string(); +} diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 2987474677f..74b1c417d61 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -16,6 +16,7 @@ class Alarm; class FileDescription; class Process; +class ProcessInspectionHandle; class Region; enum class ShouldUnblockThread { @@ -49,6 +50,8 @@ public: Process& process() { return m_process; } const Process& process() const { return m_process; } + String backtrace(ProcessInspectionHandle&) const; + void finalize(); enum State : u8 { @@ -205,6 +208,7 @@ public: u16 selector() const { return m_far_ptr.selector; } TSS32& tss() { return m_tss; } + const TSS32& tss() const { return m_tss; } State state() const { return m_state; } const char* state_string() const; u32 ticks() const { return m_ticks; } @@ -371,3 +375,7 @@ inline IterationDecision Thread::for_each_in_state(State state, Callback callbac return Scheduler::for_each_nonrunnable(new_callback); } +inline const LogStream& operator<<(const LogStream& stream, const Thread& value) +{ + return stream << "Thread{" << &value << "}(" << value.pid() << ":" << value.tid() << ")"; +}