mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +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
|
||||
|
||||
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
|
||||
|
@ -73,6 +73,19 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) {
|
||||
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: {
|
||||
LOG(ERROR) << "unknown context architecture "
|
||||
<< context_snapshot->architecture;
|
||||
@ -239,4 +252,90 @@ size_t MinidumpContextAMD64Writer::ContextSize() const {
|
||||
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
|
||||
|
@ -155,6 +155,86 @@ class MinidumpContextAMD64Writer final : public MinidumpContextWriter {
|
||||
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
|
||||
|
||||
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
|
||||
|
@ -28,6 +28,20 @@ namespace crashpad {
|
||||
namespace test {
|
||||
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) {
|
||||
StringFile string_file;
|
||||
|
||||
@ -36,16 +50,8 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
|
||||
// context.
|
||||
SCOPED_TRACE("zero");
|
||||
|
||||
MinidumpContextX86Writer context_writer;
|
||||
|
||||
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);
|
||||
EmptyContextTest<MinidumpContextX86Writer, MinidumpContextX86>(
|
||||
ExpectMinidumpContextX86);
|
||||
}
|
||||
|
||||
{
|
||||
@ -85,16 +91,8 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
|
||||
// context.
|
||||
SCOPED_TRACE("zero");
|
||||
|
||||
MinidumpContextAMD64Writer context_writer;
|
||||
|
||||
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);
|
||||
EmptyContextTest<MinidumpContextAMD64Writer, MinidumpContextAMD64>(
|
||||
ExpectMinidumpContextAMD64);
|
||||
}
|
||||
|
||||
{
|
||||
@ -117,48 +115,72 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpContextWriter, CreateFromSnapshot_X86) {
|
||||
constexpr uint32_t kSeed = 32;
|
||||
|
||||
CPUContextX86 context_snapshot_x86;
|
||||
CPUContext context_snapshot;
|
||||
context_snapshot.x86 = &context_snapshot_x86;
|
||||
InitializeCPUContextX86(&context_snapshot, kSeed);
|
||||
|
||||
template <typename Writer, typename Context>
|
||||
void FromSnapshotTest(const CPUContext& snapshot_context,
|
||||
void (*expect_context)(uint32_t, const Context*, bool),
|
||||
uint32_t seed) {
|
||||
std::unique_ptr<MinidumpContextWriter> context_writer =
|
||||
MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
|
||||
MinidumpContextWriter::CreateFromSnapshot(&snapshot_context);
|
||||
ASSERT_TRUE(context_writer);
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
|
||||
|
||||
const MinidumpContextX86* observed =
|
||||
MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
|
||||
const Context* observed =
|
||||
MinidumpWritableAtRVA<Context>(string_file.string(), 0);
|
||||
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;
|
||||
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;
|
||||
CPUContext context_snapshot;
|
||||
context_snapshot.x86_64 = &context_snapshot_x86_64;
|
||||
InitializeCPUContextX86_64(&context_snapshot, kSeed);
|
||||
TEST(MinidumpContextWriter, ARM_Zeros) {
|
||||
EmptyContextTest<MinidumpContextARMWriter, MinidumpContextARM>(
|
||||
ExpectMinidumpContextARM);
|
||||
}
|
||||
|
||||
std::unique_ptr<MinidumpContextWriter> context_writer =
|
||||
MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
|
||||
ASSERT_TRUE(context_writer);
|
||||
TEST(MinidumpContextWRiter, ARM64_Zeros) {
|
||||
EmptyContextTest<MinidumpContextARM64Writer, MinidumpContextARM64>(
|
||||
ExpectMinidumpContextARM64);
|
||||
}
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
|
||||
TEST(MinidumpContextWriter, ARM_FromSnapshot) {
|
||||
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 =
|
||||
MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
|
||||
ASSERT_TRUE(observed);
|
||||
|
||||
ExpectMinidumpContextAMD64(kSeed, observed, true);
|
||||
TEST(MinidumpContextWriter, ARM64_FromSnapshot) {
|
||||
constexpr uint32_t kSeed = 64;
|
||||
CPUContextARM64 context_arm64;
|
||||
CPUContext context;
|
||||
context.arm64 = &context_arm64;
|
||||
InitializeCPUContextARM64(&context, kSeed);
|
||||
FromSnapshotTest<MinidumpContextARM64Writer, MinidumpContextARM64>(
|
||||
context, ExpectMinidumpContextARM64, kSeed);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -140,6 +140,61 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||
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 {
|
||||
|
||||
// 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 crashpad
|
||||
|
@ -41,6 +41,9 @@ namespace test {
|
||||
void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed);
|
||||
void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||
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
|
||||
@ -67,6 +70,12 @@ void ExpectMinidumpContextX86(
|
||||
uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot);
|
||||
void ExpectMinidumpContextAMD64(
|
||||
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
|
||||
|
@ -22,6 +22,68 @@
|
||||
namespace crashpad {
|
||||
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) {
|
||||
context->architecture = kCPUArchitectureX86;
|
||||
|
||||
@ -101,68 +163,61 @@ void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) {
|
||||
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
|
||||
// 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++);
|
||||
if (seed == 0) {
|
||||
memset(arm, 0, sizeof(*arm));
|
||||
return;
|
||||
}
|
||||
|
||||
*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,
|
||||
uint32_t* seed) {
|
||||
return InitializeCPUContextFxsave(fxsave, seed);
|
||||
}
|
||||
if (seed == 0) {
|
||||
memset(arm64, 0, sizeof(*arm64));
|
||||
return;
|
||||
}
|
||||
|
||||
void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave,
|
||||
uint32_t* seed) {
|
||||
return InitializeCPUContextFxsave(fxsave, seed);
|
||||
uint32_t value = 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
|
||||
|
@ -22,6 +22,25 @@
|
||||
namespace crashpad {
|
||||
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.
|
||||
//!
|
||||
//! Initialization is compatible with the initialization used by minidump
|
||||
@ -40,25 +59,8 @@ namespace test {
|
||||
//! \{
|
||||
void InitializeCPUContextX86(CPUContext* context, uint32_t seed);
|
||||
void InitializeCPUContextX86_64(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);
|
||||
void InitializeCPUContextARM(CPUContext* context, uint32_t seed);
|
||||
void InitializeCPUContextARM64(CPUContext* context, uint32_t seed);
|
||||
//! \}
|
||||
|
||||
} // namespace test
|
||||
|
Loading…
x
Reference in New Issue
Block a user