Skip to content

Commit beb702c

Browse files
authored
Add support for arm64 registers in minidump core file saving. (llvm#72315)
This patch adds support for saving minidumps with the arm64 architecture. It also will cause unsupported architectures to emit an error where before this patch it would emit a minidump with partial information. This new code is tested by the arm64 windows buildbot that was failing: https://lab.llvm.org/buildbot/#/builders/219/builds/6868 This is needed following this PR: llvm#71772
1 parent 3dc098d commit beb702c

File tree

4 files changed

+170
-89
lines changed

4 files changed

+170
-89
lines changed

lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp

Lines changed: 150 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "MinidumpFileBuilder.h"
1010

11+
#include "Plugins/Process/minidump/RegisterContextMinidump_ARM64.h"
1112
#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"
1213

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

295296
uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
296-
const std::string &reg_name) {
297+
llvm::StringRef reg_name) {
297298
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
298299
if (!reg_info)
299300
return 0;
@@ -305,7 +306,7 @@ uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
305306
}
306307

307308
uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
308-
const std::string &reg_name) {
309+
llvm::StringRef reg_name) {
309310
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
310311
if (!reg_info)
311312
return 0;
@@ -317,7 +318,7 @@ uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
317318
}
318319

319320
uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
320-
const std::string &reg_name) {
321+
llvm::StringRef reg_name) {
321322
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
322323
if (!reg_info)
323324
return 0;
@@ -329,25 +330,42 @@ uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
329330
}
330331

331332
llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,
332-
const std::string &reg_name) {
333+
llvm::StringRef reg_name) {
333334
return static_cast<llvm::support::ulittle16_t>(
334335
read_register_u16_raw(reg_ctx, reg_name));
335336
}
336337

337338
llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,
338-
const std::string &reg_name) {
339+
llvm::StringRef reg_name) {
339340
return static_cast<llvm::support::ulittle32_t>(
340341
read_register_u32_raw(reg_ctx, reg_name));
341342
}
342343

343344
llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,
344-
const std::string &reg_name) {
345+
llvm::StringRef reg_name) {
345346
return static_cast<llvm::support::ulittle64_t>(
346347
read_register_u64_raw(reg_ctx, reg_name));
347348
}
348349

350+
void read_register_u128(RegisterContext *reg_ctx, llvm::StringRef reg_name,
351+
uint8_t *dst) {
352+
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
353+
if (reg_info) {
354+
lldb_private::RegisterValue reg_value;
355+
if (reg_ctx->ReadRegister(reg_info, reg_value)) {
356+
Status error;
357+
uint32_t bytes_copied = reg_value.GetAsMemoryData(
358+
*reg_info, dst, 16, lldb::ByteOrder::eByteOrderLittle, error);
359+
if (bytes_copied == 16)
360+
return;
361+
}
362+
}
363+
// If anything goes wrong, then zero out the register value.
364+
memset(dst, 0, 16);
365+
}
366+
349367
lldb_private::minidump::MinidumpContext_x86_64
350-
GetThreadContext_64(RegisterContext *reg_ctx) {
368+
GetThreadContext_x86_64(RegisterContext *reg_ctx) {
351369
lldb_private::minidump::MinidumpContext_x86_64 thread_context = {};
352370
thread_context.p1_home = {};
353371
thread_context.context_flags = static_cast<uint32_t>(
@@ -381,6 +399,71 @@ GetThreadContext_64(RegisterContext *reg_ctx) {
381399
return thread_context;
382400
}
383401

402+
minidump::RegisterContextMinidump_ARM64::Context
403+
GetThreadContext_ARM64(RegisterContext *reg_ctx) {
404+
minidump::RegisterContextMinidump_ARM64::Context thread_context = {};
405+
thread_context.context_flags = static_cast<uint32_t>(
406+
minidump::RegisterContextMinidump_ARM64::Flags::ARM64_Flag |
407+
minidump::RegisterContextMinidump_ARM64::Flags::Integer |
408+
minidump::RegisterContextMinidump_ARM64::Flags::FloatingPoint);
409+
char reg_name[16];
410+
for (uint32_t i = 0; i < 31; ++i) {
411+
snprintf(reg_name, sizeof(reg_name), "x%u", i);
412+
thread_context.x[i] = read_register_u64(reg_ctx, reg_name);
413+
}
414+
// Work around a bug in debugserver where "sp" on arm64 doesn't have the alt
415+
// name set to "x31"
416+
thread_context.x[31] = read_register_u64(reg_ctx, "sp");
417+
thread_context.pc = read_register_u64(reg_ctx, "pc");
418+
thread_context.cpsr = read_register_u32(reg_ctx, "cpsr");
419+
thread_context.fpsr = read_register_u32(reg_ctx, "fpsr");
420+
thread_context.fpcr = read_register_u32(reg_ctx, "fpcr");
421+
for (uint32_t i = 0; i < 32; ++i) {
422+
snprintf(reg_name, sizeof(reg_name), "v%u", i);
423+
read_register_u128(reg_ctx, reg_name, &thread_context.v[i * 16]);
424+
}
425+
return thread_context;
426+
}
427+
428+
class ArchThreadContexts {
429+
llvm::Triple::ArchType m_arch;
430+
union {
431+
lldb_private::minidump::MinidumpContext_x86_64 x86_64;
432+
lldb_private::minidump::RegisterContextMinidump_ARM64::Context arm64;
433+
};
434+
435+
public:
436+
ArchThreadContexts(llvm::Triple::ArchType arch) : m_arch(arch) {}
437+
438+
bool prepareRegisterContext(RegisterContext *reg_ctx) {
439+
switch (m_arch) {
440+
case llvm::Triple::ArchType::x86_64:
441+
x86_64 = GetThreadContext_x86_64(reg_ctx);
442+
return true;
443+
case llvm::Triple::ArchType::aarch64:
444+
arm64 = GetThreadContext_ARM64(reg_ctx);
445+
return true;
446+
default:
447+
break;
448+
}
449+
return false;
450+
}
451+
452+
const void *data() const { return &x86_64; }
453+
454+
size_t size() const {
455+
switch (m_arch) {
456+
case llvm::Triple::ArchType::x86_64:
457+
return sizeof(x86_64);
458+
case llvm::Triple::ArchType::aarch64:
459+
return sizeof(arm64);
460+
default:
461+
break;
462+
}
463+
return 0;
464+
}
465+
};
466+
384467
// Function returns start and size of the memory region that contains
385468
// memory location pointed to by the current stack pointer.
386469
llvm::Expected<std::pair<addr_t, addr_t>>
@@ -434,11 +517,20 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
434517
return error;
435518
}
436519
RegisterContext *reg_ctx = reg_ctx_sp.get();
437-
auto thread_context = GetThreadContext_64(reg_ctx);
438-
uint64_t rsp = read_register_u64_raw(reg_ctx, "rsp");
439-
auto expected_address_range = findStackHelper(process_sp, rsp);
520+
Target &target = process_sp->GetTarget();
521+
const ArchSpec &arch = target.GetArchitecture();
522+
ArchThreadContexts thread_context(arch.GetMachine());
523+
if (!thread_context.prepareRegisterContext(reg_ctx)) {
524+
error.SetErrorStringWithFormat(
525+
"architecture %s not supported.",
526+
arch.GetTriple().getArchName().str().c_str());
527+
return error;
528+
}
529+
uint64_t sp = reg_ctx->GetSP();
530+
auto expected_address_range = findStackHelper(process_sp, sp);
440531

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

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

475-
helper_data.AppendData(
476-
&thread_context,
477-
sizeof(lldb_private::minidump::MinidumpContext_x86_64));
569+
helper_data.AppendData(thread_context.data(), thread_context.size());
478570

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

495-
Status MinidumpFileBuilder::AddException(const lldb::ProcessSP &process_sp) {
496-
Status error;
587+
void MinidumpFileBuilder::AddExceptions(const lldb::ProcessSP &process_sp) {
497588
lldb_private::ThreadList thread_list = process_sp->GetThreadList();
498589

499590
const uint32_t num_threads = thread_list.GetSize();
500-
uint32_t stop_reason_thread_idx = 0;
501-
for (stop_reason_thread_idx = 0; stop_reason_thread_idx < num_threads;
502-
++stop_reason_thread_idx) {
503-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
591+
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
592+
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
504593
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
505-
506-
if (stop_info_sp && stop_info_sp->IsValid())
507-
break;
508-
}
509-
510-
if (stop_reason_thread_idx == num_threads) {
511-
error.SetErrorString("No stop reason thread found.");
512-
return error;
594+
bool add_exception = false;
595+
if (stop_info_sp) {
596+
switch (stop_info_sp->GetStopReason()) {
597+
case eStopReasonSignal:
598+
case eStopReasonException:
599+
add_exception = true;
600+
break;
601+
default:
602+
break;
603+
}
604+
}
605+
if (add_exception) {
606+
constexpr size_t minidump_exception_size =
607+
sizeof(llvm::minidump::ExceptionStream);
608+
AddDirectory(StreamType::Exception, minidump_exception_size);
609+
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
610+
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
611+
Exception exp_record = {};
612+
exp_record.ExceptionCode =
613+
static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
614+
exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
615+
exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
616+
exp_record.ExceptionAddress = reg_ctx_sp->GetPC();
617+
exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
618+
exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
619+
// exp_record.ExceptionInformation;
620+
621+
ExceptionStream exp_stream;
622+
exp_stream.ThreadId =
623+
static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
624+
exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
625+
exp_stream.ExceptionRecord = exp_record;
626+
auto Iter = m_tid_to_reg_ctx.find(thread_sp->GetID());
627+
if (Iter != m_tid_to_reg_ctx.end()) {
628+
exp_stream.ThreadContext = Iter->second;
629+
} else {
630+
exp_stream.ThreadContext.DataSize = 0;
631+
exp_stream.ThreadContext.RVA = 0;
632+
}
633+
m_data.AppendData(&exp_stream, minidump_exception_size);
634+
}
513635
}
514-
515-
constexpr size_t minidump_exception_size =
516-
sizeof(llvm::minidump::ExceptionStream);
517-
AddDirectory(StreamType::Exception, minidump_exception_size);
518-
size_t size_before = GetCurrentDataEndOffset();
519-
520-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
521-
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
522-
RegisterContext *reg_ctx = reg_ctx_sp.get();
523-
auto thread_context = GetThreadContext_64(reg_ctx);
524-
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
525-
526-
DataBufferHeap helper_data;
527-
528-
LocationDescriptor thread_context_memory_locator;
529-
thread_context_memory_locator.DataSize =
530-
static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
531-
thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
532-
size_before + minidump_exception_size + helper_data.GetByteSize());
533-
534-
helper_data.AppendData(
535-
&thread_context, sizeof(lldb_private::minidump::MinidumpContext_x86_64));
536-
537-
Exception exp_record = {};
538-
exp_record.ExceptionCode =
539-
static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
540-
exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
541-
exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
542-
exp_record.ExceptionAddress = read_register_u64(reg_ctx, "rip");
543-
exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
544-
exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
545-
// exp_record.ExceptionInformation;
546-
547-
ExceptionStream exp_stream;
548-
exp_stream.ThreadId =
549-
static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
550-
exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
551-
exp_stream.ExceptionRecord = exp_record;
552-
exp_stream.ThreadContext = thread_context_memory_locator;
553-
554-
m_data.AppendData(&exp_stream, minidump_exception_size);
555-
m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
556-
return error;
557636
}
558637

559638
lldb_private::Status

lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
1818

1919
#include <cstddef>
20+
#include <map>
2021

2122
#include "lldb/Target/Target.h"
2223
#include "lldb/Utility/DataBufferHeap.h"
@@ -59,10 +60,8 @@ class MinidumpFileBuilder {
5960
// at the moment of core saving. Contains information about thread
6061
// contexts.
6162
lldb_private::Status AddThreadList(const lldb::ProcessSP &process_sp);
62-
// Add Exception stream, this contains information about the exception
63-
// that stopped the process. In case no thread made exception it return
64-
// failed status.
65-
lldb_private::Status AddException(const lldb::ProcessSP &process_sp);
63+
// Add Exception streams for any threads that stopped with exceptions.
64+
void AddExceptions(const lldb::ProcessSP &process_sp);
6665
// Add MemoryList stream, containing dumps of important memory segments
6766
lldb_private::Status AddMemoryList(const lldb::ProcessSP &process_sp,
6867
lldb::SaveCoreStyle core_style);
@@ -88,6 +87,11 @@ class MinidumpFileBuilder {
8887
// Main data buffer consisting of data without the minidump header and
8988
// directories
9089
lldb_private::DataBufferHeap m_data;
90+
91+
// More that one place can mention the register thread context locations,
92+
// so when we emit the thread contents, remember where it is so we don't have
93+
// to duplicate it in the exception data.
94+
std::map<lldb::tid_t, llvm::minidump::LocationDescriptor> m_tid_to_reg_ctx;
9195
};
9296

9397
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H

lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,16 @@ bool ObjectFileMinidump::SaveCore(const lldb::ProcessSP &process_sp,
7878

7979
builder.AddMiscInfo(process_sp);
8080

81-
if (target.GetArchitecture().GetMachine() == llvm::Triple::ArchType::x86_64) {
82-
error = builder.AddThreadList(process_sp);
83-
if (error.Fail())
84-
return false;
85-
86-
error = builder.AddException(process_sp);
87-
if (error.Fail())
88-
return false;
89-
90-
error = builder.AddMemoryList(process_sp, core_style);
91-
if (error.Fail())
92-
return false;
93-
}
81+
error = builder.AddThreadList(process_sp);
82+
if (error.Fail())
83+
return false;
84+
85+
// Add any exceptions but only if there are any in any threads.
86+
builder.AddExceptions(process_sp);
87+
88+
error = builder.AddMemoryList(process_sp, core_style);
89+
if (error.Fail())
90+
return false;
9491

9592
if (target.GetArchitecture().GetTriple().getOS() ==
9693
llvm::Triple::OSType::Linux) {

lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,14 @@ class RegisterContextMinidump_ARM64 : public lldb_private::RegisterContext {
6767
uint8_t v[32 * 16]; // 32 128-bit floating point registers
6868
};
6969

70-
protected:
7170
enum class Flags : uint32_t {
7271
ARM64_Flag = 0x80000000,
7372
Integer = ARM64_Flag | 0x00000002,
7473
FloatingPoint = ARM64_Flag | 0x00000004,
7574
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint)
7675
};
76+
77+
protected:
7778
Context m_regs;
7879
};
7980

0 commit comments

Comments
 (0)