Skip to content

Add support for arm64 registers in minidump core file saving. #72315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 150 additions & 71 deletions lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "MinidumpFileBuilder.h"

#include "Plugins/Process/minidump/RegisterContextMinidump_ARM64.h"
#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"

#include "lldb/Core/Module.h"
Expand Down Expand Up @@ -293,7 +294,7 @@ Status MinidumpFileBuilder::AddModuleList(Target &target) {
}

uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
const std::string &reg_name) {
llvm::StringRef reg_name) {
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
if (!reg_info)
return 0;
Expand All @@ -305,7 +306,7 @@ uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
}

uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
const std::string &reg_name) {
llvm::StringRef reg_name) {
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
if (!reg_info)
return 0;
Expand All @@ -317,7 +318,7 @@ uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
}

uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
const std::string &reg_name) {
llvm::StringRef reg_name) {
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
if (!reg_info)
return 0;
Expand All @@ -329,25 +330,42 @@ uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
}

llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,
const std::string &reg_name) {
llvm::StringRef reg_name) {
return static_cast<llvm::support::ulittle16_t>(
read_register_u16_raw(reg_ctx, reg_name));
}

llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,
const std::string &reg_name) {
llvm::StringRef reg_name) {
return static_cast<llvm::support::ulittle32_t>(
read_register_u32_raw(reg_ctx, reg_name));
}

llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,
const std::string &reg_name) {
llvm::StringRef reg_name) {
return static_cast<llvm::support::ulittle64_t>(
read_register_u64_raw(reg_ctx, reg_name));
}

void read_register_u128(RegisterContext *reg_ctx, llvm::StringRef reg_name,
uint8_t *dst) {
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
if (reg_info) {
lldb_private::RegisterValue reg_value;
if (reg_ctx->ReadRegister(reg_info, reg_value)) {
Status error;
uint32_t bytes_copied = reg_value.GetAsMemoryData(
*reg_info, dst, 16, lldb::ByteOrder::eByteOrderLittle, error);
if (bytes_copied == 16)
return;
}
}
// If anything goes wrong, then zero out the register value.
memset(dst, 0, 16);
}

lldb_private::minidump::MinidumpContext_x86_64
GetThreadContext_64(RegisterContext *reg_ctx) {
GetThreadContext_x86_64(RegisterContext *reg_ctx) {
lldb_private::minidump::MinidumpContext_x86_64 thread_context = {};
thread_context.p1_home = {};
thread_context.context_flags = static_cast<uint32_t>(
Expand Down Expand Up @@ -381,6 +399,71 @@ GetThreadContext_64(RegisterContext *reg_ctx) {
return thread_context;
}

minidump::RegisterContextMinidump_ARM64::Context
GetThreadContext_ARM64(RegisterContext *reg_ctx) {
minidump::RegisterContextMinidump_ARM64::Context thread_context = {};
thread_context.context_flags = static_cast<uint32_t>(
minidump::RegisterContextMinidump_ARM64::Flags::ARM64_Flag |
minidump::RegisterContextMinidump_ARM64::Flags::Integer |
minidump::RegisterContextMinidump_ARM64::Flags::FloatingPoint);
char reg_name[16];
for (uint32_t i = 0; i < 31; ++i) {
snprintf(reg_name, sizeof(reg_name), "x%u", i);
thread_context.x[i] = read_register_u64(reg_ctx, reg_name);
}
// Work around a bug in debugserver where "sp" on arm64 doesn't have the alt
// name set to "x31"
thread_context.x[31] = read_register_u64(reg_ctx, "sp");
thread_context.pc = read_register_u64(reg_ctx, "pc");
thread_context.cpsr = read_register_u32(reg_ctx, "cpsr");
thread_context.fpsr = read_register_u32(reg_ctx, "fpsr");
thread_context.fpcr = read_register_u32(reg_ctx, "fpcr");
for (uint32_t i = 0; i < 32; ++i) {
snprintf(reg_name, sizeof(reg_name), "v%u", i);
read_register_u128(reg_ctx, reg_name, &thread_context.v[i * 16]);
}
return thread_context;
}

class ArchThreadContexts {
llvm::Triple::ArchType m_arch;
union {
lldb_private::minidump::MinidumpContext_x86_64 x86_64;
lldb_private::minidump::RegisterContextMinidump_ARM64::Context arm64;
};

public:
ArchThreadContexts(llvm::Triple::ArchType arch) : m_arch(arch) {}

bool prepareRegisterContext(RegisterContext *reg_ctx) {
switch (m_arch) {
case llvm::Triple::ArchType::x86_64:
x86_64 = GetThreadContext_x86_64(reg_ctx);
return true;
case llvm::Triple::ArchType::aarch64:
arm64 = GetThreadContext_ARM64(reg_ctx);
return true;
default:
break;
}
return false;
}

const void *data() const { return &x86_64; }

size_t size() const {
switch (m_arch) {
case llvm::Triple::ArchType::x86_64:
return sizeof(x86_64);
case llvm::Triple::ArchType::aarch64:
return sizeof(arm64);
default:
break;
}
return 0;
}
};

// Function returns start and size of the memory region that contains
// memory location pointed to by the current stack pointer.
llvm::Expected<std::pair<addr_t, addr_t>>
Expand Down Expand Up @@ -434,11 +517,20 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
return error;
}
RegisterContext *reg_ctx = reg_ctx_sp.get();
auto thread_context = GetThreadContext_64(reg_ctx);
uint64_t rsp = read_register_u64_raw(reg_ctx, "rsp");
auto expected_address_range = findStackHelper(process_sp, rsp);
Target &target = process_sp->GetTarget();
const ArchSpec &arch = target.GetArchitecture();
ArchThreadContexts thread_context(arch.GetMachine());
if (!thread_context.prepareRegisterContext(reg_ctx)) {
error.SetErrorStringWithFormat(
"architecture %s not supported.",
arch.GetTriple().getArchName().str().c_str());
return error;
}
uint64_t sp = reg_ctx->GetSP();
auto expected_address_range = findStackHelper(process_sp, sp);

if (!expected_address_range) {
consumeError(expected_address_range.takeError());
error.SetErrorString("Unable to get the stack address.");
return error;
}
Expand Down Expand Up @@ -468,13 +560,13 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {

LocationDescriptor thread_context_memory_locator;
thread_context_memory_locator.DataSize =
static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
static_cast<llvm::support::ulittle32_t>(thread_context.size());
thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
size_before + thread_stream_size + helper_data.GetByteSize());
// Cache thie thread context memory so we can reuse for exceptions.
m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator;

helper_data.AppendData(
&thread_context,
sizeof(lldb_private::minidump::MinidumpContext_x86_64));
helper_data.AppendData(thread_context.data(), thread_context.size());

llvm::minidump::Thread t;
t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
Expand All @@ -492,68 +584,55 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
return Status();
}

Status MinidumpFileBuilder::AddException(const lldb::ProcessSP &process_sp) {
Status error;
void MinidumpFileBuilder::AddExceptions(const lldb::ProcessSP &process_sp) {
lldb_private::ThreadList thread_list = process_sp->GetThreadList();

const uint32_t num_threads = thread_list.GetSize();
uint32_t stop_reason_thread_idx = 0;
for (stop_reason_thread_idx = 0; stop_reason_thread_idx < num_threads;
++stop_reason_thread_idx) {
ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();

if (stop_info_sp && stop_info_sp->IsValid())
break;
}

if (stop_reason_thread_idx == num_threads) {
error.SetErrorString("No stop reason thread found.");
return error;
bool add_exception = false;
if (stop_info_sp) {
switch (stop_info_sp->GetStopReason()) {
case eStopReasonSignal:
case eStopReasonException:
add_exception = true;
break;
default:
break;
}
}
if (add_exception) {
constexpr size_t minidump_exception_size =
sizeof(llvm::minidump::ExceptionStream);
AddDirectory(StreamType::Exception, minidump_exception_size);
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
Exception exp_record = {};
exp_record.ExceptionCode =
static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
exp_record.ExceptionAddress = reg_ctx_sp->GetPC();
exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
// exp_record.ExceptionInformation;

ExceptionStream exp_stream;
exp_stream.ThreadId =
static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
exp_stream.ExceptionRecord = exp_record;
auto Iter = m_tid_to_reg_ctx.find(thread_sp->GetID());
if (Iter != m_tid_to_reg_ctx.end()) {
exp_stream.ThreadContext = Iter->second;
} else {
exp_stream.ThreadContext.DataSize = 0;
exp_stream.ThreadContext.RVA = 0;
}
m_data.AppendData(&exp_stream, minidump_exception_size);
}
}

constexpr size_t minidump_exception_size =
sizeof(llvm::minidump::ExceptionStream);
AddDirectory(StreamType::Exception, minidump_exception_size);
size_t size_before = GetCurrentDataEndOffset();

ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
RegisterContext *reg_ctx = reg_ctx_sp.get();
auto thread_context = GetThreadContext_64(reg_ctx);
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();

DataBufferHeap helper_data;

LocationDescriptor thread_context_memory_locator;
thread_context_memory_locator.DataSize =
static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
size_before + minidump_exception_size + helper_data.GetByteSize());

helper_data.AppendData(
&thread_context, sizeof(lldb_private::minidump::MinidumpContext_x86_64));

Exception exp_record = {};
exp_record.ExceptionCode =
static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
exp_record.ExceptionAddress = read_register_u64(reg_ctx, "rip");
exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
// exp_record.ExceptionInformation;

ExceptionStream exp_stream;
exp_stream.ThreadId =
static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
exp_stream.ExceptionRecord = exp_record;
exp_stream.ThreadContext = thread_context_memory_locator;

m_data.AppendData(&exp_stream, minidump_exception_size);
m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
return error;
}

lldb_private::Status
Expand Down
12 changes: 8 additions & 4 deletions lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H

#include <cstddef>
#include <map>

#include "lldb/Target/Target.h"
#include "lldb/Utility/DataBufferHeap.h"
Expand Down Expand Up @@ -59,10 +60,8 @@ class MinidumpFileBuilder {
// at the moment of core saving. Contains information about thread
// contexts.
lldb_private::Status AddThreadList(const lldb::ProcessSP &process_sp);
// Add Exception stream, this contains information about the exception
// that stopped the process. In case no thread made exception it return
// failed status.
lldb_private::Status AddException(const lldb::ProcessSP &process_sp);
// Add Exception streams for any threads that stopped with exceptions.
void AddExceptions(const lldb::ProcessSP &process_sp);
// Add MemoryList stream, containing dumps of important memory segments
lldb_private::Status AddMemoryList(const lldb::ProcessSP &process_sp,
lldb::SaveCoreStyle core_style);
Expand All @@ -88,6 +87,11 @@ class MinidumpFileBuilder {
// Main data buffer consisting of data without the minidump header and
// directories
lldb_private::DataBufferHeap m_data;

// More that one place can mention the register thread context locations,
// so when we emit the thread contents, remember where it is so we don't have
// to duplicate it in the exception data.
std::map<lldb::tid_t, llvm::minidump::LocationDescriptor> m_tid_to_reg_ctx;
};

#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
23 changes: 10 additions & 13 deletions lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,16 @@ bool ObjectFileMinidump::SaveCore(const lldb::ProcessSP &process_sp,

builder.AddMiscInfo(process_sp);

if (target.GetArchitecture().GetMachine() == llvm::Triple::ArchType::x86_64) {
error = builder.AddThreadList(process_sp);
if (error.Fail())
return false;

error = builder.AddException(process_sp);
if (error.Fail())
return false;

error = builder.AddMemoryList(process_sp, core_style);
if (error.Fail())
return false;
}
error = builder.AddThreadList(process_sp);
if (error.Fail())
return false;

// Add any exceptions but only if there are any in any threads.
builder.AddExceptions(process_sp);

error = builder.AddMemoryList(process_sp, core_style);
if (error.Fail())
return false;

if (target.GetArchitecture().GetTriple().getOS() ==
llvm::Triple::OSType::Linux) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ class RegisterContextMinidump_ARM64 : public lldb_private::RegisterContext {
uint8_t v[32 * 16]; // 32 128-bit floating point registers
};

protected:
enum class Flags : uint32_t {
ARM64_Flag = 0x80000000,
Integer = ARM64_Flag | 0x00000002,
FloatingPoint = ARM64_Flag | 0x00000004,
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint)
};

protected:
Context m_regs;
};

Expand Down