mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 15:32:10 +08:00
Add ARM family minidump support
Bug: crashpad:30 Change-Id: I6784d42ba6c525c4e0b16dfdbbb4949c83e32fea Reviewed-on: https://chromium-review.googlesource.com/888541 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
693ff6d550
commit
c9244d58df
@ -336,6 +336,102 @@ struct alignas(16) MinidumpContextAMD64 {
|
|||||||
//! \}
|
//! \}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! \brief 32-bit ARM-specifc flags for MinidumpContextARM::context_flags.
|
||||||
|
enum MinidumpContextARMFlags : uint32_t {
|
||||||
|
//! \brief Identifies the context structure as 32-bit ARM.
|
||||||
|
kMinidumpContextARM = 0x40000000,
|
||||||
|
|
||||||
|
//! \brief Indicates the validity of integer regsiters.
|
||||||
|
//!
|
||||||
|
//! Regsiters `r0`-`r15` and `cpsr` are valid.
|
||||||
|
kMinidumpContextARMInteger = kMinidumpContextARM | 0x00000002,
|
||||||
|
|
||||||
|
//! \brief Inidicates the validity of VFP regsiters.
|
||||||
|
//!
|
||||||
|
//! Registers `d0`-`d31` and `fpscr` are valid.
|
||||||
|
kMinidumpContextARMVFP = kMinidumpContextARM | 0x00000004,
|
||||||
|
|
||||||
|
//! \brief Indicates the validity of all registers.
|
||||||
|
kMinidumpContextARMAll = kMinidumpContextARMInteger | kMinidumpContextARMVFP,
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief A 32-bit ARM CPU context (register state) carried in a minidump file.
|
||||||
|
struct MinidumpContextARM {
|
||||||
|
//! \brief A bitfield composed of values of #MinidumpContextFlags and
|
||||||
|
//! #MinidumpContextARMFlags.
|
||||||
|
//!
|
||||||
|
//! This field identifies the context structure as a 32-bit ARM CPU context,
|
||||||
|
//! and indicates which other fields in the structure are valid.
|
||||||
|
uint32_t context_flags;
|
||||||
|
|
||||||
|
//! \brief General-purpose registers `r0`-`r15`.
|
||||||
|
uint32_t regs[11];
|
||||||
|
uint32_t fp; // r11
|
||||||
|
uint32_t ip; // r12
|
||||||
|
uint32_t sp; // r13
|
||||||
|
uint32_t lr; // r14
|
||||||
|
uint32_t pc; // r15
|
||||||
|
|
||||||
|
//! \brief Current program status register.
|
||||||
|
uint32_t cpsr;
|
||||||
|
|
||||||
|
//! \brief Floating-point status and control register.
|
||||||
|
uint32_t fpscr;
|
||||||
|
|
||||||
|
//! \brief VFP registers `d0`-`d31`.
|
||||||
|
uint64_t vfp[32];
|
||||||
|
|
||||||
|
//! \brief This space is unused. It is included for compatibility with
|
||||||
|
//! breakpad (which also doesn't use it).
|
||||||
|
uint32_t extra[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags.
|
||||||
|
enum MinidumpContextARM64Flags : uint32_t {
|
||||||
|
//! \brief Identifies the context structure as 64-bit ARM.
|
||||||
|
kMinidumpContextARM64 = 0x80000000,
|
||||||
|
|
||||||
|
//! \brief Indicates the validty of integer registers.
|
||||||
|
//!
|
||||||
|
//! Registers `x0`-`x31`, `pc`, and `cpsr`.
|
||||||
|
kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002,
|
||||||
|
|
||||||
|
//! \brief Indicates the validity of fpsimd registers.
|
||||||
|
//!
|
||||||
|
//! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid.
|
||||||
|
kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004,
|
||||||
|
|
||||||
|
//! \brief Indicates the validity of all registers.
|
||||||
|
kMinidumpContextARM64All =
|
||||||
|
kMinidumpContextARM64Integer | kMinidumpContextARM64Fpsimd,
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief A 64-bit ARM CPU context (register state) carried in a minidump file.
|
||||||
|
struct MinidumpContextARM64 {
|
||||||
|
uint64_t context_flags;
|
||||||
|
|
||||||
|
//! \brief General-purpose registers `x0`-`x30`.
|
||||||
|
uint64_t regs[31];
|
||||||
|
|
||||||
|
//! \brief Stack pointer or `x31`.
|
||||||
|
uint64_t sp;
|
||||||
|
|
||||||
|
//! \brief Program counter.
|
||||||
|
uint64_t pc;
|
||||||
|
|
||||||
|
//! \brief Current program status register.
|
||||||
|
uint32_t cpsr;
|
||||||
|
|
||||||
|
//! \brief Floating-point status register.
|
||||||
|
uint32_t fpsr;
|
||||||
|
|
||||||
|
//! \brief Floating-point control register.
|
||||||
|
uint32_t fpcr;
|
||||||
|
|
||||||
|
//! \brief NEON registers `v0`-`v31`.
|
||||||
|
uint128_struct fpsimd[32];
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
|
||||||
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
|
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
|
||||||
|
@ -73,6 +73,19 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case kCPUArchitectureARM: {
|
||||||
|
context = std::make_unique<MinidumpContextARMWriter>();
|
||||||
|
reinterpret_cast<MinidumpContextARMWriter*>(context.get())
|
||||||
|
->InitializeFromSnapshot(context_snapshot->arm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kCPUArchitectureARM64: {
|
||||||
|
context = std::make_unique<MinidumpContextARM64Writer>();
|
||||||
|
reinterpret_cast<MinidumpContextARM64Writer*>(context.get())
|
||||||
|
->InitializeFromSnapshot(context_snapshot->arm64);
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
LOG(ERROR) << "unknown context architecture "
|
LOG(ERROR) << "unknown context architecture "
|
||||||
<< context_snapshot->architecture;
|
<< context_snapshot->architecture;
|
||||||
@ -239,4 +252,90 @@ size_t MinidumpContextAMD64Writer::ContextSize() const {
|
|||||||
return sizeof(context_);
|
return sizeof(context_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MinidumpContextARMWriter::MinidumpContextARMWriter()
|
||||||
|
: MinidumpContextWriter(), context_() {
|
||||||
|
context_.context_flags = kMinidumpContextARM;
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpContextARMWriter::~MinidumpContextARMWriter() = default;
|
||||||
|
|
||||||
|
void MinidumpContextARMWriter::InitializeFromSnapshot(
|
||||||
|
const CPUContextARM* context_snapshot) {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
DCHECK_EQ(context_.context_flags, kMinidumpContextARM);
|
||||||
|
|
||||||
|
context_.context_flags = kMinidumpContextARMAll;
|
||||||
|
|
||||||
|
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
|
||||||
|
"GPRS size mismatch");
|
||||||
|
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
|
||||||
|
context_.fp = context_snapshot->fp;
|
||||||
|
context_.ip = context_snapshot->ip;
|
||||||
|
context_.sp = context_snapshot->sp;
|
||||||
|
context_.lr = context_snapshot->lr;
|
||||||
|
context_.pc = context_snapshot->pc;
|
||||||
|
context_.cpsr = context_snapshot->cpsr;
|
||||||
|
|
||||||
|
context_.fpscr = context_snapshot->vfp_regs.fpscr;
|
||||||
|
static_assert(sizeof(context_.vfp) == sizeof(context_snapshot->vfp_regs.vfp),
|
||||||
|
"VFP size mismatch");
|
||||||
|
memcpy(context_.vfp, context_snapshot->vfp_regs.vfp, sizeof(context_.vfp));
|
||||||
|
|
||||||
|
memset(context_.extra, 0, sizeof(context_.extra));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpContextARMWriter::WriteObject(FileWriterInterface* file_writer) {
|
||||||
|
DCHECK_EQ(state(), kStateWritable);
|
||||||
|
return file_writer->Write(&context_, sizeof(context_));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MinidumpContextARMWriter::ContextSize() const {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
return sizeof(context_);
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpContextARM64Writer::MinidumpContextARM64Writer()
|
||||||
|
: MinidumpContextWriter(), context_() {
|
||||||
|
context_.context_flags = kMinidumpContextARM64;
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpContextARM64Writer::~MinidumpContextARM64Writer() = default;
|
||||||
|
|
||||||
|
void MinidumpContextARM64Writer::InitializeFromSnapshot(
|
||||||
|
const CPUContextARM64* context_snapshot) {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
DCHECK_EQ(context_.context_flags, kMinidumpContextARM64);
|
||||||
|
|
||||||
|
context_.context_flags = kMinidumpContextARM64All;
|
||||||
|
|
||||||
|
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
|
||||||
|
"GPRs size mismatch");
|
||||||
|
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
|
||||||
|
context_.sp = context_snapshot->sp;
|
||||||
|
context_.pc = context_snapshot->pc;
|
||||||
|
|
||||||
|
if (context_snapshot->pstate >
|
||||||
|
std::numeric_limits<decltype(context_.cpsr)>::max()) {
|
||||||
|
LOG(WARNING) << "pstate truncation";
|
||||||
|
}
|
||||||
|
context_.cpsr =
|
||||||
|
static_cast<decltype(context_.cpsr)>(context_snapshot->pstate);
|
||||||
|
|
||||||
|
context_.fpsr = context_snapshot->fpsr;
|
||||||
|
context_.fpcr = context_snapshot->fpcr;
|
||||||
|
static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd),
|
||||||
|
"FPSIMD size mismatch");
|
||||||
|
memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) {
|
||||||
|
DCHECK_EQ(state(), kStateWritable);
|
||||||
|
return file_writer->Write(&context_, sizeof(context_));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MinidumpContextARM64Writer::ContextSize() const {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
return sizeof(context_);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -155,6 +155,86 @@ class MinidumpContextAMD64Writer final : public MinidumpContextWriter {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer);
|
DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! \brief The writer for a MinidumpContextARM structure in a minidump file.
|
||||||
|
class MinidumpContextARMWriter final : public MinidumpContextWriter {
|
||||||
|
public:
|
||||||
|
MinidumpContextARMWriter();
|
||||||
|
~MinidumpContextARMWriter() override;
|
||||||
|
|
||||||
|
//! \brief Initializes the MinidumpContextARM based on \a context_snapshot.
|
||||||
|
//!
|
||||||
|
//! \param[in] context_snapshot The context snapshot to use as source data.
|
||||||
|
//!
|
||||||
|
//! \note Valid in #kStateMutable. No mutation of context() may be done before
|
||||||
|
//! calling this method, and it is not normally necessary to alter
|
||||||
|
//! context() after calling this method.
|
||||||
|
void InitializeFromSnapshot(const CPUContextARM* context_snapshot);
|
||||||
|
|
||||||
|
//! \brief Returns a pointer to the context structure that this object will
|
||||||
|
//! write.
|
||||||
|
//!
|
||||||
|
//! \attention This returns a non-`const` pointer to this object’s private
|
||||||
|
//! data so that a caller can populate the context structure directly.
|
||||||
|
//! This is done because providing setter interfaces to each field in the
|
||||||
|
//! context structure would be unwieldy and cumbersome. Care must be taken
|
||||||
|
//! to populate the context structure correctly. The context structure
|
||||||
|
//! must only be modified while this object is in the #kStateMutable
|
||||||
|
//! state.
|
||||||
|
MinidumpContextARM* context() { return &context_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// MinidumpWritable:
|
||||||
|
bool WriteObject(FileWriterInterface* file_writer) override;
|
||||||
|
|
||||||
|
// MinidumpContextWriter:
|
||||||
|
size_t ContextSize() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MinidumpContextARM context_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(MinidumpContextARMWriter);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief The writer for a MinidumpContextARM64 structure in a minidump file.
|
||||||
|
class MinidumpContextARM64Writer final : public MinidumpContextWriter {
|
||||||
|
public:
|
||||||
|
MinidumpContextARM64Writer();
|
||||||
|
~MinidumpContextARM64Writer() override;
|
||||||
|
|
||||||
|
//! \brief Initializes the MinidumpContextARM64 based on \a context_snapshot.
|
||||||
|
//!
|
||||||
|
//! \param[in] context_snapshot The context snapshot to use as source data.
|
||||||
|
//!
|
||||||
|
//! \note Valid in #kStateMutable. No mutation of context() may be done before
|
||||||
|
//! calling this method, and it is not normally necessary to alter
|
||||||
|
//! context() after calling this method.
|
||||||
|
void InitializeFromSnapshot(const CPUContextARM64* context_snapshot);
|
||||||
|
|
||||||
|
//! \brief Returns a pointer to the context structure that this object will
|
||||||
|
//! write.
|
||||||
|
//!
|
||||||
|
//! \attention This returns a non-`const` pointer to this object’s private
|
||||||
|
//! data so that a caller can populate the context structure directly.
|
||||||
|
//! This is done because providing setter interfaces to each field in the
|
||||||
|
//! context structure would be unwieldy and cumbersome. Care must be taken
|
||||||
|
//! to populate the context structure correctly. The context structure
|
||||||
|
//! must only be modified while this object is in the #kStateMutable
|
||||||
|
//! state.
|
||||||
|
MinidumpContextARM64* context() { return &context_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// MinidumpWritable:
|
||||||
|
bool WriteObject(FileWriterInterface* file_writer) override;
|
||||||
|
|
||||||
|
// MinidumpContextWriter:
|
||||||
|
size_t ContextSize() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MinidumpContextARM64 context_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(MinidumpContextARM64Writer);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
|
||||||
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
|
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
|
||||||
|
@ -28,6 +28,20 @@ namespace crashpad {
|
|||||||
namespace test {
|
namespace test {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
template <typename Writer, typename Context>
|
||||||
|
void EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) {
|
||||||
|
Writer context_writer;
|
||||||
|
StringFile string_file;
|
||||||
|
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
|
||||||
|
ASSERT_EQ(string_file.string().size(), sizeof(Context));
|
||||||
|
|
||||||
|
const Context* observed =
|
||||||
|
MinidumpWritableAtRVA<Context>(string_file.string(), 0);
|
||||||
|
ASSERT_TRUE(observed);
|
||||||
|
|
||||||
|
expect_context(0, observed, false);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
|
TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
|
||||||
StringFile string_file;
|
StringFile string_file;
|
||||||
|
|
||||||
@ -36,16 +50,8 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
|
|||||||
// context.
|
// context.
|
||||||
SCOPED_TRACE("zero");
|
SCOPED_TRACE("zero");
|
||||||
|
|
||||||
MinidumpContextX86Writer context_writer;
|
EmptyContextTest<MinidumpContextX86Writer, MinidumpContextX86>(
|
||||||
|
ExpectMinidumpContextX86);
|
||||||
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
|
|
||||||
ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86));
|
|
||||||
|
|
||||||
const MinidumpContextX86* observed =
|
|
||||||
MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
|
|
||||||
ASSERT_TRUE(observed);
|
|
||||||
|
|
||||||
ExpectMinidumpContextX86(0, observed, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -85,16 +91,8 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
|
|||||||
// context.
|
// context.
|
||||||
SCOPED_TRACE("zero");
|
SCOPED_TRACE("zero");
|
||||||
|
|
||||||
MinidumpContextAMD64Writer context_writer;
|
EmptyContextTest<MinidumpContextAMD64Writer, MinidumpContextAMD64>(
|
||||||
|
ExpectMinidumpContextAMD64);
|
||||||
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
|
|
||||||
ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64));
|
|
||||||
|
|
||||||
const MinidumpContextAMD64* observed =
|
|
||||||
MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
|
|
||||||
ASSERT_TRUE(observed);
|
|
||||||
|
|
||||||
ExpectMinidumpContextAMD64(0, observed, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -117,48 +115,72 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MinidumpContextWriter, CreateFromSnapshot_X86) {
|
template <typename Writer, typename Context>
|
||||||
constexpr uint32_t kSeed = 32;
|
void FromSnapshotTest(const CPUContext& snapshot_context,
|
||||||
|
void (*expect_context)(uint32_t, const Context*, bool),
|
||||||
CPUContextX86 context_snapshot_x86;
|
uint32_t seed) {
|
||||||
CPUContext context_snapshot;
|
|
||||||
context_snapshot.x86 = &context_snapshot_x86;
|
|
||||||
InitializeCPUContextX86(&context_snapshot, kSeed);
|
|
||||||
|
|
||||||
std::unique_ptr<MinidumpContextWriter> context_writer =
|
std::unique_ptr<MinidumpContextWriter> context_writer =
|
||||||
MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
|
MinidumpContextWriter::CreateFromSnapshot(&snapshot_context);
|
||||||
ASSERT_TRUE(context_writer);
|
ASSERT_TRUE(context_writer);
|
||||||
|
|
||||||
StringFile string_file;
|
StringFile string_file;
|
||||||
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
|
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
|
||||||
|
|
||||||
const MinidumpContextX86* observed =
|
const Context* observed =
|
||||||
MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
|
MinidumpWritableAtRVA<Context>(string_file.string(), 0);
|
||||||
ASSERT_TRUE(observed);
|
ASSERT_TRUE(observed);
|
||||||
|
|
||||||
ExpectMinidumpContextX86(kSeed, observed, true);
|
expect_context(seed, observed, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) {
|
TEST(MinidumpContextWriter, X86_FromSnapshot) {
|
||||||
|
constexpr uint32_t kSeed = 32;
|
||||||
|
CPUContextX86 context_x86;
|
||||||
|
CPUContext context;
|
||||||
|
context.x86 = &context_x86;
|
||||||
|
InitializeCPUContextX86(&context, kSeed);
|
||||||
|
FromSnapshotTest<MinidumpContextX86Writer, MinidumpContextX86>(
|
||||||
|
context, ExpectMinidumpContextX86, kSeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpContextWriter, AMD64_FromSnapshot) {
|
||||||
constexpr uint32_t kSeed = 64;
|
constexpr uint32_t kSeed = 64;
|
||||||
|
CPUContextX86_64 context_x86_64;
|
||||||
|
CPUContext context;
|
||||||
|
context.x86_64 = &context_x86_64;
|
||||||
|
InitializeCPUContextX86_64(&context, kSeed);
|
||||||
|
FromSnapshotTest<MinidumpContextAMD64Writer, MinidumpContextAMD64>(
|
||||||
|
context, ExpectMinidumpContextAMD64, kSeed);
|
||||||
|
}
|
||||||
|
|
||||||
CPUContextX86_64 context_snapshot_x86_64;
|
TEST(MinidumpContextWriter, ARM_Zeros) {
|
||||||
CPUContext context_snapshot;
|
EmptyContextTest<MinidumpContextARMWriter, MinidumpContextARM>(
|
||||||
context_snapshot.x86_64 = &context_snapshot_x86_64;
|
ExpectMinidumpContextARM);
|
||||||
InitializeCPUContextX86_64(&context_snapshot, kSeed);
|
}
|
||||||
|
|
||||||
std::unique_ptr<MinidumpContextWriter> context_writer =
|
TEST(MinidumpContextWRiter, ARM64_Zeros) {
|
||||||
MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
|
EmptyContextTest<MinidumpContextARM64Writer, MinidumpContextARM64>(
|
||||||
ASSERT_TRUE(context_writer);
|
ExpectMinidumpContextARM64);
|
||||||
|
}
|
||||||
|
|
||||||
StringFile string_file;
|
TEST(MinidumpContextWriter, ARM_FromSnapshot) {
|
||||||
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
|
constexpr uint32_t kSeed = 32;
|
||||||
|
CPUContextARM context_arm;
|
||||||
|
CPUContext context;
|
||||||
|
context.arm = &context_arm;
|
||||||
|
InitializeCPUContextARM(&context, kSeed);
|
||||||
|
FromSnapshotTest<MinidumpContextARMWriter, MinidumpContextARM>(
|
||||||
|
context, ExpectMinidumpContextARM, kSeed);
|
||||||
|
}
|
||||||
|
|
||||||
const MinidumpContextAMD64* observed =
|
TEST(MinidumpContextWriter, ARM64_FromSnapshot) {
|
||||||
MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
|
constexpr uint32_t kSeed = 64;
|
||||||
ASSERT_TRUE(observed);
|
CPUContextARM64 context_arm64;
|
||||||
|
CPUContext context;
|
||||||
ExpectMinidumpContextAMD64(kSeed, observed, true);
|
context.arm64 = &context_arm64;
|
||||||
|
InitializeCPUContextARM64(&context, kSeed);
|
||||||
|
FromSnapshotTest<MinidumpContextARM64Writer, MinidumpContextARM64>(
|
||||||
|
context, ExpectMinidumpContextARM64, kSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -140,6 +140,61 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
|||||||
context->last_exception_from_rip = value++;
|
context->last_exception_from_rip = value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed) {
|
||||||
|
if (seed == 0) {
|
||||||
|
memset(context, 0, sizeof(*context));
|
||||||
|
context->context_flags = kMinidumpContextARM;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->context_flags = kMinidumpContextARMAll;
|
||||||
|
|
||||||
|
uint32_t value = seed;
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(context->regs); ++index) {
|
||||||
|
context->regs[index] = value++;
|
||||||
|
}
|
||||||
|
context->fp = value++;
|
||||||
|
context->ip = value++;
|
||||||
|
context->ip = value++;
|
||||||
|
context->sp = value++;
|
||||||
|
context->lr = value++;
|
||||||
|
context->pc = value++;
|
||||||
|
context->cpsr = value++;
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(context->vfp); ++index) {
|
||||||
|
context->vfp[index] = value++;
|
||||||
|
}
|
||||||
|
context->fpscr = value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeMinidumpContextARM64(MinidumpContextARM64* context,
|
||||||
|
uint32_t seed) {
|
||||||
|
if (seed == 0) {
|
||||||
|
memset(context, 0, sizeof(*context));
|
||||||
|
context->context_flags = kMinidumpContextARM64;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->context_flags = kMinidumpContextARM64All;
|
||||||
|
|
||||||
|
uint32_t value = seed;
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(context->regs); ++index) {
|
||||||
|
context->regs[index] = value++;
|
||||||
|
}
|
||||||
|
context->sp = value++;
|
||||||
|
context->pc = value++;
|
||||||
|
context->cpsr = value++;
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(context->fpsimd); ++index) {
|
||||||
|
context->fpsimd[index].lo = value++;
|
||||||
|
context->fpsimd[index].hi = value++;
|
||||||
|
}
|
||||||
|
context->fpsr = value++;
|
||||||
|
context->fpcr = value++;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Using gtest assertions, compares |expected| to |observed|. This is
|
// Using gtest assertions, compares |expected| to |observed|. This is
|
||||||
@ -350,5 +405,53 @@ void ExpectMinidumpContextAMD64(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExpectMinidumpContextARM(uint32_t expect_seed,
|
||||||
|
const MinidumpContextARM* observed,
|
||||||
|
bool snapshot) {
|
||||||
|
MinidumpContextARM expected;
|
||||||
|
InitializeMinidumpContextARM(&expected, expect_seed);
|
||||||
|
|
||||||
|
EXPECT_EQ(observed->context_flags, expected.context_flags);
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(expected.regs); ++index) {
|
||||||
|
EXPECT_EQ(observed->regs[index], expected.regs[index]);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(observed->fp, expected.fp);
|
||||||
|
EXPECT_EQ(observed->ip, expected.ip);
|
||||||
|
EXPECT_EQ(observed->sp, expected.sp);
|
||||||
|
EXPECT_EQ(observed->lr, expected.lr);
|
||||||
|
EXPECT_EQ(observed->pc, expected.pc);
|
||||||
|
EXPECT_EQ(observed->cpsr, expected.cpsr);
|
||||||
|
|
||||||
|
EXPECT_EQ(observed->fpscr, expected.fpscr);
|
||||||
|
for (size_t index = 0; index < arraysize(expected.vfp); ++index) {
|
||||||
|
EXPECT_EQ(observed->vfp[index], expected.vfp[index]);
|
||||||
|
}
|
||||||
|
for (size_t index = 0; index < arraysize(expected.extra); ++index) {
|
||||||
|
EXPECT_EQ(observed->extra[index], snapshot ? 0 : expected.extra[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectMinidumpContextARM64(uint32_t expect_seed,
|
||||||
|
const MinidumpContextARM64* observed,
|
||||||
|
bool snapshot) {
|
||||||
|
MinidumpContextARM64 expected;
|
||||||
|
InitializeMinidumpContextARM64(&expected, expect_seed);
|
||||||
|
|
||||||
|
EXPECT_EQ(observed->context_flags, expected.context_flags);
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(expected.regs); ++index) {
|
||||||
|
EXPECT_EQ(observed->regs[index], expected.regs[index]);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(observed->cpsr, expected.cpsr);
|
||||||
|
|
||||||
|
EXPECT_EQ(observed->fpsr, expected.fpsr);
|
||||||
|
EXPECT_EQ(observed->fpcr, expected.fpcr);
|
||||||
|
for (size_t index = 0; index < arraysize(expected.fpsimd); ++index) {
|
||||||
|
EXPECT_EQ(observed->fpsimd[index].lo, expected.fpsimd[index].lo);
|
||||||
|
EXPECT_EQ(observed->fpsimd[index].hi, expected.fpsimd[index].hi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -41,6 +41,9 @@ namespace test {
|
|||||||
void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed);
|
void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed);
|
||||||
void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||||
uint32_t seed);
|
uint32_t seed);
|
||||||
|
void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed);
|
||||||
|
void InitializeMinidumpContextARM64(MinidumpContextARM64* context,
|
||||||
|
uint32_t seed);
|
||||||
//! \}
|
//! \}
|
||||||
|
|
||||||
//! \brief Verifies, via gtest assertions, that a context structure contains
|
//! \brief Verifies, via gtest assertions, that a context structure contains
|
||||||
@ -67,6 +70,12 @@ void ExpectMinidumpContextX86(
|
|||||||
uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot);
|
uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot);
|
||||||
void ExpectMinidumpContextAMD64(
|
void ExpectMinidumpContextAMD64(
|
||||||
uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot);
|
uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot);
|
||||||
|
void ExpectMinidumpContextARM(uint32_t expect_seed,
|
||||||
|
const MinidumpContextARM* observed,
|
||||||
|
bool snapshot);
|
||||||
|
void ExpectMinidumpContextARM64(uint32_t expect_seed,
|
||||||
|
const MinidumpContextARM64* observed,
|
||||||
|
bool snapshot);
|
||||||
//! \}
|
//! \}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
@ -22,6 +22,68 @@
|
|||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// This is templatized because the CPUContextX86::Fxsave and
|
||||||
|
// CPUContextX86_64::Fxsave are nearly identical but have different sizes for
|
||||||
|
// the members |xmm|, |reserved_4|, and |available|.
|
||||||
|
template <typename FxsaveType>
|
||||||
|
void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) {
|
||||||
|
uint32_t value = *seed;
|
||||||
|
|
||||||
|
fxsave->fcw = static_cast<uint16_t>(value++);
|
||||||
|
fxsave->fsw = static_cast<uint16_t>(value++);
|
||||||
|
fxsave->ftw = static_cast<uint8_t>(value++);
|
||||||
|
fxsave->reserved_1 = static_cast<uint8_t>(value++);
|
||||||
|
fxsave->fop = static_cast<uint16_t>(value++);
|
||||||
|
fxsave->fpu_ip = value++;
|
||||||
|
fxsave->fpu_cs = static_cast<uint16_t>(value++);
|
||||||
|
fxsave->reserved_2 = static_cast<uint16_t>(value++);
|
||||||
|
fxsave->fpu_dp = value++;
|
||||||
|
fxsave->fpu_ds = static_cast<uint16_t>(value++);
|
||||||
|
fxsave->reserved_3 = static_cast<uint16_t>(value++);
|
||||||
|
fxsave->mxcsr = value++;
|
||||||
|
fxsave->mxcsr_mask = value++;
|
||||||
|
for (size_t st_mm_index = 0; st_mm_index < arraysize(fxsave->st_mm);
|
||||||
|
++st_mm_index) {
|
||||||
|
for (size_t byte = 0; byte < arraysize(fxsave->st_mm[st_mm_index].st);
|
||||||
|
++byte) {
|
||||||
|
fxsave->st_mm[st_mm_index].st[byte] = static_cast<uint8_t>(value++);
|
||||||
|
}
|
||||||
|
for (size_t byte = 0;
|
||||||
|
byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved);
|
||||||
|
++byte) {
|
||||||
|
fxsave->st_mm[st_mm_index].st_reserved[byte] =
|
||||||
|
static_cast<uint8_t>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) {
|
||||||
|
for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) {
|
||||||
|
fxsave->xmm[xmm_index][byte] = static_cast<uint8_t>(value++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) {
|
||||||
|
fxsave->reserved_4[byte] = static_cast<uint8_t>(value++);
|
||||||
|
}
|
||||||
|
for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) {
|
||||||
|
fxsave->available[byte] = static_cast<uint8_t>(value++);
|
||||||
|
}
|
||||||
|
|
||||||
|
*seed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave,
|
||||||
|
uint32_t* seed) {
|
||||||
|
return InitializeCPUContextFxsave(fxsave, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave,
|
||||||
|
uint32_t* seed) {
|
||||||
|
return InitializeCPUContextFxsave(fxsave, seed);
|
||||||
|
}
|
||||||
|
|
||||||
void InitializeCPUContextX86(CPUContext* context, uint32_t seed) {
|
void InitializeCPUContextX86(CPUContext* context, uint32_t seed) {
|
||||||
context->architecture = kCPUArchitectureX86;
|
context->architecture = kCPUArchitectureX86;
|
||||||
|
|
||||||
@ -101,68 +163,61 @@ void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) {
|
|||||||
context->x86_64->dr7 = value++;
|
context->x86_64->dr7 = value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
void InitializeCPUContextARM(CPUContext* context, uint32_t seed) {
|
||||||
|
context->architecture = kCPUArchitectureARM;
|
||||||
|
CPUContextARM* arm = context->arm;
|
||||||
|
|
||||||
// This is templatized because the CPUContextX86::Fxsave and
|
if (seed == 0) {
|
||||||
// CPUContextX86_64::Fxsave are nearly identical but have different sizes for
|
memset(arm, 0, sizeof(*arm));
|
||||||
// the members |xmm|, |reserved_4|, and |available|.
|
return;
|
||||||
template <typename FxsaveType>
|
|
||||||
void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) {
|
|
||||||
uint32_t value = *seed;
|
|
||||||
|
|
||||||
fxsave->fcw = static_cast<uint16_t>(value++);
|
|
||||||
fxsave->fsw = static_cast<uint16_t>(value++);
|
|
||||||
fxsave->ftw = static_cast<uint8_t>(value++);
|
|
||||||
fxsave->reserved_1 = static_cast<uint8_t>(value++);
|
|
||||||
fxsave->fop = static_cast<uint16_t>(value++);
|
|
||||||
fxsave->fpu_ip = value++;
|
|
||||||
fxsave->fpu_cs = static_cast<uint16_t>(value++);
|
|
||||||
fxsave->reserved_2 = static_cast<uint16_t>(value++);
|
|
||||||
fxsave->fpu_dp = value++;
|
|
||||||
fxsave->fpu_ds = static_cast<uint16_t>(value++);
|
|
||||||
fxsave->reserved_3 = static_cast<uint16_t>(value++);
|
|
||||||
fxsave->mxcsr = value++;
|
|
||||||
fxsave->mxcsr_mask = value++;
|
|
||||||
for (size_t st_mm_index = 0;
|
|
||||||
st_mm_index < arraysize(fxsave->st_mm);
|
|
||||||
++st_mm_index) {
|
|
||||||
for (size_t byte = 0;
|
|
||||||
byte < arraysize(fxsave->st_mm[st_mm_index].st);
|
|
||||||
++byte) {
|
|
||||||
fxsave->st_mm[st_mm_index].st[byte] = static_cast<uint8_t>(value++);
|
|
||||||
}
|
|
||||||
for (size_t byte = 0;
|
|
||||||
byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved);
|
|
||||||
++byte) {
|
|
||||||
fxsave->st_mm[st_mm_index].st_reserved[byte] =
|
|
||||||
static_cast<uint8_t>(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) {
|
|
||||||
for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) {
|
|
||||||
fxsave->xmm[xmm_index][byte] = static_cast<uint8_t>(value++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) {
|
|
||||||
fxsave->reserved_4[byte] = static_cast<uint8_t>(value++);
|
|
||||||
}
|
|
||||||
for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) {
|
|
||||||
fxsave->available[byte] = static_cast<uint8_t>(value++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*seed = value;
|
uint32_t value = seed;
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(arm->regs); ++index) {
|
||||||
|
arm->regs[index] = value++;
|
||||||
|
}
|
||||||
|
arm->fp = value++;
|
||||||
|
arm->ip = value++;
|
||||||
|
arm->ip = value++;
|
||||||
|
arm->sp = value++;
|
||||||
|
arm->lr = value++;
|
||||||
|
arm->pc = value++;
|
||||||
|
arm->cpsr = value++;
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(arm->vfp_regs.vfp); ++index) {
|
||||||
|
arm->vfp_regs.vfp[index] = value++;
|
||||||
|
}
|
||||||
|
arm->vfp_regs.fpscr = value++;
|
||||||
|
|
||||||
|
arm->have_fpa_regs = false;
|
||||||
|
arm->have_vfp_regs = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
void InitializeCPUContextARM64(CPUContext* context, uint32_t seed) {
|
||||||
|
context->architecture = kCPUArchitectureARM64;
|
||||||
|
CPUContextARM64* arm64 = context->arm64;
|
||||||
|
|
||||||
void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave,
|
if (seed == 0) {
|
||||||
uint32_t* seed) {
|
memset(arm64, 0, sizeof(*arm64));
|
||||||
return InitializeCPUContextFxsave(fxsave, seed);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave,
|
uint32_t value = seed;
|
||||||
uint32_t* seed) {
|
|
||||||
return InitializeCPUContextFxsave(fxsave, seed);
|
for (size_t index = 0; index < arraysize(arm64->regs); ++index) {
|
||||||
|
arm64->regs[index] = value++;
|
||||||
|
}
|
||||||
|
arm64->sp = value++;
|
||||||
|
arm64->pc = value++;
|
||||||
|
arm64->pstate = value++;
|
||||||
|
|
||||||
|
for (size_t index = 0; index < arraysize(arm64->fpsimd); ++index) {
|
||||||
|
arm64->fpsimd[index].lo = value++;
|
||||||
|
arm64->fpsimd[index].hi = value++;
|
||||||
|
}
|
||||||
|
arm64->fpsr = value++;
|
||||||
|
arm64->fpcr = value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
@ -22,6 +22,25 @@
|
|||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
|
//! \brief Initializes an `fxsave` context substructure for testing.
|
||||||
|
//!
|
||||||
|
//! \param[out] fxsave The structure to initialize.
|
||||||
|
//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of
|
||||||
|
//! the same type with identical seed values should produce identical
|
||||||
|
//! structures. Initialization with a different seed value should produce
|
||||||
|
//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed
|
||||||
|
//! out entirely. If \a seed is nonzero, \a fxsave will be populated
|
||||||
|
//! entirely with nonzero values. \a seed will be updated by this function
|
||||||
|
//! to allow the caller to perform subsequent initialization of the context
|
||||||
|
//! structure containing \a fxsave.
|
||||||
|
//!
|
||||||
|
//! \{
|
||||||
|
void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave,
|
||||||
|
uint32_t* seed);
|
||||||
|
void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave,
|
||||||
|
uint32_t* seed);
|
||||||
|
//! \}
|
||||||
|
|
||||||
//! \brief Initializes a context structure for testing.
|
//! \brief Initializes a context structure for testing.
|
||||||
//!
|
//!
|
||||||
//! Initialization is compatible with the initialization used by minidump
|
//! Initialization is compatible with the initialization used by minidump
|
||||||
@ -40,25 +59,8 @@ namespace test {
|
|||||||
//! \{
|
//! \{
|
||||||
void InitializeCPUContextX86(CPUContext* context, uint32_t seed);
|
void InitializeCPUContextX86(CPUContext* context, uint32_t seed);
|
||||||
void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed);
|
void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed);
|
||||||
//! \}
|
void InitializeCPUContextARM(CPUContext* context, uint32_t seed);
|
||||||
|
void InitializeCPUContextARM64(CPUContext* context, uint32_t seed);
|
||||||
//! \brief Initializes an `fxsave` context substructure for testing.
|
|
||||||
//!
|
|
||||||
//! \param[out] fxsave The structure to initialize.
|
|
||||||
//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of
|
|
||||||
//! the same type with identical seed values should produce identical
|
|
||||||
//! structures. Initialization with a different seed value should produce
|
|
||||||
//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed
|
|
||||||
//! out entirely. If \a seed is nonzero, \a fxsave will be populated
|
|
||||||
//! entirely with nonzero values. \a seed will be updated by this function
|
|
||||||
//! to allow the caller to perform subsequent initialization of the context
|
|
||||||
//! structure containing \a fxsave.
|
|
||||||
//!
|
|
||||||
//! \{
|
|
||||||
void InitializeCPUContextX86Fxsave(
|
|
||||||
CPUContextX86::Fxsave* fxsave, uint32_t* seed);
|
|
||||||
void InitializeCPUContextX86_64Fxsave(
|
|
||||||
CPUContextX86_64::Fxsave* fxsave, uint32_t* seed);
|
|
||||||
//! \}
|
//! \}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user