diff options
author | Linus Groh <mail@linusgroh.de> | 2020-12-29 13:04:26 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-12-29 15:42:30 +0100 |
commit | 2939ad0110ca2ec3da0081291ec0951385786c74 (patch) | |
tree | ef6008d9eba2e0b0a9bf8395ef9b77a212a5ca8d | |
parent | e2e2b2c08ef9eee7c6ed7a9a7199d50db1f48e1d (diff) | |
download | serenity-2939ad0110ca2ec3da0081291ec0951385786c74.zip |
LibCoreDump+CrashDaemon: Add and use CoreDump::Backtrace
Creating a backtrace from a crashdump already existed as a few
standalone functions in CrashDaemon. This patch refactors the code
required for this to make it generally usable and moves it to
CoreDump::Backtrace, which provides both raw data as well as
stringification.
-rw-r--r-- | Applications/CrashDaemon/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Applications/CrashDaemon/main.cpp | 120 | ||||
-rw-r--r-- | Libraries/LibCoreDump/Backtrace.cpp | 127 | ||||
-rw-r--r-- | Libraries/LibCoreDump/Backtrace.h | 68 | ||||
-rw-r--r-- | Libraries/LibCoreDump/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Libraries/LibCoreDump/Forward.h | 34 | ||||
-rw-r--r-- | Libraries/LibCoreDump/Reader.cpp | 8 | ||||
-rw-r--r-- | Libraries/LibCoreDump/Reader.h | 3 |
8 files changed, 251 insertions, 114 deletions
diff --git a/Applications/CrashDaemon/CMakeLists.txt b/Applications/CrashDaemon/CMakeLists.txt index e5da5d5ada..5dcf13aacb 100644 --- a/Applications/CrashDaemon/CMakeLists.txt +++ b/Applications/CrashDaemon/CMakeLists.txt @@ -3,4 +3,4 @@ set(SOURCES ) serenity_bin(CrashDaemon) -target_link_libraries(CrashDaemon LibC LibCore LibCoreDump LibDebug) +target_link_libraries(CrashDaemon LibC LibCore LibCoreDump) diff --git a/Applications/CrashDaemon/main.cpp b/Applications/CrashDaemon/main.cpp index aefa6885f3..b2f0d3debc 100644 --- a/Applications/CrashDaemon/main.cpp +++ b/Applications/CrashDaemon/main.cpp @@ -24,18 +24,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <AK/Assertions.h> -#include <AK/HashMap.h> #include <AK/LexicalPath.h> -#include <AK/LogStream.h> -#include <AK/ScopeGuard.h> #include <LibCore/DirectoryWatcher.h> -#include <LibCore/File.h> +#include <LibCoreDump/Backtrace.h> #include <LibCoreDump/Reader.h> -#include <LibDebug/DebugInfo.h> -#include <LibELF/Image.h> -#include <fcntl.h> -#include <stdio.h> #include <sys/stat.h> #include <time.h> #include <unistd.h> @@ -55,107 +47,15 @@ static void wait_until_coredump_is_ready(const String& coredump_path) } } -static String object_name(StringView memory_region_name) +static void print_backtrace(const String& coredump_path) { - if (memory_region_name.contains("Loader.so")) - return "Loader.so"; - if (!memory_region_name.contains(":")) - return {}; - return memory_region_name.substring_view(0, memory_region_name.find_first_of(":").value()).to_string(); -} - -struct ElfObjectInfo { - ElfObjectInfo(MappedFile&& file, Debug::DebugInfo&& debug_info) - : file(move(file)) - , debug_info(move(debug_info)) - { - } - - MappedFile file; - Debug::DebugInfo debug_info; -}; - -// FIXME: This cache has to be invalidated when libraries/programs are re-compiled. -// We can store the last-modified timestamp of the elf files in ElfObjectInfo to invalidate cache entries. -static HashMap<String, NonnullOwnPtr<ElfObjectInfo>> s_debug_info_cache; - -static const ElfObjectInfo* object_info_for_region(const ELF::Core::MemoryRegionInfo* region) -{ - auto name = object_name(region->region_name); - - String path; - if (name.contains(".so")) - path = String::formatted("/usr/lib/{}", name); - else { - path = name; - } - - if (auto it = s_debug_info_cache.find(path); it != s_debug_info_cache.end()) - return it->value.ptr(); - - if (!Core::File::exists(path.characters())) - return nullptr; - - MappedFile object_file(path); - if (!object_file.is_valid()) - return nullptr; - - auto info = make<ElfObjectInfo>(move(object_file), Debug::DebugInfo { make<ELF::Image>((const u8*)object_file.data(), object_file.size()) }); - auto* info_ptr = info.ptr(); - s_debug_info_cache.set(path, move(info)); - return info_ptr; -} - -static String backtrace_line(const CoreDump::Reader& coredump, FlatPtr eip) -{ - auto* region = coredump.region_containing((FlatPtr)eip); - if (!region) - return String::format("%p: ???", eip); - - if (StringView { region->region_name }.contains("Loader.so")) - return {}; - - auto* object_info = object_info_for_region(region); - if (!object_info) - return {}; - - auto func_name = object_info->debug_info.elf().symbolicate(eip - region->region_start); - - auto source_position = object_info->debug_info.get_source_position(eip - region->region_start); - - auto source_position_string = String::empty(); - if (source_position.has_value()) - source_position_string = String::format(" (\033[34;1m%s\033[0m:%u)", LexicalPath(source_position.value().file_path).basename().characters(), source_position.value().line_number); - - return String::format("%p: [%s] %s%s", eip, object_name(region->region_name).characters(), func_name.is_null() ? "???" : func_name.characters(), source_position_string.characters()); -} - -static void backtrace(const String& coredump_path) -{ - size_t thread_index = 0; auto coredump = CoreDump::Reader::create(coredump_path); - coredump->for_each_thread_info([&thread_index, &coredump](const ELF::Core::ThreadInfo& thread_info) { - dbgln("Backtrace for thread #{}, tid={}", thread_index++, thread_info.tid); - - uint32_t* ebp = (uint32_t*)thread_info.regs.ebp; - uint32_t* eip = (uint32_t*)thread_info.regs.eip; - while (ebp && eip) { - - auto line = backtrace_line(*coredump, (FlatPtr)eip); - if (!line.is_null()) - dbgprintf("%s\n", line.characters()); - auto next_eip = coredump->peek_memory((FlatPtr)(ebp + 1)); - auto next_ebp = coredump->peek_memory((FlatPtr)(ebp)); - if (!next_ebp.has_value() || !next_eip.has_value()) { - break; - } - - eip = (uint32_t*)next_eip.value(); - ebp = (uint32_t*)next_ebp.value(); - } - - return IterationDecision::Continue; - }); + if (!coredump) { + dbgln("Could not open coredump '{}'", coredump_path); + return; + } + for (auto& entry : coredump->backtrace().entries()) + dbgln("{}", entry.to_string(true)); } int main() @@ -170,9 +70,7 @@ int main() continue; auto coredump_path = event.value().child_path; dbgln("New coredump file: {}", coredump_path); - wait_until_coredump_is_ready(coredump_path); - - backtrace(coredump_path); + print_backtrace(coredump_path); } } diff --git a/Libraries/LibCoreDump/Backtrace.cpp b/Libraries/LibCoreDump/Backtrace.cpp new file mode 100644 index 0000000000..62a7ff9ce1 --- /dev/null +++ b/Libraries/LibCoreDump/Backtrace.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020, Linus Groh <mail@linusgroh.de> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <AK/LexicalPath.h> +#include <AK/MappedFile.h> +#include <AK/StringBuilder.h> +#include <AK/Types.h> +#include <LibCore/File.h> +#include <LibCoreDump/Backtrace.h> +#include <LibCoreDump/Reader.h> +#include <LibELF/CoreDump.h> +#include <LibELF/Image.h> + +namespace CoreDump { + +// FIXME: This cache has to be invalidated when libraries/programs are re-compiled. +// We can store the last-modified timestamp of the elf files in ELFObjectInfo to invalidate cache entries. +static HashMap<String, NonnullOwnPtr<ELFObjectInfo>> s_debug_info_cache; + +static const ELFObjectInfo* object_info_for_region(const ELF::Core::MemoryRegionInfo& region) +{ + auto name = region.object_name(); + + String path; + if (name.contains(".so")) + path = String::formatted("/usr/lib/{}", name); + else { + path = name; + } + + if (auto it = s_debug_info_cache.find(path); it != s_debug_info_cache.end()) + return it->value.ptr(); + + if (!Core::File::exists(path.characters())) + return nullptr; + + MappedFile object_file(path); + if (!object_file.is_valid()) + return nullptr; + + auto info = make<ELFObjectInfo>(move(object_file), Debug::DebugInfo { make<ELF::Image>((const u8*)object_file.data(), object_file.size()) }); + auto* info_ptr = info.ptr(); + s_debug_info_cache.set(path, move(info)); + return info_ptr; +} + +Backtrace::Backtrace(const Reader& coredump) +{ + coredump.for_each_thread_info([this, &coredump](const ELF::Core::ThreadInfo& thread_info) { + uint32_t* ebp = (uint32_t*)thread_info.regs.ebp; + uint32_t* eip = (uint32_t*)thread_info.regs.eip; + while (ebp && eip) { + add_backtrace_entry(coredump, (FlatPtr)eip); + auto next_eip = coredump.peek_memory((FlatPtr)(ebp + 1)); + auto next_ebp = coredump.peek_memory((FlatPtr)(ebp)); + if (!next_eip.has_value() || !next_ebp.has_value()) + break; + eip = (uint32_t*)next_eip.value(); + ebp = (uint32_t*)next_ebp.value(); + } + return IterationDecision::Continue; + }); +} + +Backtrace::~Backtrace() +{ +} + +void Backtrace::add_backtrace_entry(const Reader& coredump, FlatPtr eip) +{ + auto* region = coredump.region_containing((FlatPtr)eip); + if (!region) { + m_entries.append({ eip, {}, {}, {} }); + return; + } + auto object_name = region->object_name(); + if (object_name == "Loader.so") + return; + auto* object_info = object_info_for_region(*region); + if (!object_info) + return; + auto function_name = object_info->debug_info.elf().symbolicate(eip - region->region_start); + auto source_position = object_info->debug_info.get_source_position(eip - region->region_start); + m_entries.append({ eip, object_name, function_name, source_position }); +} + +String Backtrace::Entry::to_string(bool color) const +{ + StringBuilder builder; + builder.appendff("{:p}: ", eip); + if (object_name.is_empty()) { + builder.append("???"); + return builder.build(); + } + builder.appendff("[{}] {}", object_name, function_name.is_empty() ? "???" : function_name); + if (source_position.has_value()) { + auto& source_position = this->source_position.value(); + auto fmt = color ? " (\033[34;1m{}\033[0m:{})" : " ({}:{})"; + builder.appendff(fmt, LexicalPath(source_position.file_path).basename(), source_position.line_number); + } + return builder.build(); +} + +} diff --git a/Libraries/LibCoreDump/Backtrace.h b/Libraries/LibCoreDump/Backtrace.h new file mode 100644 index 0000000000..b2ded64414 --- /dev/null +++ b/Libraries/LibCoreDump/Backtrace.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, Linus Groh <mail@linusgroh.de> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/Types.h> +#include <LibCoreDump/Reader.h> +#include <LibDebug/DebugInfo.h> + +namespace CoreDump { + +struct ELFObjectInfo { + ELFObjectInfo(MappedFile&& file, Debug::DebugInfo&& debug_info) + : file(move(file)) + , debug_info(move(debug_info)) + { + } + + MappedFile file; + Debug::DebugInfo debug_info; +}; + +class Backtrace { +public: + struct Entry { + FlatPtr eip; + String object_name; + String function_name; + Optional<Debug::DebugInfo::SourcePosition> source_position; + + String to_string(bool color = false) const; + }; + + Backtrace(const Reader&); + ~Backtrace(); + + const Vector<Entry> entries() const { return m_entries; } + +private: + void add_backtrace_entry(const Reader&, FlatPtr eip); + + Vector<Entry> m_entries; +}; + +} diff --git a/Libraries/LibCoreDump/CMakeLists.txt b/Libraries/LibCoreDump/CMakeLists.txt index 4ddec4b1f5..f6a9457d30 100644 --- a/Libraries/LibCoreDump/CMakeLists.txt +++ b/Libraries/LibCoreDump/CMakeLists.txt @@ -1,6 +1,7 @@ set(SOURCES + Backtrace.cpp Reader.cpp ) serenity_lib(LibCoreDump coredump) -target_link_libraries(LibCoreDump LibC LibCore) +target_link_libraries(LibCoreDump LibC LibCore LibDebug) diff --git a/Libraries/LibCoreDump/Forward.h b/Libraries/LibCoreDump/Forward.h new file mode 100644 index 0000000000..e63caa1eb8 --- /dev/null +++ b/Libraries/LibCoreDump/Forward.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020, Linus Groh <mail@linusgroh.de> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace CoreDump { + +class Backtrace; +class Reader; + +} diff --git a/Libraries/LibCoreDump/Reader.cpp b/Libraries/LibCoreDump/Reader.cpp index b8df7f767a..7aae04fb8f 100644 --- a/Libraries/LibCoreDump/Reader.cpp +++ b/Libraries/LibCoreDump/Reader.cpp @@ -24,7 +24,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "Reader.h" +#include <LibCoreDump/Backtrace.h> +#include <LibCoreDump/Reader.h> #include <string.h> namespace CoreDump { @@ -120,4 +121,9 @@ const ELF::Core::MemoryRegionInfo* Reader::region_containing(FlatPtr address) co return ret; } +Backtrace Reader::backtrace() const +{ + return Backtrace(*this); +} + } diff --git a/Libraries/LibCoreDump/Reader.h b/Libraries/LibCoreDump/Reader.h index 7ec49ff51c..e754488b48 100644 --- a/Libraries/LibCoreDump/Reader.h +++ b/Libraries/LibCoreDump/Reader.h @@ -29,6 +29,7 @@ #include <AK/MappedFile.h> #include <AK/Noncopyable.h> #include <AK/OwnPtr.h> +#include <LibCoreDump/Forward.h> #include <LibELF/CoreDump.h> #include <LibELF/Image.h> @@ -54,6 +55,8 @@ public: Optional<uint32_t> peek_memory(FlatPtr address) const; const ELF::Core::MemoryRegionInfo* region_containing(FlatPtr address) const; + Backtrace backtrace() const; + private: class NotesEntryIterator { public: |