mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 01:08:01 +08:00
Add MinidumpContextWriter::CreateFromSnapshot(), everything downstream,
and its test. Minidump context structures now interoperate more easily with snapshot CPUContext structures, while maintaining identical layout to before. This is facilitated by reusing the Fxsave types for the substructures which were completely identical, and by using compatible logic to initialize the minidump and snapshot structures for testing. TEST=minidump_test, snapshot_test R=rsesek@chromium.org Review URL: https://codereview.chromium.org/686353004
This commit is contained in:
parent
f3bdc972f9
commit
52c2f6edfc
@ -17,6 +17,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "util/numeric/int128.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -130,6 +131,11 @@ enum MinidumpContextX86Flags : uint32_t {
|
||||
//! This is analogous to the `CONTEXT` structure on Windows when targeting
|
||||
//! 32-bit x86. This structure is used instead of `CONTEXT` to make it available
|
||||
//! when targeting other architectures.
|
||||
//!
|
||||
//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and
|
||||
//! normally alias `dr6` and `dr7`, respectively. See Intel Software
|
||||
//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052),
|
||||
//! 17.2.2 “Debug Registers DR4 and DR5”.
|
||||
struct MinidumpContextX86 {
|
||||
//! \brief A bitfield composed of values of #MinidumpContextFlags and
|
||||
//! #MinidumpContextX86Flags.
|
||||
@ -176,7 +182,9 @@ struct MinidumpContextX86 {
|
||||
uint32_t esp;
|
||||
uint32_t ss;
|
||||
|
||||
uint8_t extended_registers[512];
|
||||
// CPUContextX86::Fxsave has identical layout to what the x86 CONTEXT
|
||||
// structure places here.
|
||||
CPUContextX86::Fxsave fxsave;
|
||||
};
|
||||
|
||||
//! \brief x86_64-specifc flags for MinidumpContextAMD64::context_flags.
|
||||
@ -240,6 +248,11 @@ enum MinidumpContextAMD64Flags : uint32_t {
|
||||
//! This is analogous to the `CONTEXT` structure on Windows when targeting
|
||||
//! x86_64. This structure is used instead of `CONTEXT` to make it available
|
||||
//! when targeting other architectures.
|
||||
//!
|
||||
//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and
|
||||
//! normally alias `dr6` and `dr7`, respectively. See Intel Software
|
||||
//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052),
|
||||
//! 17.2.2 “Debug Registers DR4 and DR5”.
|
||||
struct __attribute__((aligned(16))) MinidumpContextAMD64 {
|
||||
//! \brief Register parameter home address.
|
||||
//!
|
||||
@ -301,46 +314,9 @@ struct __attribute__((aligned(16))) MinidumpContextAMD64 {
|
||||
|
||||
uint64_t rip;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint16_t control_word;
|
||||
uint16_t status_word;
|
||||
uint8_t tag_word;
|
||||
uint8_t reserved_1;
|
||||
uint16_t error_opcode;
|
||||
uint32_t error_offset;
|
||||
uint16_t error_selector;
|
||||
uint16_t reserved_2;
|
||||
uint32_t data_offset;
|
||||
uint16_t data_selector;
|
||||
uint16_t reserved_3;
|
||||
uint32_t mx_csr;
|
||||
uint32_t mx_csr_mask;
|
||||
uint128_struct float_registers[8];
|
||||
uint128_struct xmm_registers[16];
|
||||
uint8_t reserved_4[96];
|
||||
} float_save;
|
||||
struct {
|
||||
uint128_struct header[2];
|
||||
uint128_struct legacy[8];
|
||||
uint128_struct xmm0;
|
||||
uint128_struct xmm1;
|
||||
uint128_struct xmm2;
|
||||
uint128_struct xmm3;
|
||||
uint128_struct xmm4;
|
||||
uint128_struct xmm5;
|
||||
uint128_struct xmm6;
|
||||
uint128_struct xmm7;
|
||||
uint128_struct xmm8;
|
||||
uint128_struct xmm9;
|
||||
uint128_struct xmm10;
|
||||
uint128_struct xmm11;
|
||||
uint128_struct xmm12;
|
||||
uint128_struct xmm13;
|
||||
uint128_struct xmm14;
|
||||
uint128_struct xmm15;
|
||||
};
|
||||
};
|
||||
// CPUContextX86_64::Fxsave has identical layout to what the x86_64 CONTEXT
|
||||
// structure places here.
|
||||
CPUContextX86_64::Fxsave fxsave;
|
||||
|
||||
uint128_struct vector_register[26];
|
||||
uint64_t vector_control;
|
||||
|
@ -14,7 +14,10 @@
|
||||
|
||||
#include "minidump/minidump_context_writer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "util/file/file_writer.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -22,6 +25,37 @@ namespace crashpad {
|
||||
MinidumpContextWriter::~MinidumpContextWriter() {
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_ptr<MinidumpContextWriter> MinidumpContextWriter::CreateFromSnapshot(
|
||||
const CPUContext* context_snapshot) {
|
||||
scoped_ptr<MinidumpContextWriter> context;
|
||||
|
||||
switch (context_snapshot->architecture) {
|
||||
case kCPUArchitectureX86: {
|
||||
MinidumpContextX86Writer* context_x86 = new MinidumpContextX86Writer();
|
||||
context.reset(context_x86);
|
||||
context_x86->InitializeFromSnapshot(context_snapshot->x86);
|
||||
break;
|
||||
}
|
||||
|
||||
case kCPUArchitectureX86_64: {
|
||||
MinidumpContextAMD64Writer* context_amd64 =
|
||||
new MinidumpContextAMD64Writer();
|
||||
context.reset(context_amd64);
|
||||
context_amd64->InitializeFromSnapshot(context_snapshot->x86_64);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
LOG(ERROR) << "unknown context architecture "
|
||||
<< context_snapshot->architecture;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
size_t MinidumpContextWriter::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
@ -36,6 +70,65 @@ MinidumpContextX86Writer::MinidumpContextX86Writer()
|
||||
MinidumpContextX86Writer::~MinidumpContextX86Writer() {
|
||||
}
|
||||
|
||||
|
||||
void MinidumpContextX86Writer::InitializeFromSnapshot(
|
||||
const CPUContextX86* context_snapshot) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
DCHECK_EQ(context_.context_flags, kMinidumpContextX86);
|
||||
|
||||
context_.context_flags = kMinidumpContextX86All;
|
||||
|
||||
context_.dr0 = context_snapshot->dr0;
|
||||
context_.dr1 = context_snapshot->dr1;
|
||||
context_.dr2 = context_snapshot->dr2;
|
||||
context_.dr3 = context_snapshot->dr3;
|
||||
context_.dr6 = context_snapshot->dr6;
|
||||
context_.dr7 = context_snapshot->dr7;
|
||||
|
||||
// The contents of context_.float_save effectively alias everything in
|
||||
// context_.fxsave that’s related to x87 FPU state. context_.float_save
|
||||
// doesn’t carry state specific to SSE (or later), such as mxcsr and the xmm
|
||||
// registers.
|
||||
context_.float_save.control_word = context_snapshot->fxsave.fcw;
|
||||
context_.float_save.status_word = context_snapshot->fxsave.fsw;
|
||||
context_.float_save.tag_word =
|
||||
CPUContextX86::FxsaveToFsaveTagWord(context_snapshot->fxsave.fsw,
|
||||
context_snapshot->fxsave.ftw,
|
||||
context_snapshot->fxsave.st_mm);
|
||||
context_.float_save.error_offset = context_snapshot->fxsave.fpu_ip;
|
||||
context_.float_save.error_selector = context_snapshot->fxsave.fpu_cs;
|
||||
context_.float_save.data_offset = context_snapshot->fxsave.fpu_dp;
|
||||
context_.float_save.data_selector = context_snapshot->fxsave.fpu_ds;
|
||||
|
||||
for (size_t index = 0, offset = 0;
|
||||
index < arraysize(context_snapshot->fxsave.st_mm);
|
||||
offset += sizeof(context_snapshot->fxsave.st_mm[index].st), ++index) {
|
||||
memcpy(&context_.float_save.register_area[offset],
|
||||
&context_snapshot->fxsave.st_mm[index].st,
|
||||
sizeof(context_snapshot->fxsave.st_mm[index].st));
|
||||
}
|
||||
|
||||
context_.gs = context_snapshot->gs;
|
||||
context_.fs = context_snapshot->fs;
|
||||
context_.es = context_snapshot->es;
|
||||
context_.ds = context_snapshot->ds;
|
||||
context_.edi = context_snapshot->edi;
|
||||
context_.esi = context_snapshot->esi;
|
||||
context_.ebx = context_snapshot->ebx;
|
||||
context_.edx = context_snapshot->edx;
|
||||
context_.ecx = context_snapshot->ecx;
|
||||
context_.eax = context_snapshot->eax;
|
||||
context_.ebp = context_snapshot->ebp;
|
||||
context_.eip = context_snapshot->eip;
|
||||
context_.cs = context_snapshot->cs;
|
||||
context_.eflags = context_snapshot->eflags;
|
||||
context_.esp = context_snapshot->esp;
|
||||
context_.ss = context_snapshot->ss;
|
||||
|
||||
// This is effectively a memcpy() of a big structure.
|
||||
context_.fxsave = context_snapshot->fxsave;
|
||||
}
|
||||
|
||||
bool MinidumpContextX86Writer::WriteObject(FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
@ -56,6 +149,46 @@ MinidumpContextAMD64Writer::MinidumpContextAMD64Writer()
|
||||
MinidumpContextAMD64Writer::~MinidumpContextAMD64Writer() {
|
||||
}
|
||||
|
||||
void MinidumpContextAMD64Writer::InitializeFromSnapshot(
|
||||
const CPUContextX86_64* context_snapshot) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
DCHECK_EQ(context_.context_flags, kMinidumpContextAMD64);
|
||||
|
||||
context_.context_flags = kMinidumpContextAMD64All;
|
||||
|
||||
context_.mx_csr = context_snapshot->fxsave.mxcsr;
|
||||
context_.cs = context_snapshot->cs;
|
||||
context_.fs = context_snapshot->fs;
|
||||
context_.gs = context_snapshot->gs;
|
||||
context_.eflags = context_snapshot->rflags;
|
||||
context_.dr0 = context_snapshot->dr0;
|
||||
context_.dr1 = context_snapshot->dr1;
|
||||
context_.dr2 = context_snapshot->dr2;
|
||||
context_.dr3 = context_snapshot->dr3;
|
||||
context_.dr6 = context_snapshot->dr6;
|
||||
context_.dr7 = context_snapshot->dr7;
|
||||
context_.rax = context_snapshot->rax;
|
||||
context_.rcx = context_snapshot->rcx;
|
||||
context_.rdx = context_snapshot->rdx;
|
||||
context_.rbx = context_snapshot->rbx;
|
||||
context_.rsp = context_snapshot->rsp;
|
||||
context_.rbp = context_snapshot->rbp;
|
||||
context_.rsi = context_snapshot->rsi;
|
||||
context_.rdi = context_snapshot->rdi;
|
||||
context_.r8 = context_snapshot->r8;
|
||||
context_.r9 = context_snapshot->r9;
|
||||
context_.r10 = context_snapshot->r10;
|
||||
context_.r11 = context_snapshot->r11;
|
||||
context_.r12 = context_snapshot->r12;
|
||||
context_.r13 = context_snapshot->r13;
|
||||
context_.r14 = context_snapshot->r14;
|
||||
context_.r15 = context_snapshot->r15;
|
||||
context_.rip = context_snapshot->rip;
|
||||
|
||||
// This is effectively a memcpy() of a big structure.
|
||||
context_.fxsave = context_snapshot->fxsave;
|
||||
}
|
||||
|
||||
size_t MinidumpContextAMD64Writer::Alignment() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
|
@ -18,17 +18,34 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "minidump/minidump_context.h"
|
||||
#include "minidump/minidump_writable.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
struct CPUContext;
|
||||
struct CPUContextX86;
|
||||
struct CPUContextX86_64;
|
||||
|
||||
//! \brief The base class for writers of CPU context structures in minidump
|
||||
//! files.
|
||||
class MinidumpContextWriter : public internal::MinidumpWritable {
|
||||
public:
|
||||
~MinidumpContextWriter() override;
|
||||
|
||||
//! \brief Creates a MinidumpContextWriter based on \a context_snapshot.
|
||||
//!
|
||||
//! \param[in] context_snapshot The context snapshot to use as source data.
|
||||
//!
|
||||
//! \return A MinidumpContextWriter subclass, such as MinidumpContextWriterX86
|
||||
//! or MinidumpContextWriterAMD64, appropriate to the CPU type of \a
|
||||
//! context_snapshot. The returned object is initialized using the source
|
||||
//! data in \a context_snapshot. If \a context_snapshot is an unknown CPU
|
||||
//! type’s context, logs a message and returns `nullptr`.
|
||||
static scoped_ptr<MinidumpContextWriter> CreateFromSnapshot(
|
||||
const CPUContext* context_snapshot);
|
||||
|
||||
protected:
|
||||
MinidumpContextWriter() : MinidumpWritable() {}
|
||||
|
||||
@ -52,6 +69,15 @@ class MinidumpContextX86Writer final : public MinidumpContextWriter {
|
||||
MinidumpContextX86Writer();
|
||||
~MinidumpContextX86Writer() override;
|
||||
|
||||
//! \brief Initializes the MinidumpContextX86 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 CPUContextX86* context_snapshot);
|
||||
|
||||
//! \brief Returns a pointer to the context structure that this object will
|
||||
//! write.
|
||||
//!
|
||||
@ -83,6 +109,15 @@ class MinidumpContextAMD64Writer final : public MinidumpContextWriter {
|
||||
MinidumpContextAMD64Writer();
|
||||
~MinidumpContextAMD64Writer() override;
|
||||
|
||||
//! \brief Initializes the MinidumpContextAMD64 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 CPUContextX86_64* context_snapshot);
|
||||
|
||||
//! \brief Returns a pointer to the context structure that this object will
|
||||
//! write.
|
||||
//!
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "minidump/minidump_context.h"
|
||||
#include "minidump/test/minidump_context_test_util.h"
|
||||
#include "minidump/test/minidump_writable_test_util.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "snapshot/test/test_cpu_context.h"
|
||||
#include "util/file/string_file_writer.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -41,7 +43,9 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
|
||||
|
||||
const MinidumpContextX86* observed =
|
||||
MinidumpWritableAtRVA<MinidumpContextX86>(file_writer.string(), 0);
|
||||
ExpectMinidumpContextX86(0, observed);
|
||||
ASSERT_TRUE(observed);
|
||||
|
||||
ExpectMinidumpContextX86(0, observed, false);
|
||||
}
|
||||
|
||||
{
|
||||
@ -58,7 +62,9 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
|
||||
|
||||
const MinidumpContextX86* observed =
|
||||
MinidumpWritableAtRVA<MinidumpContextX86>(file_writer.string(), 0);
|
||||
ExpectMinidumpContextX86(kSeed, observed);
|
||||
ASSERT_TRUE(observed);
|
||||
|
||||
ExpectMinidumpContextX86(kSeed, observed, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +83,9 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
|
||||
|
||||
const MinidumpContextAMD64* observed =
|
||||
MinidumpWritableAtRVA<MinidumpContextAMD64>(file_writer.string(), 0);
|
||||
ExpectMinidumpContextAMD64(0, observed);
|
||||
ASSERT_TRUE(observed);
|
||||
|
||||
ExpectMinidumpContextAMD64(0, observed, false);
|
||||
}
|
||||
|
||||
{
|
||||
@ -94,10 +102,56 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
|
||||
|
||||
const MinidumpContextAMD64* observed =
|
||||
MinidumpWritableAtRVA<MinidumpContextAMD64>(file_writer.string(), 0);
|
||||
ExpectMinidumpContextAMD64(kSeed, observed);
|
||||
ASSERT_TRUE(observed);
|
||||
|
||||
ExpectMinidumpContextAMD64(kSeed, observed, false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpContextWriter, CreateFromSnapshot_X86) {
|
||||
const uint32_t kSeed = 32;
|
||||
|
||||
CPUContextX86 context_snapshot_x86;
|
||||
CPUContext context_snapshot;
|
||||
context_snapshot.x86 = &context_snapshot_x86;
|
||||
InitializeCPUContextX86(&context_snapshot, kSeed);
|
||||
|
||||
scoped_ptr<MinidumpContextWriter> context_writer =
|
||||
MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
|
||||
ASSERT_TRUE(context_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_TRUE(context_writer->WriteEverything(&file_writer));
|
||||
|
||||
const MinidumpContextX86* observed =
|
||||
MinidumpWritableAtRVA<MinidumpContextX86>(file_writer.string(), 0);
|
||||
ASSERT_TRUE(observed);
|
||||
|
||||
ExpectMinidumpContextX86(kSeed, observed, true);
|
||||
}
|
||||
|
||||
TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) {
|
||||
const uint32_t kSeed = 64;
|
||||
|
||||
CPUContextX86_64 context_snapshot_x86_64;
|
||||
CPUContext context_snapshot;
|
||||
context_snapshot.x86_64 = &context_snapshot_x86_64;
|
||||
InitializeCPUContextX86_64(&context_snapshot, kSeed);
|
||||
|
||||
scoped_ptr<MinidumpContextWriter> context_writer =
|
||||
MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
|
||||
ASSERT_TRUE(context_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_TRUE(context_writer->WriteEverything(&file_writer));
|
||||
|
||||
const MinidumpContextAMD64* observed =
|
||||
MinidumpWritableAtRVA<MinidumpContextAMD64>(file_writer.string(), 0);
|
||||
ASSERT_TRUE(observed);
|
||||
|
||||
ExpectMinidumpContextAMD64(kSeed, observed, true);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
@ -119,7 +119,8 @@ TEST(MinidumpExceptionWriter, Minimal) {
|
||||
file_writer.string(),
|
||||
&observed_context));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed, observed_context));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectMinidumpContextX86(kSeed, observed_context, false));
|
||||
}
|
||||
|
||||
TEST(MinidumpExceptionWriter, Standard) {
|
||||
@ -189,7 +190,8 @@ TEST(MinidumpExceptionWriter, Standard) {
|
||||
file_writer.string(),
|
||||
&observed_context));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed, observed_context));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectMinidumpContextX86(kSeed, observed_context, false));
|
||||
}
|
||||
|
||||
TEST(MinidumpExceptionWriterDeathTest, NoContext) {
|
||||
|
@ -184,7 +184,8 @@ TEST(MinidumpThreadWriter, OneThread_x86_NoStack) {
|
||||
nullptr,
|
||||
reinterpret_cast<const void**>(&observed_context)));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed, observed_context));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectMinidumpContextX86(kSeed, observed_context, false));
|
||||
}
|
||||
|
||||
TEST(MinidumpThreadWriter, OneThread_AMD64_Stack) {
|
||||
@ -258,7 +259,8 @@ TEST(MinidumpThreadWriter, OneThread_AMD64_Stack) {
|
||||
file_writer.string(),
|
||||
kMemoryValue,
|
||||
true));
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextAMD64(kSeed, observed_context));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectMinidumpContextAMD64(kSeed, observed_context, false));
|
||||
}
|
||||
|
||||
TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) {
|
||||
@ -398,7 +400,8 @@ TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) {
|
||||
file_writer.string(),
|
||||
kMemoryValue0,
|
||||
false));
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed0, observed_context));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectMinidumpContextX86(kSeed0, observed_context, false));
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
|
||||
observed_stack, &memory_list->MemoryRanges[0]));
|
||||
}
|
||||
@ -431,7 +434,8 @@ TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) {
|
||||
file_writer.string(),
|
||||
kMemoryValue1,
|
||||
false));
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed1, observed_context));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectMinidumpContextX86(kSeed1, observed_context, false));
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
|
||||
observed_stack, &memory_list->MemoryRanges[1]));
|
||||
}
|
||||
@ -464,7 +468,8 @@ TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) {
|
||||
file_writer.string(),
|
||||
kMemoryValue2,
|
||||
true));
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed2, observed_context));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectMinidumpContextX86(kSeed2, observed_context, false));
|
||||
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
|
||||
observed_stack, &memory_list->MemoryRanges[2]));
|
||||
}
|
||||
|
@ -15,7 +15,10 @@
|
||||
#include "minidump/test/minidump_context_test_util.h"
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "snapshot/test/test_cpu_context.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
@ -31,45 +34,59 @@ void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed) {
|
||||
|
||||
uint32_t value = seed;
|
||||
|
||||
context->eax = value++;
|
||||
context->ebx = value++;
|
||||
context->ecx = value++;
|
||||
context->edx = value++;
|
||||
context->edi = value++;
|
||||
context->esi = value++;
|
||||
context->ebp = value++;
|
||||
context->esp = value++;
|
||||
context->eip = value++;
|
||||
context->eflags = value++;
|
||||
context->cs = value++ & 0xffff;
|
||||
context->ds = value++ & 0xffff;
|
||||
context->es = value++ & 0xffff;
|
||||
context->fs = value++ & 0xffff;
|
||||
context->gs = value++ & 0xffff;
|
||||
context->ss = value++ & 0xffff;
|
||||
|
||||
InitializeCPUContextX86Fxsave(&context->fxsave, &value);
|
||||
|
||||
context->dr0 = value++;
|
||||
context->dr1 = value++;
|
||||
context->dr2 = value++;
|
||||
context->dr3 = value++;
|
||||
value += 2; // Minidumps don’t carry dr4 or dr5.
|
||||
context->dr6 = value++;
|
||||
context->dr7 = value++;
|
||||
context->float_save.control_word = value++;
|
||||
context->float_save.status_word = value++;
|
||||
context->float_save.tag_word = value++;
|
||||
context->float_save.error_offset = value++;
|
||||
context->float_save.error_selector = value++;
|
||||
context->float_save.data_offset = value++;
|
||||
context->float_save.data_selector = value++;
|
||||
for (size_t index = 0;
|
||||
index < arraysize(context->float_save.register_area);
|
||||
++index) {
|
||||
context->float_save.register_area[index] = value++;
|
||||
|
||||
// Copy the values that are aliased between the fxsave area
|
||||
// (context->extended_registers) and the floating-point save area
|
||||
// (context->float_save).
|
||||
context->float_save.control_word = context->fxsave.fcw;
|
||||
context->float_save.status_word = context->fxsave.fsw;
|
||||
context->float_save.tag_word = CPUContextX86::FxsaveToFsaveTagWord(
|
||||
context->fxsave.fsw, context->fxsave.ftw, context->fxsave.st_mm);
|
||||
context->float_save.error_offset = context->fxsave.fpu_ip;
|
||||
context->float_save.error_selector = context->fxsave.fpu_cs;
|
||||
context->float_save.data_offset = context->fxsave.fpu_dp;
|
||||
context->float_save.data_selector = context->fxsave.fpu_ds;
|
||||
for (size_t st_mm_index = 0;
|
||||
st_mm_index < arraysize(context->fxsave.st_mm);
|
||||
++st_mm_index) {
|
||||
for (size_t byte = 0;
|
||||
byte < arraysize(context->fxsave.st_mm[st_mm_index].st);
|
||||
++byte) {
|
||||
size_t st_index =
|
||||
st_mm_index * arraysize(context->fxsave.st_mm[st_mm_index].st) + byte;
|
||||
context->float_save.register_area[st_index] =
|
||||
context->fxsave.st_mm[st_mm_index].st[byte];
|
||||
}
|
||||
}
|
||||
|
||||
// Set this field last, because it has no analogue in CPUContextX86.
|
||||
context->float_save.spare_0 = value++;
|
||||
context->gs = value++;
|
||||
context->fs = value++;
|
||||
context->es = value++;
|
||||
context->ds = value++;
|
||||
context->edi = value++;
|
||||
context->esi = value++;
|
||||
context->ebx = value++;
|
||||
context->edx = value++;
|
||||
context->ecx = value++;
|
||||
context->eax = value++;
|
||||
context->ebp = value++;
|
||||
context->eip = value++;
|
||||
context->cs = value++;
|
||||
context->eflags = value++;
|
||||
context->esp = value++;
|
||||
context->ss = value++;
|
||||
for (size_t index = 0; index < arraysize(context->extended_registers);
|
||||
++index) {
|
||||
context->extended_registers[index] = value++;
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||
@ -84,34 +101,14 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||
|
||||
uint32_t value = seed;
|
||||
|
||||
context->p1_home = value++;
|
||||
context->p2_home = value++;
|
||||
context->p3_home = value++;
|
||||
context->p4_home = value++;
|
||||
context->p5_home = value++;
|
||||
context->p6_home = value++;
|
||||
context->mx_csr = value++;
|
||||
context->cs = value++;
|
||||
context->ds = value++;
|
||||
context->es = value++;
|
||||
context->fs = value++;
|
||||
context->gs = value++;
|
||||
context->ss = value++;
|
||||
context->eflags = value++;
|
||||
context->dr0 = value++;
|
||||
context->dr1 = value++;
|
||||
context->dr2 = value++;
|
||||
context->dr3 = value++;
|
||||
context->dr6 = value++;
|
||||
context->dr7 = value++;
|
||||
context->rax = value++;
|
||||
context->rbx = value++;
|
||||
context->rcx = value++;
|
||||
context->rdx = value++;
|
||||
context->rbx = value++;
|
||||
context->rsp = value++;
|
||||
context->rbp = value++;
|
||||
context->rsi = value++;
|
||||
context->rdi = value++;
|
||||
context->rsi = value++;
|
||||
context->rbp = value++;
|
||||
context->rsp = value++;
|
||||
context->r8 = value++;
|
||||
context->r9 = value++;
|
||||
context->r10 = value++;
|
||||
@ -121,36 +118,34 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||
context->r14 = value++;
|
||||
context->r15 = value++;
|
||||
context->rip = value++;
|
||||
context->float_save.control_word = value++;
|
||||
context->float_save.status_word = value++;
|
||||
context->float_save.tag_word = value++;
|
||||
context->float_save.reserved_1 = value++;
|
||||
context->float_save.error_opcode = value++;
|
||||
context->float_save.error_offset = value++;
|
||||
context->float_save.error_selector = value++;
|
||||
context->float_save.reserved_2 = value++;
|
||||
context->float_save.data_offset = value++;
|
||||
context->float_save.data_selector = value++;
|
||||
context->float_save.reserved_3 = value++;
|
||||
context->float_save.mx_csr = value++;
|
||||
context->float_save.mx_csr_mask = value++;
|
||||
for (size_t index = 0;
|
||||
index < arraysize(context->float_save.float_registers);
|
||||
++index) {
|
||||
context->float_save.float_registers[index].lo = value++;
|
||||
context->float_save.float_registers[index].hi = value++;
|
||||
}
|
||||
for (size_t index = 0;
|
||||
index < arraysize(context->float_save.xmm_registers);
|
||||
++index) {
|
||||
context->float_save.xmm_registers[index].lo = value++;
|
||||
context->float_save.xmm_registers[index].hi = value++;
|
||||
}
|
||||
for (size_t index = 0;
|
||||
index < arraysize(context->float_save.reserved_4);
|
||||
++index) {
|
||||
context->float_save.reserved_4[index] = value++;
|
||||
}
|
||||
context->eflags = value++;
|
||||
context->cs = value++;
|
||||
context->fs = value++;
|
||||
context->gs = value++;
|
||||
|
||||
InitializeCPUContextX86_64Fxsave(&context->fxsave, &value);
|
||||
|
||||
// mxcsr appears twice, and the two values should be aliased.
|
||||
context->mx_csr = context->fxsave.mxcsr;
|
||||
|
||||
context->dr0 = value++;
|
||||
context->dr1 = value++;
|
||||
context->dr2 = value++;
|
||||
context->dr3 = value++;
|
||||
value += 2; // Minidumps don’t carry dr4 or dr5.
|
||||
context->dr6 = value++;
|
||||
context->dr7 = value++;
|
||||
|
||||
// Set these fields last, because they have no analogues in CPUContextX86_64.
|
||||
context->p1_home = value++;
|
||||
context->p2_home = value++;
|
||||
context->p3_home = value++;
|
||||
context->p4_home = value++;
|
||||
context->p5_home = value++;
|
||||
context->p6_home = value++;
|
||||
context->ds = value++;
|
||||
context->es = value++;
|
||||
context->ss = value++;
|
||||
for (size_t index = 0; index < arraysize(context->vector_register); ++index) {
|
||||
context->vector_register[index].lo = value++;
|
||||
context->vector_register[index].hi = value++;
|
||||
@ -163,8 +158,69 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||
context->last_exception_from_rip = value++;
|
||||
}
|
||||
|
||||
void ExpectMinidumpContextX86(uint32_t expect_seed,
|
||||
const MinidumpContextX86* observed) {
|
||||
namespace {
|
||||
|
||||
// Using gtest assertions, compares |expected| to |observed|. 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 ExpectMinidumpContextFxsave(const FxsaveType* expected,
|
||||
const FxsaveType* observed) {
|
||||
EXPECT_EQ(expected->fcw, observed->fcw);
|
||||
EXPECT_EQ(expected->fsw, observed->fsw);
|
||||
EXPECT_EQ(expected->ftw, observed->ftw);
|
||||
EXPECT_EQ(expected->reserved_1, observed->reserved_1);
|
||||
EXPECT_EQ(expected->fop, observed->fop);
|
||||
EXPECT_EQ(expected->fpu_ip, observed->fpu_ip);
|
||||
EXPECT_EQ(expected->fpu_cs, observed->fpu_cs);
|
||||
EXPECT_EQ(expected->reserved_2, observed->reserved_2);
|
||||
EXPECT_EQ(expected->fpu_dp, observed->fpu_dp);
|
||||
EXPECT_EQ(expected->fpu_ds, observed->fpu_ds);
|
||||
EXPECT_EQ(expected->reserved_3, observed->reserved_3);
|
||||
EXPECT_EQ(expected->mxcsr, observed->mxcsr);
|
||||
EXPECT_EQ(expected->mxcsr_mask, observed->mxcsr_mask);
|
||||
for (size_t st_mm_index = 0;
|
||||
st_mm_index < arraysize(expected->st_mm);
|
||||
++st_mm_index) {
|
||||
SCOPED_TRACE(base::StringPrintf("st_mm_index %zu", st_mm_index));
|
||||
for (size_t byte = 0;
|
||||
byte < arraysize(expected->st_mm[st_mm_index].st);
|
||||
++byte) {
|
||||
EXPECT_EQ(expected->st_mm[st_mm_index].st[byte],
|
||||
observed->st_mm[st_mm_index].st[byte]) << "byte " << byte;
|
||||
}
|
||||
for (size_t byte = 0;
|
||||
byte < arraysize(expected->st_mm[st_mm_index].st_reserved);
|
||||
++byte) {
|
||||
EXPECT_EQ(expected->st_mm[st_mm_index].st_reserved[byte],
|
||||
observed->st_mm[st_mm_index].st_reserved[byte])
|
||||
<< "byte " << byte;
|
||||
}
|
||||
}
|
||||
for (size_t xmm_index = 0;
|
||||
xmm_index < arraysize(expected->xmm);
|
||||
++xmm_index) {
|
||||
SCOPED_TRACE(base::StringPrintf("xmm_index %zu", xmm_index));
|
||||
for (size_t byte = 0; byte < arraysize(expected->xmm[xmm_index]); ++byte) {
|
||||
EXPECT_EQ(expected->xmm[xmm_index][byte], observed->xmm[xmm_index][byte])
|
||||
<< "byte " << byte;
|
||||
}
|
||||
}
|
||||
for (size_t byte = 0; byte < arraysize(expected->reserved_4); ++byte) {
|
||||
EXPECT_EQ(expected->reserved_4[byte], observed->reserved_4[byte])
|
||||
<< "byte " << byte;
|
||||
}
|
||||
for (size_t byte = 0; byte < arraysize(expected->available); ++byte) {
|
||||
EXPECT_EQ(expected->available[byte], observed->available[byte])
|
||||
<< "byte " << byte;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ExpectMinidumpContextX86(
|
||||
uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) {
|
||||
MinidumpContextX86 expected;
|
||||
InitializeMinidumpContextX86(&expected, expect_seed);
|
||||
|
||||
@ -175,6 +231,7 @@ void ExpectMinidumpContextX86(uint32_t expect_seed,
|
||||
EXPECT_EQ(expected.dr3, observed->dr3);
|
||||
EXPECT_EQ(expected.dr6, observed->dr6);
|
||||
EXPECT_EQ(expected.dr7, observed->dr7);
|
||||
|
||||
EXPECT_EQ(expected.float_save.control_word,
|
||||
observed->float_save.control_word);
|
||||
EXPECT_EQ(expected.float_save.status_word, observed->float_save.status_word);
|
||||
@ -190,9 +247,14 @@ void ExpectMinidumpContextX86(uint32_t expect_seed,
|
||||
index < arraysize(expected.float_save.register_area);
|
||||
++index) {
|
||||
EXPECT_EQ(expected.float_save.register_area[index],
|
||||
observed->float_save.register_area[index]);
|
||||
observed->float_save.register_area[index]) << "index " << index;
|
||||
}
|
||||
EXPECT_EQ(expected.float_save.spare_0, observed->float_save.spare_0);
|
||||
if (snapshot) {
|
||||
EXPECT_EQ(0u, observed->float_save.spare_0);
|
||||
} else {
|
||||
EXPECT_EQ(expected.float_save.spare_0, observed->float_save.spare_0);
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected.gs, observed->gs);
|
||||
EXPECT_EQ(expected.fs, observed->fs);
|
||||
EXPECT_EQ(expected.es, observed->es);
|
||||
@ -209,40 +271,60 @@ void ExpectMinidumpContextX86(uint32_t expect_seed,
|
||||
EXPECT_EQ(expected.eflags, observed->eflags);
|
||||
EXPECT_EQ(expected.esp, observed->esp);
|
||||
EXPECT_EQ(expected.ss, observed->ss);
|
||||
for (size_t index = 0;
|
||||
index < arraysize(expected.extended_registers);
|
||||
++index) {
|
||||
EXPECT_EQ(expected.extended_registers[index],
|
||||
observed->extended_registers[index]);
|
||||
}
|
||||
|
||||
ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave);
|
||||
}
|
||||
|
||||
void ExpectMinidumpContextAMD64(uint32_t expect_seed,
|
||||
const MinidumpContextAMD64* observed) {
|
||||
void ExpectMinidumpContextAMD64(
|
||||
uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot) {
|
||||
MinidumpContextAMD64 expected;
|
||||
InitializeMinidumpContextAMD64(&expected, expect_seed);
|
||||
|
||||
EXPECT_EQ(expected.context_flags, observed->context_flags);
|
||||
EXPECT_EQ(expected.p1_home, observed->p1_home);
|
||||
EXPECT_EQ(expected.p2_home, observed->p2_home);
|
||||
EXPECT_EQ(expected.p3_home, observed->p3_home);
|
||||
EXPECT_EQ(expected.p4_home, observed->p4_home);
|
||||
EXPECT_EQ(expected.p5_home, observed->p5_home);
|
||||
EXPECT_EQ(expected.p6_home, observed->p6_home);
|
||||
|
||||
if (snapshot) {
|
||||
EXPECT_EQ(0u, observed->p1_home);
|
||||
EXPECT_EQ(0u, observed->p2_home);
|
||||
EXPECT_EQ(0u, observed->p3_home);
|
||||
EXPECT_EQ(0u, observed->p4_home);
|
||||
EXPECT_EQ(0u, observed->p5_home);
|
||||
EXPECT_EQ(0u, observed->p6_home);
|
||||
} else {
|
||||
EXPECT_EQ(expected.p1_home, observed->p1_home);
|
||||
EXPECT_EQ(expected.p2_home, observed->p2_home);
|
||||
EXPECT_EQ(expected.p3_home, observed->p3_home);
|
||||
EXPECT_EQ(expected.p4_home, observed->p4_home);
|
||||
EXPECT_EQ(expected.p5_home, observed->p5_home);
|
||||
EXPECT_EQ(expected.p6_home, observed->p6_home);
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected.mx_csr, observed->mx_csr);
|
||||
|
||||
EXPECT_EQ(expected.cs, observed->cs);
|
||||
EXPECT_EQ(expected.ds, observed->ds);
|
||||
EXPECT_EQ(expected.es, observed->es);
|
||||
if (snapshot) {
|
||||
EXPECT_EQ(0u, observed->ds);
|
||||
EXPECT_EQ(0u, observed->es);
|
||||
} else {
|
||||
EXPECT_EQ(expected.ds, observed->ds);
|
||||
EXPECT_EQ(expected.es, observed->es);
|
||||
}
|
||||
EXPECT_EQ(expected.fs, observed->fs);
|
||||
EXPECT_EQ(expected.gs, observed->gs);
|
||||
EXPECT_EQ(expected.ss, observed->ss);
|
||||
if (snapshot) {
|
||||
EXPECT_EQ(0u, observed->ss);
|
||||
} else {
|
||||
EXPECT_EQ(expected.ss, observed->ss);
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected.eflags, observed->eflags);
|
||||
|
||||
EXPECT_EQ(expected.dr0, observed->dr0);
|
||||
EXPECT_EQ(expected.dr1, observed->dr1);
|
||||
EXPECT_EQ(expected.dr2, observed->dr2);
|
||||
EXPECT_EQ(expected.dr3, observed->dr3);
|
||||
EXPECT_EQ(expected.dr6, observed->dr6);
|
||||
EXPECT_EQ(expected.dr7, observed->dr7);
|
||||
|
||||
EXPECT_EQ(expected.rax, observed->rax);
|
||||
EXPECT_EQ(expected.rcx, observed->rcx);
|
||||
EXPECT_EQ(expected.rdx, observed->rdx);
|
||||
@ -260,59 +342,37 @@ void ExpectMinidumpContextAMD64(uint32_t expect_seed,
|
||||
EXPECT_EQ(expected.r14, observed->r14);
|
||||
EXPECT_EQ(expected.r15, observed->r15);
|
||||
EXPECT_EQ(expected.rip, observed->rip);
|
||||
EXPECT_EQ(expected.float_save.control_word,
|
||||
observed->float_save.control_word);
|
||||
EXPECT_EQ(expected.float_save.status_word, observed->float_save.status_word);
|
||||
EXPECT_EQ(expected.float_save.tag_word, observed->float_save.tag_word);
|
||||
EXPECT_EQ(expected.float_save.reserved_1, observed->float_save.reserved_1);
|
||||
EXPECT_EQ(expected.float_save.error_opcode,
|
||||
observed->float_save.error_opcode);
|
||||
EXPECT_EQ(expected.float_save.error_offset,
|
||||
observed->float_save.error_offset);
|
||||
EXPECT_EQ(expected.float_save.error_selector,
|
||||
observed->float_save.error_selector);
|
||||
EXPECT_EQ(expected.float_save.reserved_2, observed->float_save.reserved_2);
|
||||
EXPECT_EQ(expected.float_save.data_offset, observed->float_save.data_offset);
|
||||
EXPECT_EQ(expected.float_save.data_selector,
|
||||
observed->float_save.data_selector);
|
||||
EXPECT_EQ(expected.float_save.reserved_3, observed->float_save.reserved_3);
|
||||
EXPECT_EQ(expected.float_save.mx_csr, observed->float_save.mx_csr);
|
||||
EXPECT_EQ(expected.float_save.mx_csr_mask, observed->float_save.mx_csr_mask);
|
||||
for (size_t index = 0;
|
||||
index < arraysize(expected.float_save.float_registers);
|
||||
++index) {
|
||||
EXPECT_EQ(expected.float_save.float_registers[index].lo,
|
||||
observed->float_save.float_registers[index].lo);
|
||||
EXPECT_EQ(expected.float_save.float_registers[index].hi,
|
||||
observed->float_save.float_registers[index].hi);
|
||||
}
|
||||
for (size_t index = 0;
|
||||
index < arraysize(expected.float_save.xmm_registers);
|
||||
++index) {
|
||||
EXPECT_EQ(expected.float_save.xmm_registers[index].lo,
|
||||
observed->float_save.xmm_registers[index].lo);
|
||||
EXPECT_EQ(expected.float_save.xmm_registers[index].hi,
|
||||
observed->float_save.xmm_registers[index].hi);
|
||||
}
|
||||
for (size_t index = 0;
|
||||
index < arraysize(expected.float_save.reserved_4);
|
||||
++index) {
|
||||
EXPECT_EQ(expected.float_save.reserved_4[index],
|
||||
observed->float_save.reserved_4[index]);
|
||||
}
|
||||
|
||||
ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave);
|
||||
|
||||
for (size_t index = 0; index < arraysize(expected.vector_register); ++index) {
|
||||
EXPECT_EQ(expected.vector_register[index].lo,
|
||||
observed->vector_register[index].lo);
|
||||
EXPECT_EQ(expected.vector_register[index].hi,
|
||||
observed->vector_register[index].hi);
|
||||
if (snapshot) {
|
||||
EXPECT_EQ(0u, observed->vector_register[index].lo) << "index " << index;
|
||||
EXPECT_EQ(0u, observed->vector_register[index].hi) << "index " << index;
|
||||
} else {
|
||||
EXPECT_EQ(expected.vector_register[index].lo,
|
||||
observed->vector_register[index].lo) << "index " << index;
|
||||
EXPECT_EQ(expected.vector_register[index].hi,
|
||||
observed->vector_register[index].hi) << "index " << index;
|
||||
}
|
||||
}
|
||||
|
||||
if (snapshot) {
|
||||
EXPECT_EQ(0u, observed->vector_control);
|
||||
EXPECT_EQ(0u, observed->debug_control);
|
||||
EXPECT_EQ(0u, observed->last_branch_to_rip);
|
||||
EXPECT_EQ(0u, observed->last_branch_from_rip);
|
||||
EXPECT_EQ(0u, observed->last_exception_to_rip);
|
||||
EXPECT_EQ(0u, observed->last_exception_from_rip);
|
||||
} else {
|
||||
EXPECT_EQ(expected.vector_control, observed->vector_control);
|
||||
EXPECT_EQ(expected.debug_control, observed->debug_control);
|
||||
EXPECT_EQ(expected.last_branch_to_rip, observed->last_branch_to_rip);
|
||||
EXPECT_EQ(expected.last_branch_from_rip, observed->last_branch_from_rip);
|
||||
EXPECT_EQ(expected.last_exception_to_rip, observed->last_exception_to_rip);
|
||||
EXPECT_EQ(expected.last_exception_from_rip,
|
||||
observed->last_exception_from_rip);
|
||||
}
|
||||
EXPECT_EQ(expected.vector_control, observed->vector_control);
|
||||
EXPECT_EQ(expected.debug_control, observed->debug_control);
|
||||
EXPECT_EQ(expected.last_branch_to_rip, observed->last_branch_to_rip);
|
||||
EXPECT_EQ(expected.last_branch_from_rip, observed->last_branch_from_rip);
|
||||
EXPECT_EQ(expected.last_exception_to_rip, observed->last_exception_to_rip);
|
||||
EXPECT_EQ(expected.last_exception_from_rip,
|
||||
observed->last_exception_from_rip);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
@ -24,13 +24,17 @@ namespace test {
|
||||
|
||||
//! \brief Initializes a context structure for testing.
|
||||
//!
|
||||
//! Initialization is compatible with the initialization used by CPUContext test
|
||||
//! initialization functions such as InitializeCPUContextX86() and
|
||||
//! InitializeCPUContextX86_64() for identical \a seed values.
|
||||
//!
|
||||
//! \param[out] context The structure to initialize.
|
||||
//! \param[in] seed The seed value. Initializing two context structures of the
|
||||
//! same type with identical seed values should produce identical context
|
||||
//! structures. Initialization with a different seed value should produce
|
||||
//! a different context structure. If \a seed is `0`, \a context is zeroed
|
||||
//! out entirely except for the flags field, which will identify the context
|
||||
//! type. If \a seed is nonzero \a context will be populated entirely with
|
||||
//! type. If \a seed is nonzero, \a context will be populated entirely with
|
||||
//! nonzero values.
|
||||
//!
|
||||
//! \{
|
||||
@ -48,11 +52,21 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
|
||||
//! \param[in] observed The context structure to check. All fields of this
|
||||
//! structure will be compared against the expected context structure, one
|
||||
//! initialized with \a expect_seed.
|
||||
//! \param[in] snapshot If `true`, compare \a observed to a context structure
|
||||
//! expected to be produced from a CPUContext snapshot. If `false`, compare
|
||||
//! \a observed to a native minidump context structure. CPUContext snapshot
|
||||
//! structures may carry different sets of data than native minidump context
|
||||
//! structures in meaningless ways. When `true`, fields not found in
|
||||
//! CPUContext structures are expected to be `0`. When `false`, all fields
|
||||
//! are compared. This makes it possible to test both that these fields are
|
||||
//! passed through correctly by the native minidump writer and are zeroed
|
||||
//! out when creating a minidump context structure from a CPUContext
|
||||
//! structure.
|
||||
//! \{
|
||||
void ExpectMinidumpContextX86(uint32_t expect_seed,
|
||||
const MinidumpContextX86* observed);
|
||||
void ExpectMinidumpContextAMD64(uint32_t expect_seed,
|
||||
const MinidumpContextAMD64* observed);
|
||||
void ExpectMinidumpContextX86(
|
||||
uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot);
|
||||
void ExpectMinidumpContextAMD64(
|
||||
uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot);
|
||||
//! \}
|
||||
|
||||
} // namespace test
|
||||
|
@ -18,6 +18,72 @@
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
// static
|
||||
uint16_t CPUContextX86::FxsaveToFsaveTagWord(
|
||||
uint16_t fsw,
|
||||
uint8_t fxsave_tag,
|
||||
const CPUContextX86::X87OrMMXRegister st_mm[8]) {
|
||||
enum {
|
||||
kX87TagValid = 0,
|
||||
kX87TagZero,
|
||||
kX87TagSpecial,
|
||||
kX87TagEmpty,
|
||||
};
|
||||
|
||||
// The x87 tag word (in both abridged and full form) identifies physical
|
||||
// registers, but |st_mm| is arranged in logical stack order. In order to map
|
||||
// physical tag word bits to the logical stack registers they correspond to,
|
||||
// the “stack top” value from the x87 status word is necessary.
|
||||
int stack_top = (fsw >> 11) & 0x7;
|
||||
|
||||
uint16_t fsave_tag = 0;
|
||||
for (int physical_index = 0; physical_index < 8; ++physical_index) {
|
||||
bool fxsave_bit = fxsave_tag & (1 << physical_index);
|
||||
uint8_t fsave_bits;
|
||||
|
||||
if (fxsave_bit) {
|
||||
int st_index = (physical_index + 8 - stack_top) % 8;
|
||||
const CPUContextX86::X87Register& st = st_mm[st_index].st;
|
||||
|
||||
uint32_t exponent = ((st[9] & 0x7f) << 8) | st[8];
|
||||
if (exponent == 0x7fff) {
|
||||
// Infinity, NaN, pseudo-infinity, or pseudo-NaN. If it was important to
|
||||
// distinguish between these, the J bit and the M bit (the most
|
||||
// significant bit of |fraction|) could be consulted.
|
||||
fsave_bits = kX87TagSpecial;
|
||||
} else {
|
||||
// The integer bit the “J bit”.
|
||||
bool integer_bit = st[7] & 0x80;
|
||||
if (exponent == 0) {
|
||||
uint64_t fraction = ((static_cast<uint64_t>(st[7]) & 0x7f) << 56) |
|
||||
(static_cast<uint64_t>(st[6]) << 48) |
|
||||
(static_cast<uint64_t>(st[5]) << 40) |
|
||||
(static_cast<uint64_t>(st[4]) << 32) |
|
||||
(static_cast<uint32_t>(st[3]) << 24) |
|
||||
(st[2] << 16) | (st[1] << 8) | st[0];
|
||||
if (!integer_bit && fraction == 0) {
|
||||
fsave_bits = kX87TagZero;
|
||||
} else {
|
||||
// Denormal (if the J bit is clear) or pseudo-denormal.
|
||||
fsave_bits = kX87TagSpecial;
|
||||
}
|
||||
} else if (integer_bit) {
|
||||
fsave_bits = kX87TagValid;
|
||||
} else {
|
||||
// Unnormal.
|
||||
fsave_bits = kX87TagSpecial;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fsave_bits = kX87TagEmpty;
|
||||
}
|
||||
|
||||
fsave_tag |= (fsave_bits << (physical_index * 2));
|
||||
}
|
||||
|
||||
return fsave_tag;
|
||||
}
|
||||
|
||||
uint64_t CPUContext::InstructionPointer() const {
|
||||
switch (architecture) {
|
||||
case kCPUArchitectureX86:
|
||||
|
@ -58,6 +58,35 @@ struct CPUContextX86 {
|
||||
uint8_t available[48];
|
||||
};
|
||||
|
||||
//! \brief Converts x87 floating-point tag words from `fxsave` (abridged,
|
||||
//! 8-bit) to `fsave` (full, 16-bit) form.
|
||||
//!
|
||||
//! `fxsave` stores the x87 floating-point tag word in abridged 8-bit form,
|
||||
//! and `fsave` stores it in full 16-bit form. Some users, notably
|
||||
//! MinidumpContextX86::float_save::tag_word, require the full 16-bit form,
|
||||
//! where most other contemporary code uses `fxsave` and thus the abridged
|
||||
//! 8-bit form found in CPUContextX86::Fxsave::ftw.
|
||||
//!
|
||||
//! This function converts an abridged tag word to the full version by using
|
||||
//! the abridged tag word and the contents of the registers it describes. See
|
||||
//! Intel Software Developer’s Manual, Volume 2A: Instruction Set Reference
|
||||
//! A-M (253666-052), 3.2 “FXSAVE”, specifically, the notes on the abridged
|
||||
//! FTW and recreating the FSAVE format, and AMD Architecture Programmer’s
|
||||
//! Manual, Volume 2: System Programming (24593-3.24), “FXSAVE Format for x87
|
||||
//! Tag Word”.
|
||||
//!
|
||||
//! \param[in] fsw The FPU status word, used to map logical \a st_mm registers
|
||||
//! to their physical counterparts. This can be taken from
|
||||
//! CPUContextX86::Fxsave::fsw.
|
||||
//! \param[in] fxsave_tag The abridged FPU tag word. This can be taken from
|
||||
//! CPUContextX86::Fxsave::ftw.
|
||||
//! \param[in] st_mm The floating-point registers in logical order. This can
|
||||
//! be taken from CPUContextX86::Fxsave::st_mm.
|
||||
//!
|
||||
//! \return The full FPU tag word.
|
||||
static uint16_t FxsaveToFsaveTagWord(
|
||||
uint16_t fsw, uint8_t fxsave_tag, const X87OrMMXRegister st_mm[8]);
|
||||
|
||||
// Integer registers.
|
||||
uint32_t eax;
|
||||
uint32_t ebx;
|
||||
|
150
snapshot/cpu_context_test.cc
Normal file
150
snapshot/cpu_context_test.cc
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/cpu_context.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
enum ExponentValue {
|
||||
kExponentAllZero = 0,
|
||||
kExponentAllOne,
|
||||
kExponentNormal,
|
||||
};
|
||||
|
||||
enum FractionValue {
|
||||
kFractionAllZero = 0,
|
||||
kFractionNormal,
|
||||
};
|
||||
|
||||
//! \brief Initializes an x87 register to a known bit pattern.
|
||||
//!
|
||||
//! \param[out] st_mm The x87 register to initialize. The reserved portion of
|
||||
//! the register is always zeroed out.
|
||||
//! \param[in] exponent_value The bit pattern to use for the exponent. If this
|
||||
//! is kExponentAllZero, the sign bit will be set to `1`, and if this is
|
||||
//! kExponentAllOne, the sign bit will be set to `0`. This tests that the
|
||||
//! implementation doesn’t erroneously consider the sign bit to be part of
|
||||
//! the exponent. This may also be kExponentNormal, indicating that the
|
||||
//! exponent shall neither be all zeroes nor all ones.
|
||||
//! \param[in] j_bit The value to use for the “J bit” (“integer bit”).
|
||||
//! \param[in] fraction_value If kFractionAllZero, the fraction will be zeroed
|
||||
//! out. If kFractionNormal, the fraction will not be all zeroes.
|
||||
void SetX87Register(CPUContextX86::X87OrMMXRegister* st_mm,
|
||||
ExponentValue exponent_value,
|
||||
bool j_bit,
|
||||
FractionValue fraction_value) {
|
||||
switch (exponent_value) {
|
||||
case kExponentAllZero:
|
||||
st_mm->st[9] = 0x80;
|
||||
st_mm->st[8] = 0;
|
||||
break;
|
||||
case kExponentAllOne:
|
||||
st_mm->st[9] = 0x7f;
|
||||
st_mm->st[8] = 0xff;
|
||||
break;
|
||||
case kExponentNormal:
|
||||
st_mm->st[9] = 0x55;
|
||||
st_mm->st[8] = 0x55;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t fraction_pattern = fraction_value == kFractionAllZero ? 0 : 0x55;
|
||||
memset(&st_mm->st[0], fraction_pattern, 8);
|
||||
|
||||
if (j_bit) {
|
||||
st_mm->st[7] |= 0x80;
|
||||
} else {
|
||||
st_mm->st[7] &= ~0x80;
|
||||
}
|
||||
|
||||
memset(st_mm->st_reserved, 0, sizeof(st_mm->st_reserved));
|
||||
}
|
||||
|
||||
TEST(CPUContextX86, FxsaveToFsaveTagWord) {
|
||||
// The fsave tag word uses bit pattern 00 for valid, 01 for zero, 10 for
|
||||
// “special”, and 11 for empty. Like the fxsave tag word, it is arranged by
|
||||
// physical register. The fxsave tag word determines whether a register is
|
||||
// empty, and analysis of the x87 register content distinguishes between
|
||||
// valid, zero, and special. In the initializations below, comments show
|
||||
// whether a register is expected to be considered valid, zero, or special,
|
||||
// except where the tag word is expected to indicate that it is empty. Each
|
||||
// combination appears twice: once where the fxsave tag word indicates a
|
||||
// nonempty register, and once again where it indicates an empty register.
|
||||
|
||||
uint16_t fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7
|
||||
uint8_t fxsave_tag = 0x0f; // physical 4-7 (logical 4-7) empty
|
||||
CPUContextX86::X87OrMMXRegister st_mm[8];
|
||||
SetX87Register(&st_mm[0], kExponentNormal, false, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[1], kExponentNormal, true, kFractionNormal); // valid
|
||||
SetX87Register(&st_mm[2], kExponentNormal, false, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[3], kExponentNormal, true, kFractionAllZero); // valid
|
||||
SetX87Register(&st_mm[4], kExponentNormal, false, kFractionNormal);
|
||||
SetX87Register(&st_mm[5], kExponentNormal, true, kFractionNormal);
|
||||
SetX87Register(&st_mm[6], kExponentNormal, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[7], kExponentNormal, true, kFractionAllZero);
|
||||
EXPECT_EQ(0xff22,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
fsw = 2 << 11; // top = 2: logical 0-7 maps to physical 2-7, 0-1
|
||||
fxsave_tag = 0xf0; // physical 0-3 (logical 6-7, 0-1) empty
|
||||
SetX87Register(&st_mm[0], kExponentAllZero, false, kFractionNormal);
|
||||
SetX87Register(&st_mm[1], kExponentAllZero, true, kFractionNormal);
|
||||
SetX87Register(&st_mm[2], kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87Register(&st_mm[3], kExponentAllZero, true, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[5], kExponentAllZero, true, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[7], kExponentAllZero, true, kFractionAllZero);
|
||||
EXPECT_EQ(0xa9ff,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
fsw = 5 << 11; // top = 5: logical 0-7 maps to physical 5-7, 0-4
|
||||
fxsave_tag = 0x5a; // physical 0, 2, 5, and 7 (logical 5, 0, 2, and 3) empty
|
||||
SetX87Register(&st_mm[0], kExponentAllOne, false, kFractionNormal);
|
||||
SetX87Register(&st_mm[1], kExponentAllOne, true, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[2], kExponentAllOne, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionAllZero);
|
||||
SetX87Register(&st_mm[4], kExponentAllOne, false, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[5], kExponentAllOne, true, kFractionNormal);
|
||||
SetX87Register(&st_mm[6], kExponentAllOne, false, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[7], kExponentAllOne, true, kFractionAllZero); // spec.
|
||||
EXPECT_EQ(0xeebb,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
// This set set is just a mix of all of the possible tag types in a single
|
||||
// register file.
|
||||
fsw = 1 << 11; // top = 1: logical 0-7 maps to physical 1-7, 0
|
||||
fxsave_tag = 0x1f; // physical 5-7 (logical 4-6) empty
|
||||
SetX87Register(&st_mm[0], kExponentNormal, true, kFractionAllZero); // valid
|
||||
SetX87Register(&st_mm[1], kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87Register(&st_mm[2], kExponentAllOne, true, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[5], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[7], kExponentNormal, true, kFractionNormal); // valid
|
||||
EXPECT_EQ(0xfe90,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -86,6 +86,8 @@
|
||||
'..',
|
||||
],
|
||||
'sources': [
|
||||
'test/test_cpu_context.cc',
|
||||
'test/test_cpu_context.h',
|
||||
'test/test_memory_snapshot.cc',
|
||||
'test/test_memory_snapshot.h',
|
||||
'test/test_module_snapshot.cc',
|
||||
@ -109,6 +111,7 @@
|
||||
'..',
|
||||
],
|
||||
'sources': [
|
||||
'cpu_context_test.cc',
|
||||
'mac/cpu_context_mac_test.cc',
|
||||
'mac/mach_o_image_annotations_reader_test.cc',
|
||||
'mac/mach_o_image_reader_test.cc',
|
||||
|
165
snapshot/test/test_cpu_context.cc
Normal file
165
snapshot/test/test_cpu_context.cc
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/test/test_cpu_context.h"
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
||||
void InitializeCPUContextX86(CPUContext* context, uint32_t seed) {
|
||||
context->architecture = kCPUArchitectureX86;
|
||||
|
||||
if (seed == 0) {
|
||||
memset(context->x86, 0, sizeof(*context->x86));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t value = seed;
|
||||
|
||||
context->x86->eax = value++;
|
||||
context->x86->ebx = value++;
|
||||
context->x86->ecx = value++;
|
||||
context->x86->edx = value++;
|
||||
context->x86->edi = value++;
|
||||
context->x86->esi = value++;
|
||||
context->x86->ebp = value++;
|
||||
context->x86->esp = value++;
|
||||
context->x86->eip = value++;
|
||||
context->x86->eflags = value++;
|
||||
context->x86->cs = value++;
|
||||
context->x86->ds = value++;
|
||||
context->x86->es = value++;
|
||||
context->x86->fs = value++;
|
||||
context->x86->gs = value++;
|
||||
context->x86->ss = value++;
|
||||
InitializeCPUContextX86Fxsave(&context->x86->fxsave, &value);
|
||||
context->x86->dr0 = value++;
|
||||
context->x86->dr1 = value++;
|
||||
context->x86->dr2 = value++;
|
||||
context->x86->dr3 = value++;
|
||||
context->x86->dr4 = value++;
|
||||
context->x86->dr5 = value++;
|
||||
context->x86->dr6 = value++;
|
||||
context->x86->dr7 = value++;
|
||||
}
|
||||
|
||||
void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) {
|
||||
context->architecture = kCPUArchitectureX86_64;
|
||||
|
||||
if (seed == 0) {
|
||||
memset(context->x86_64, 0, sizeof(*context->x86_64));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t value = seed;
|
||||
|
||||
context->x86_64->rax = value++;
|
||||
context->x86_64->rbx = value++;
|
||||
context->x86_64->rcx = value++;
|
||||
context->x86_64->rdx = value++;
|
||||
context->x86_64->rdi = value++;
|
||||
context->x86_64->rsi = value++;
|
||||
context->x86_64->rbp = value++;
|
||||
context->x86_64->rsp = value++;
|
||||
context->x86_64->r8 = value++;
|
||||
context->x86_64->r9 = value++;
|
||||
context->x86_64->r10 = value++;
|
||||
context->x86_64->r11 = value++;
|
||||
context->x86_64->r12 = value++;
|
||||
context->x86_64->r13 = value++;
|
||||
context->x86_64->r14 = value++;
|
||||
context->x86_64->r15 = value++;
|
||||
context->x86_64->rip = value++;
|
||||
context->x86_64->rflags = value++;
|
||||
context->x86_64->cs = value++;
|
||||
context->x86_64->fs = value++;
|
||||
context->x86_64->gs = value++;
|
||||
InitializeCPUContextX86_64Fxsave(&context->x86_64->fxsave, &value);
|
||||
context->x86_64->dr0 = value++;
|
||||
context->x86_64->dr1 = value++;
|
||||
context->x86_64->dr2 = value++;
|
||||
context->x86_64->dr3 = value++;
|
||||
context->x86_64->dr4 = value++;
|
||||
context->x86_64->dr5 = value++;
|
||||
context->x86_64->dr6 = value++;
|
||||
context->x86_64->dr7 = value++;
|
||||
}
|
||||
|
||||
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 = value++;
|
||||
fxsave->fsw = value++;
|
||||
fxsave->ftw = value++;
|
||||
fxsave->reserved_1 = value++;
|
||||
fxsave->fop = value++;
|
||||
fxsave->fpu_ip = value++;
|
||||
fxsave->fpu_cs = value++;
|
||||
fxsave->reserved_2 = value++;
|
||||
fxsave->fpu_dp = value++;
|
||||
fxsave->fpu_ds = value++;
|
||||
fxsave->reserved_3 = 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] = 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] = 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] = value++;
|
||||
}
|
||||
}
|
||||
for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) {
|
||||
fxsave->reserved_4[byte] = value++;
|
||||
}
|
||||
for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) {
|
||||
fxsave->available[byte] = 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);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
67
snapshot/test/test_cpu_context.h
Normal file
67
snapshot/test/test_cpu_context.h
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_
|
||||
#define CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "snapshot/cpu_context.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
||||
//! \brief Initializes a context structure for testing.
|
||||
//!
|
||||
//! Initialization is compatible with the initialization used by minidump
|
||||
//! context test initialization functions such as InitializeMinidumpContextX86()
|
||||
//! and InitializeMinidumpContextAMD64() for identical \a seed values.
|
||||
//!
|
||||
//! \param[out] context The structure to initialize.
|
||||
//! \param[in] seed The seed value. Initializing two context structures of the
|
||||
//! same type with identical seed values should produce identical context
|
||||
//! structures. Initialization with a different seed value should produce
|
||||
//! a different context structure. If \a seed is `0`, \a context is zeroed
|
||||
//! out entirely except for the CPUContext::architecture field, which will
|
||||
//! identify the context type. If \a seed is nonzero, \a context will be
|
||||
//! populated entirely with nonzero values.
|
||||
//!
|
||||
//! \{
|
||||
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);
|
||||
//! \}
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_
|
Loading…
x
Reference in New Issue
Block a user