kicad/thirdparty/sentry-native/external/crashpad/minidump/minidump_context_writer.cc

634 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2014 The Crashpad Authors
//
// 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 "minidump/minidump_context_writer.h"
#include <windows.h>
#include <dbghelp.h>
#include <stdint.h>
#include <string.h>
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#include "util/file/file_writer.h"
#include "util/stdlib/aligned_allocator.h"
namespace crashpad {
namespace {
// Sanity-check complex structures to ensure interoperability.
static_assert(sizeof(MinidumpContextX86) == 716, "MinidumpContextX86 size");
static_assert(sizeof(MinidumpContextAMD64) == 1232,
"MinidumpContextAMD64 size");
// These structures can also be checked against definitions in the Windows SDK.
#if BUILDFLAG(IS_WIN)
#if defined(ARCH_CPU_X86_FAMILY)
static_assert(sizeof(MinidumpContextX86) == sizeof(WOW64_CONTEXT),
"WOW64_CONTEXT size");
#if defined(ARCH_CPU_X86)
static_assert(sizeof(MinidumpContextX86) == sizeof(CONTEXT), "CONTEXT size");
#elif defined(ARCH_CPU_X86_64)
static_assert(sizeof(MinidumpContextAMD64) == sizeof(CONTEXT), "CONTEXT size");
#endif
#endif // ARCH_CPU_X86_FAMILY
#endif // BUILDFLAG(IS_WIN)
} // namespace
MinidumpContextWriter::~MinidumpContextWriter() {
}
// static
std::unique_ptr<MinidumpContextWriter>
MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) {
std::unique_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;
}
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);
break;
}
case kCPUArchitectureMIPSEL: {
context = std::make_unique<MinidumpContextMIPSWriter>();
reinterpret_cast<MinidumpContextMIPSWriter*>(context.get())
->InitializeFromSnapshot(context_snapshot->mipsel);
break;
}
case kCPUArchitectureMIPS64EL: {
context = std::make_unique<MinidumpContextMIPS64Writer>();
reinterpret_cast<MinidumpContextMIPS64Writer*>(context.get())
->InitializeFromSnapshot(context_snapshot->mips64);
break;
}
case kCPUArchitectureRISCV64: {
context = std::make_unique<MinidumpContextRISCV64Writer>();
reinterpret_cast<MinidumpContextRISCV64Writer*>(context.get())
->InitializeFromSnapshot(context_snapshot->riscv64);
break;
}
default: {
LOG(ERROR) << "unknown context architecture "
<< context_snapshot->architecture;
break;
}
}
return context;
}
size_t MinidumpContextWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
return ContextSize();
}
size_t MinidumpContextWriter::FreezeAndGetSizeOfObject() {
Freeze();
return SizeOfObject();
}
MinidumpContextX86Writer::MinidumpContextX86Writer()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextX86;
}
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_.fsave effectively alias everything in
// context_.fxsave thats related to x87 FPU state. context_.fsave doesnt
// carry state specific to SSE (or later), such as mxcsr and the xmm
// registers.
CPUContextX86::FxsaveToFsave(context_snapshot->fxsave, &context_.fsave);
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);
return file_writer->Write(&context_, sizeof(context_));
}
size_t MinidumpContextX86Writer::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
return sizeof(context_);
}
static_assert(alignof(MinidumpContextAMD64) >= 16,
"MinidumpContextAMD64 alignment");
static_assert(alignof(MinidumpContextAMD64Writer) >=
alignof(MinidumpContextAMD64),
"MinidumpContextAMD64Writer alignment");
MinidumpContextAMD64Writer::MinidumpContextAMD64Writer()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextAMD64;
}
MinidumpContextAMD64Writer::~MinidumpContextAMD64Writer() {
}
// static
void* MinidumpContextAMD64Writer::operator new(size_t size) {
// MinidumpContextAMD64 requests an alignment of 16, which can be larger than
// what standard new provides. This may trigger MSVC warning C4316. As a
// workaround to this language deficiency, provide a custom allocation
// function to allocate a block meeting the alignment requirement.
return AlignedAllocate(alignof(MinidumpContextAMD64Writer), size);
}
// static
void MinidumpContextAMD64Writer::operator delete(void* pointer) {
return AlignedFree(pointer);
}
void MinidumpContextAMD64Writer::InitializeFromSnapshot(
const CPUContextX86_64* context_snapshot) {
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextAMD64);
if (context_snapshot->xstate.enabled_features != 0) {
// Extended context.
context_.context_flags =
kMinidumpContextAMD64All | kMinidumpContextAMD64Xstate;
} else {
// Fixed size context - no xsave components.
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;
// The top 32 bits of rflags are reserved/unused.
context_.eflags = static_cast<uint32_t>(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;
// If XSave features are being recorded store in xsave_entries in xcomp_bv
// order. We will not see features we do not support as we provide flags
// to the OS when first obtaining a snapshot.
if (context_snapshot->xstate.enabled_features & XSTATE_MASK_CET_U) {
auto cet_u = std::make_unique<MinidumpXSaveAMD64CetU>();
cet_u->InitializeFromSnapshot(context_snapshot);
xsave_entries_.push_back(std::move(cet_u));
}
}
size_t MinidumpContextAMD64Writer::Alignment() {
DCHECK_GE(state(), kStateFrozen);
// Match the alignment of MinidumpContextAMD64.
return 16;
}
bool MinidumpContextAMD64Writer::WriteObject(FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
// Note: all sizes here come from our constants, not from untrustworthy data.
std::vector<unsigned char> data(ContextSize());
unsigned char* const buf = data.data();
// CONTEXT always comes first.
DCHECK_LE(sizeof(context_), data.size());
memcpy(buf, &context_, sizeof(context_));
if (xsave_entries_.size() > 0) {
MinidumpContextExHeader context_ex = {{0, 0}, {0, 0}, {0, 0}};
MinidumpXSaveAreaHeader xsave_header = {0, 0, {}};
// CONTEXT_EX goes directly after the CONTEXT. |offset| is relative to
// &CONTEXT_EX.
context_ex.all.offset = -static_cast<int32_t>(sizeof(context_));
context_ex.all.size = static_cast<uint32_t>(ContextSize());
context_ex.legacy.offset = context_ex.all.offset;
context_ex.legacy.size = sizeof(context_);
// Then... there is a gap.
//
// In the compacted format the XSave area header goes just before
// the first xsave entry. It has a total size given by the header
// + (padded) sizes of all the entries.
context_ex.xstate.offset = static_cast<int32_t>(
kMinidumpAMD64XSaveOffset - sizeof(MinidumpXSaveAreaHeader) -
sizeof(context_));
context_ex.xstate.size =
static_cast<uint32_t>(sizeof(MinidumpXSaveAreaHeader) + ContextSize() -
kMinidumpAMD64XSaveOffset);
// Store CONTEXT_EX now it is complete.
DCHECK_LE(sizeof(context_) + sizeof(context_ex), data.size());
memcpy(&buf[sizeof(context_)], &context_ex, sizeof(context_ex));
// Calculate flags for xsave header & write entries (they will be
// *after* the xsave header).
size_t cursor = kMinidumpAMD64XSaveOffset;
for (auto const& entry : xsave_entries_) {
xsave_header.mask |= 1ull << entry->XCompBVBit();
DCHECK_LE(cursor + entry->Size(), data.size());
entry->Copy(&buf[cursor]);
cursor += entry->Size();
}
xsave_header.compaction_mask =
xsave_header.mask | XSTATE_COMPACTION_ENABLE_MASK;
// Store xsave header at its calculated offset. It is before the entries
// above, but we need to add the |mask| bits before writing it.
DCHECK_LE(
context_ex.xstate.offset + sizeof(context_) + sizeof(xsave_header),
data.size());
memcpy(&buf[context_ex.xstate.offset + sizeof(context_)],
&xsave_header,
sizeof(xsave_header));
}
if (!file_writer->Write(data.data(), data.size()))
return false;
return true;
}
size_t MinidumpContextAMD64Writer::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
if (xsave_entries_.size() == 0) {
return sizeof(context_);
} else {
DCHECK_EQ(context_.context_flags,
kMinidumpContextAMD64All | kMinidumpContextAMD64Xstate);
DCHECK(xsave_entries_.size() != 0);
size_t size = kMinidumpAMD64XSaveOffset;
for (auto& entry : xsave_entries_) {
size += entry->Size();
}
return size;
}
}
bool MinidumpXSaveAMD64CetU::InitializeFromSnapshot(
const CPUContextX86_64* context_snapshot) {
#ifdef SENTRY_DISABLED
DCHECK_EQ(context_snapshot->xstate.cet_u.cetmsr, 1ull);
#else
// TODO(supervacuus): this DCHECK led to multiple user inquiries because it
// ends up killing the crashpad_handler (when using a DEBUG build) which in
// turn keeps the crashpad client waiting indefinitely.
//
// It seems that crashpad devs put a DCHECK here because they already check
// at the call-site that the CET_U flag is enabled in the XSAVE feature set.
// However, that this flag is set, only means that the CET_U registers in
// XSAVE are valid, not necessarily that the SH_STK_EN bit is set.
//
// I couldn't find anything in the Intel (SDM 13.1) or AMD (PR 11.5.2,
// 18.11/12/13) CET/SS spec that would signal that SH_STK_EN(=cetmsr[0])
// cannot be 0 at this point. Ideally, if SH_STK_EN is not set, then SSP
// should be set to 0 too (which means both are in their initial state). But
// even that should not lead to fatally exit the crashpad_handler (even in
// DEBUG), but rather produce a log and result in something that can be
// analysed in the backend.
//
// Any validation based on these register contents must check SH_STK_EN
// anyway or check SSP for !NULL and as a valid base like it is done here:
// https://chromium.googlesource.com/crashpad/crashpad/+/6278690abe6ef0dda047e67dc1d0c49ce7af3811/snapshot/win/thread_snapshot_win.cc#130
if (!(context_snapshot->xstate.cet_u.cetmsr & 1ull)) {
LOG(WARNING) << "CET MSR enabled flag is not set ("
<< context_snapshot->xstate.cet_u.cetmsr
<< "); SSP = "
<< context_snapshot->xstate.cet_u.ssp;
}
#endif
cet_u_.cetmsr = context_snapshot->xstate.cet_u.cetmsr;
cet_u_.ssp = context_snapshot->xstate.cet_u.ssp;
return true;
}
bool MinidumpXSaveAMD64CetU::Copy(void* dst) const {
memcpy(dst, &cet_u_, sizeof(cet_u_));
return true;
}
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 = kMinidumpContextARM64Full;
static_assert(
sizeof(context_.regs) == sizeof(context_snapshot->regs) -
2 * sizeof(context_snapshot->regs[0]),
"GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
context_.fp = context_snapshot->regs[29];
context_.lr = context_snapshot->regs[30];
context_.sp = context_snapshot->sp;
context_.pc = context_snapshot->pc;
context_.cpsr = context_snapshot->spsr;
static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd),
"FPSIMD size mismatch");
memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd));
context_.fpcr = context_snapshot->fpcr;
context_.fpsr = context_snapshot->fpsr;
memset(context_.bcr, 0, sizeof(context_.bcr));
memset(context_.bvr, 0, sizeof(context_.bvr));
memset(context_.wcr, 0, sizeof(context_.wcr));
memset(context_.wvr, 0, sizeof(context_.wvr));
}
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_);
}
MinidumpContextMIPSWriter::MinidumpContextMIPSWriter()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextMIPS;
}
MinidumpContextMIPSWriter::~MinidumpContextMIPSWriter() = default;
void MinidumpContextMIPSWriter::InitializeFromSnapshot(
const CPUContextMIPS* context_snapshot) {
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS);
context_.context_flags = kMinidumpContextMIPSAll;
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
"GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
context_.mdhi = context_snapshot->mdhi;
context_.mdlo = context_snapshot->mdlo;
context_.epc = context_snapshot->cp0_epc;
context_.badvaddr = context_snapshot->cp0_badvaddr;
context_.status = context_snapshot->cp0_status;
context_.cause = context_snapshot->cp0_cause;
static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),
"FPRs size mismatch");
memcpy(&context_.fpregs, &context_snapshot->fpregs, sizeof(context_.fpregs));
context_.fpcsr = context_snapshot->fpcsr;
context_.fir = context_snapshot->fir;
for (size_t index = 0; index < 3; ++index) {
context_.hi[index] = context_snapshot->hi[index];
context_.lo[index] = context_snapshot->lo[index];
}
context_.dsp_control = context_snapshot->dsp_control;
}
bool MinidumpContextMIPSWriter::WriteObject(FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
return file_writer->Write(&context_, sizeof(context_));
}
size_t MinidumpContextMIPSWriter::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
return sizeof(context_);
}
MinidumpContextMIPS64Writer::MinidumpContextMIPS64Writer()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextMIPS64;
}
MinidumpContextMIPS64Writer::~MinidumpContextMIPS64Writer() = default;
void MinidumpContextMIPS64Writer::InitializeFromSnapshot(
const CPUContextMIPS64* context_snapshot) {
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS64);
context_.context_flags = kMinidumpContextMIPS64All;
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
"GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
context_.mdhi = context_snapshot->mdhi;
context_.mdlo = context_snapshot->mdlo;
context_.epc = context_snapshot->cp0_epc;
context_.badvaddr = context_snapshot->cp0_badvaddr;
context_.status = context_snapshot->cp0_status;
context_.cause = context_snapshot->cp0_cause;
static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),
"FPRs size mismatch");
memcpy(context_.fpregs.dregs,
context_snapshot->fpregs.dregs,
sizeof(context_.fpregs.dregs));
context_.fpcsr = context_snapshot->fpcsr;
context_.fir = context_snapshot->fir;
for (size_t index = 0; index < 3; ++index) {
context_.hi[index] = context_snapshot->hi[index];
context_.lo[index] = context_snapshot->lo[index];
}
context_.dsp_control = context_snapshot->dsp_control;
}
bool MinidumpContextMIPS64Writer::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
return file_writer->Write(&context_, sizeof(context_));
}
size_t MinidumpContextMIPS64Writer::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
return sizeof(context_);
}
MinidumpContextRISCV64Writer::MinidumpContextRISCV64Writer()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextRISCV64;
context_.version = MinidumpContextRISCV64::kVersion;
}
MinidumpContextRISCV64Writer::~MinidumpContextRISCV64Writer() = default;
void MinidumpContextRISCV64Writer::InitializeFromSnapshot(
const CPUContextRISCV64* context_snapshot) {
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextRISCV64);
context_.context_flags = kMinidumpContextRISCV64All;
context_.version = MinidumpContextRISCV64::kVersion;
context_.pc = context_snapshot->pc;
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
"GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),
"FPRs size mismatch");
memcpy(context_.fpregs, context_snapshot->fpregs, sizeof(context_.fpregs));
context_.fcsr = context_snapshot->fcsr;
}
bool MinidumpContextRISCV64Writer::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
return file_writer->Write(&context_, sizeof(context_));
}
size_t MinidumpContextRISCV64Writer::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
return sizeof(context_);
}
} // namespace crashpad