mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 20:35:06 +00:00

Having an alias function that only wraps another one is silly, and keeping the more obvious name should flush out more uses of deprecated strings. No behavior change.
164 lines
5.5 KiB
C++
164 lines
5.5 KiB
C++
/*
|
|
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
|
|
* Copyright (c) 2022, the SerenityOS developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/LexicalPath.h>
|
|
#include <AK/Platform.h>
|
|
#include <AK/StringBuilder.h>
|
|
#include <AK/Types.h>
|
|
#include <LibCore/File.h>
|
|
#include <LibCore/MappedFile.h>
|
|
#include <LibCoredump/Backtrace.h>
|
|
#include <LibCoredump/Reader.h>
|
|
#include <LibELF/Core.h>
|
|
#include <LibELF/Image.h>
|
|
|
|
namespace Coredump {
|
|
|
|
ELFObjectInfo const* Backtrace::object_info_for_region(Reader const& coredump, MemoryRegionInfo const& region)
|
|
{
|
|
DeprecatedString path = coredump.resolve_object_path(region.object_name());
|
|
|
|
auto maybe_ptr = m_debug_info_cache.get(path);
|
|
if (maybe_ptr.has_value())
|
|
return *maybe_ptr;
|
|
|
|
if (!Core::File::exists(path))
|
|
return nullptr;
|
|
|
|
auto file_or_error = Core::MappedFile::map(path);
|
|
if (file_or_error.is_error())
|
|
return nullptr;
|
|
|
|
auto image = make<ELF::Image>(file_or_error.value()->bytes());
|
|
auto& image_reference = *image;
|
|
auto info = make<ELFObjectInfo>(file_or_error.release_value(), make<Debug::DebugInfo>(image_reference), move(image));
|
|
auto* info_ptr = info.ptr();
|
|
m_debug_info_cache.set(path, move(info));
|
|
return info_ptr;
|
|
}
|
|
|
|
Backtrace::Backtrace(Reader const& coredump, const ELF::Core::ThreadInfo& thread_info, Function<void(size_t, size_t)> on_progress)
|
|
: m_thread_info(move(thread_info))
|
|
{
|
|
#if ARCH(X86_64)
|
|
auto start_bp = m_thread_info.regs.rbp;
|
|
auto start_ip = m_thread_info.regs.rip;
|
|
#elif ARCH(AARCH64)
|
|
auto start_bp = 0;
|
|
auto start_ip = 0;
|
|
TODO_AARCH64();
|
|
#else
|
|
# error Unknown architecture
|
|
#endif
|
|
|
|
// In order to provide progress updates, we first have to walk the
|
|
// call stack to determine how many frames it has.
|
|
size_t frame_count = 0;
|
|
{
|
|
auto bp = start_bp;
|
|
auto ip = start_ip;
|
|
while (bp && ip) {
|
|
++frame_count;
|
|
auto next_ip = coredump.peek_memory(bp + sizeof(FlatPtr));
|
|
auto next_bp = coredump.peek_memory(bp);
|
|
if (!next_ip.has_value() || !next_bp.has_value())
|
|
break;
|
|
ip = next_ip.value();
|
|
bp = next_bp.value();
|
|
}
|
|
}
|
|
|
|
auto bp = start_bp;
|
|
auto ip = start_ip;
|
|
size_t frame_index = 0;
|
|
while (bp && ip) {
|
|
// We use eip - 1 because the return address from a function frame
|
|
// is the instruction that comes after the 'call' instruction.
|
|
// However, because the first frame represents the faulting
|
|
// instruction rather than the return address we don't subtract
|
|
// 1 there.
|
|
VERIFY(ip > 0);
|
|
add_entry(coredump, ip - ((frame_index == 0) ? 0 : 1));
|
|
if (on_progress)
|
|
on_progress(frame_index, frame_count);
|
|
++frame_index;
|
|
auto next_ip = coredump.peek_memory(bp + sizeof(FlatPtr));
|
|
auto next_bp = coredump.peek_memory(bp);
|
|
if (!next_ip.has_value() || !next_bp.has_value())
|
|
break;
|
|
ip = next_ip.value();
|
|
bp = next_bp.value();
|
|
}
|
|
}
|
|
|
|
void Backtrace::add_entry(Reader const& coredump, FlatPtr ip)
|
|
{
|
|
auto ip_region = coredump.region_containing(ip);
|
|
if (!ip_region.has_value()) {
|
|
m_entries.append({ ip, {}, {}, {} });
|
|
return;
|
|
}
|
|
auto object_name = ip_region->object_name();
|
|
// Only skip addresses coming from Loader.so if the faulting instruction is not in Loader.so
|
|
if (object_name == "Loader.so") {
|
|
if (m_skip_loader_so)
|
|
return;
|
|
} else {
|
|
m_skip_loader_so = true;
|
|
}
|
|
// We need to find the first region for the object, just in case
|
|
// the PT_LOAD header for the .text segment isn't the first one
|
|
// in the object file.
|
|
auto region = coredump.first_region_for_object(object_name);
|
|
auto object_info = object_info_for_region(coredump, *region);
|
|
if (!object_info) {
|
|
m_entries.append({ ip, object_name, {}, {} });
|
|
return;
|
|
}
|
|
|
|
auto function_name = object_info->debug_info->elf().symbolicate(ip - region->region_start);
|
|
auto source_position = object_info->debug_info->get_source_position_with_inlines(ip - region->region_start).release_value_but_fixme_should_propagate_errors();
|
|
m_entries.append({ ip, object_name, function_name, source_position });
|
|
}
|
|
|
|
DeprecatedString Backtrace::Entry::to_deprecated_string(bool color) const
|
|
{
|
|
StringBuilder builder;
|
|
builder.appendff("{:p}: ", eip);
|
|
if (object_name.is_empty()) {
|
|
builder.append("???"sv);
|
|
return builder.to_deprecated_string();
|
|
}
|
|
builder.appendff("[{}] {}", object_name, function_name.is_empty() ? "???" : function_name);
|
|
builder.append(" ("sv);
|
|
|
|
Vector<Debug::DebugInfo::SourcePosition> source_positions;
|
|
|
|
for (auto& position : source_position_with_inlines.inline_chain) {
|
|
if (!source_positions.contains_slow(position))
|
|
source_positions.append(position);
|
|
}
|
|
|
|
if (source_position_with_inlines.source_position.has_value() && !source_positions.contains_slow(source_position_with_inlines.source_position.value())) {
|
|
source_positions.insert(0, source_position_with_inlines.source_position.value());
|
|
}
|
|
|
|
for (size_t i = 0; i < source_positions.size(); ++i) {
|
|
auto& position = source_positions[i];
|
|
auto fmt = color ? "\033[34;1m{}\033[0m:{}"sv : "{}:{}"sv;
|
|
builder.appendff(fmt, LexicalPath::basename(position.file_path), position.line_number);
|
|
if (i != source_positions.size() - 1) {
|
|
builder.append(" => "sv);
|
|
}
|
|
}
|
|
|
|
builder.append(')');
|
|
|
|
return builder.to_deprecated_string();
|
|
}
|
|
|
|
}
|