mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 15:32:10 +08:00
9e0051aba6
Adds new structures and offsets for minidump extended contexts. This information will be captured from threads in a later CL so this CL does not yet write different dumps, except in testing. Minidump format for extended compacted contexts has been determined by experiment. Offsets for where to write various parts of the context are hardcoded to 0x550 as this matches values seen in Windows. Offsets for misc_info_5 match those seen in working minidumps that can be opened in windbg. Our hope is that while these could change in future, CPU and OS vendors are unlikely to change them. See doc[0] for a discussion of these fields and offsets in the minidump. See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the Intel SDM[1]. Many of the offsets and sizes of the extended features are provided by cpu specific values. We can access these in Windows using the SDK, and transfer these to the saved extended context which in turn is understandable by windbg. Further information is available from AMD Ch. 18 "Shadow Stacks"[2]. [0] https://docs.google.com/document/d/1Dn8n97r5B7kxYouvujNnPIYd_7QeVHpahSRmB92Qn6g/edit#heading=h.hivqj2jg39y [1] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html. [2] https://www.amd.com/system/files/TechDocs/24593.pdf Bug: 1250098 Change-Id: Ia9041acc379c6d38329ee99737a2a0a77f7a1ee0 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3536964 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Alex Gough <ajgo@chromium.org>
465 lines
16 KiB
C++
465 lines
16 KiB
C++
// 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 "minidump/minidump_misc_info_writer.h"
|
||
|
||
#include <iterator>
|
||
#include <limits>
|
||
|
||
#include "base/check_op.h"
|
||
#include "base/logging.h"
|
||
#include "base/numerics/safe_conversions.h"
|
||
#include "base/strings/stringprintf.h"
|
||
#include "base/strings/utf_string_conversions.h"
|
||
#include "build/build_config.h"
|
||
#include "minidump/minidump_context_writer.h"
|
||
#include "minidump/minidump_writer_util.h"
|
||
#include "package.h"
|
||
#include "snapshot/cpu_context.h"
|
||
#include "snapshot/process_snapshot.h"
|
||
#include "snapshot/system_snapshot.h"
|
||
#include "snapshot/thread_snapshot.h"
|
||
#include "util/file/file_writer.h"
|
||
#include "util/numeric/in_range_cast.h"
|
||
#include "util/numeric/safe_assignment.h"
|
||
|
||
#if BUILDFLAG(IS_MAC)
|
||
#include <Availability.h>
|
||
#elif BUILDFLAG(IS_ANDROID)
|
||
#include <android/api-level.h>
|
||
#endif
|
||
|
||
namespace crashpad {
|
||
namespace {
|
||
|
||
uint32_t TimevalToRoundedSeconds(const timeval& tv) {
|
||
uint32_t seconds =
|
||
InRangeCast<uint32_t>(tv.tv_sec, std::numeric_limits<uint32_t>::max());
|
||
constexpr int kMicrosecondsPerSecond = static_cast<int>(1E6);
|
||
if (tv.tv_usec >= kMicrosecondsPerSecond / 2 &&
|
||
seconds != std::numeric_limits<uint32_t>::max()) {
|
||
++seconds;
|
||
}
|
||
return seconds;
|
||
}
|
||
|
||
// For MINIDUMP_MISC_INFO_4::BuildString. dbghelp only places OS version
|
||
// information here, but if a machine description is also available, this is the
|
||
// only reasonable place in a minidump file to put it.
|
||
std::string BuildString(const SystemSnapshot* system_snapshot) {
|
||
std::string os_version_full = system_snapshot->OSVersionFull();
|
||
std::string machine_description = system_snapshot->MachineDescription();
|
||
if (!os_version_full.empty()) {
|
||
if (!machine_description.empty()) {
|
||
return base::StringPrintf(
|
||
"%s; %s", os_version_full.c_str(), machine_description.c_str());
|
||
}
|
||
return os_version_full;
|
||
}
|
||
return machine_description;
|
||
}
|
||
|
||
#if BUILDFLAG(IS_MAC)
|
||
// Converts the value of the __MAC_OS_X_VERSION_MIN_REQUIRED or
|
||
// __MAC_OS_X_VERSION_MAX_ALLOWED macro from <Availability.h> to a number
|
||
// identifying the macOS version that it represents, in the same format used by
|
||
// MacOSVersionNumber(). For example, with an argument of __MAC_10_15, this
|
||
// function will return 10'15'00, which is incidentally the same as __MAC_10_15.
|
||
// With an argument of __MAC_10_9, this function will return 10'09'00, different
|
||
// from __MAC_10_9, which is 10'9'0.
|
||
int AvailabilityVersionToMacOSVersionNumber(int availability) {
|
||
#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10
|
||
DCHECK_GE(availability, 10'0'0);
|
||
|
||
// Until __MAC_10_10, the format is major * 1'0'0 + minor * 1'0 + bugfix.
|
||
if (availability >= 10'0'0 && availability <= 10'9'9) {
|
||
int minor = (availability / 1'0) % 1'0;
|
||
int bugfix = availability % 1'0;
|
||
return 10'00'00 + minor * 1'00 + bugfix;
|
||
}
|
||
#endif
|
||
|
||
// Since __MAC_10_10, the format is major * 1'00'00 + minor * 1'00 + bugfix.
|
||
DCHECK_GE(availability, 10'10'00);
|
||
DCHECK_LE(availability, 99'99'99);
|
||
|
||
return availability;
|
||
}
|
||
#endif // BUILDFLAG(IS_MAC)
|
||
|
||
bool MaybeSetXStateData(const ProcessSnapshot* process_snapshot,
|
||
XSTATE_CONFIG_FEATURE_MSC_INFO* xstate) {
|
||
// Cannot set xstate data if there are no threads.
|
||
auto threads = process_snapshot->Threads();
|
||
if (threads.size() == 0)
|
||
return false;
|
||
|
||
// All threads should be the same as we request contexts in the same way.
|
||
auto context = threads.at(0)->Context();
|
||
|
||
// Only support AMD64.
|
||
if (context->architecture != kCPUArchitectureX86_64)
|
||
return false;
|
||
|
||
// If no extended features, then we will just write the standard context.
|
||
if (context->x86_64->xstate.enabled_features == 0)
|
||
return false;
|
||
|
||
xstate->SizeOfInfo = sizeof(*xstate);
|
||
// Needs to match the size of the context we'll write or the dump is invalid,
|
||
// so ask the first thread how large it will be.
|
||
auto context_writer = MinidumpContextWriter::CreateFromSnapshot(context);
|
||
xstate->ContextSize =
|
||
static_cast<uint32_t>(context_writer->FreezeAndGetSizeOfObject());
|
||
// Note: This isn't the same as xstateenabledfeatures!
|
||
xstate->EnabledFeatures =
|
||
context->x86_64->xstate.enabled_features | XSTATE_COMPACTION_ENABLE_MASK;
|
||
|
||
// Note: if other XSAVE entries are to be supported they will be in order,
|
||
// and may have different offsets depending on what is saved.
|
||
if (context->x86_64->xstate.enabled_features & XSTATE_MASK_CET_U) {
|
||
xstate->Features[XSTATE_CET_U].Offset = kXSaveAreaFirstOffset;
|
||
xstate->Features[XSTATE_CET_U].Size = sizeof(MinidumpAMD64XSaveFormatCetU);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
} // namespace
|
||
|
||
namespace internal {
|
||
|
||
// For MINIDUMP_MISC_INFO_4::DbgBldStr. dbghelp produces strings like
|
||
// “dbghelp.i386,6.3.9600.16520” and “dbghelp.amd64,6.3.9600.16520”. Mimic that
|
||
// format, and add the OS that wrote the minidump along with any relevant
|
||
// platform-specific data describing the compilation environment.
|
||
std::string MinidumpMiscInfoDebugBuildString() {
|
||
// Caution: the minidump file format only has room for 39 UTF-16 code units
|
||
// plus a UTF-16 NUL terminator. Don’t let strings get longer than this, or
|
||
// they will be truncated and a message will be logged.
|
||
#if BUILDFLAG(IS_MAC)
|
||
static constexpr char kOS[] = "mac";
|
||
#elif BUILDFLAG(IS_IOS)
|
||
static constexpr char kOS[] = "ios";
|
||
#elif BUILDFLAG(IS_ANDROID)
|
||
static constexpr char kOS[] = "android";
|
||
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
|
||
static constexpr char kOS[] = "linux";
|
||
#elif BUILDFLAG(IS_WIN)
|
||
static constexpr char kOS[] = "win";
|
||
#elif BUILDFLAG(IS_FUCHSIA)
|
||
static constexpr char kOS[] = "fuchsia";
|
||
#else
|
||
#error define kOS for this operating system
|
||
#endif
|
||
|
||
#if defined(ARCH_CPU_X86)
|
||
static constexpr char kCPU[] = "i386";
|
||
#elif defined(ARCH_CPU_X86_64)
|
||
static constexpr char kCPU[] = "amd64";
|
||
#elif defined(ARCH_CPU_ARMEL)
|
||
static constexpr char kCPU[] = "arm";
|
||
#elif defined(ARCH_CPU_ARM64)
|
||
static constexpr char kCPU[] = "arm64";
|
||
#elif defined(ARCH_CPU_MIPSEL)
|
||
static constexpr char kCPU[] = "mips";
|
||
#elif defined(ARCH_CPU_MIPS64EL)
|
||
static constexpr char kCPU[] = "mips64";
|
||
#else
|
||
#error define kCPU for this CPU
|
||
#endif
|
||
|
||
std::string debug_build_string = base::StringPrintf("%s.%s,%s,%s",
|
||
PACKAGE_TARNAME,
|
||
kCPU,
|
||
PACKAGE_VERSION,
|
||
kOS);
|
||
|
||
#if BUILDFLAG(IS_MAC)
|
||
debug_build_string += base::StringPrintf(
|
||
",%d,%d",
|
||
AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MIN_REQUIRED),
|
||
AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MAX_ALLOWED));
|
||
#elif BUILDFLAG(IS_ANDROID)
|
||
debug_build_string += base::StringPrintf(",%d", __ANDROID_API__);
|
||
#endif
|
||
|
||
return debug_build_string;
|
||
}
|
||
|
||
} // namespace internal
|
||
|
||
MinidumpMiscInfoWriter::MinidumpMiscInfoWriter()
|
||
: MinidumpStreamWriter(), misc_info_(), has_xstate_data_(false) {
|
||
}
|
||
|
||
MinidumpMiscInfoWriter::~MinidumpMiscInfoWriter() {
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::InitializeFromSnapshot(
|
||
const ProcessSnapshot* process_snapshot) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
DCHECK_EQ(misc_info_.Flags1, 0u);
|
||
|
||
SetProcessID(InRangeCast<uint32_t>(process_snapshot->ProcessID(), 0));
|
||
|
||
const SystemSnapshot* system_snapshot = process_snapshot->System();
|
||
|
||
uint64_t current_hz;
|
||
uint64_t max_hz;
|
||
system_snapshot->CPUFrequency(¤t_hz, &max_hz);
|
||
constexpr uint32_t kHzPerMHz = static_cast<const uint32_t>(1E6);
|
||
SetProcessorPowerInfo(
|
||
InRangeCast<uint32_t>(current_hz / kHzPerMHz,
|
||
std::numeric_limits<uint32_t>::max()),
|
||
InRangeCast<uint32_t>(max_hz / kHzPerMHz,
|
||
std::numeric_limits<uint32_t>::max()),
|
||
0,
|
||
0,
|
||
0);
|
||
|
||
timeval start_time;
|
||
process_snapshot->ProcessStartTime(&start_time);
|
||
|
||
timeval user_time;
|
||
timeval system_time;
|
||
process_snapshot->ProcessCPUTimes(&user_time, &system_time);
|
||
|
||
// Round the resource usage fields to the nearest second, because the minidump
|
||
// format only has one-second resolution. The start_time field is truncated
|
||
// instead of rounded so that the process uptime is reflected more accurately
|
||
// when the start time is compared to the snapshot time in the
|
||
// MINIDUMP_HEADER, which is also truncated, not rounded.
|
||
uint32_t user_seconds = TimevalToRoundedSeconds(user_time);
|
||
uint32_t system_seconds = TimevalToRoundedSeconds(system_time);
|
||
|
||
SetProcessTimes(start_time.tv_sec, user_seconds, system_seconds);
|
||
|
||
// This determines the system’s time zone, which may be different than the
|
||
// process’ notion of the time zone.
|
||
SystemSnapshot::DaylightSavingTimeStatus dst_status;
|
||
int standard_offset_seconds;
|
||
int daylight_offset_seconds;
|
||
std::string standard_name;
|
||
std::string daylight_name;
|
||
system_snapshot->TimeZone(&dst_status,
|
||
&standard_offset_seconds,
|
||
&daylight_offset_seconds,
|
||
&standard_name,
|
||
&daylight_name);
|
||
|
||
// standard_offset_seconds is seconds east of UTC, but the minidump file wants
|
||
// minutes west of UTC. daylight_offset_seconds is also seconds east of UTC,
|
||
// but the minidump file wants minutes west of the standard offset. The empty
|
||
// ({}) arguments are for the transition times in and out of daylight saving
|
||
// time. These are not determined because no API exists to do so, and the
|
||
// transition times may vary from year to year.
|
||
SetTimeZone(dst_status,
|
||
standard_offset_seconds / -60,
|
||
standard_name,
|
||
{},
|
||
0,
|
||
daylight_name,
|
||
{},
|
||
(standard_offset_seconds - daylight_offset_seconds) / 60);
|
||
|
||
SetBuildString(BuildString(system_snapshot),
|
||
internal::MinidumpMiscInfoDebugBuildString());
|
||
|
||
XSTATE_CONFIG_FEATURE_MSC_INFO xstate{};
|
||
if (MaybeSetXStateData(process_snapshot, &xstate)) {
|
||
SetXStateData(xstate);
|
||
}
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetProcessID(uint32_t process_id) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.ProcessId = process_id;
|
||
misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_ID;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetProcessTimes(time_t process_create_time,
|
||
uint32_t process_user_time,
|
||
uint32_t process_kernel_time) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
internal::MinidumpWriterUtil::AssignTimeT(&misc_info_.ProcessCreateTime,
|
||
process_create_time);
|
||
|
||
misc_info_.ProcessUserTime = process_user_time;
|
||
misc_info_.ProcessKernelTime = process_kernel_time;
|
||
misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_TIMES;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetProcessorPowerInfo(
|
||
uint32_t processor_max_mhz,
|
||
uint32_t processor_current_mhz,
|
||
uint32_t processor_mhz_limit,
|
||
uint32_t processor_max_idle_state,
|
||
uint32_t processor_current_idle_state) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.ProcessorMaxMhz = processor_max_mhz;
|
||
misc_info_.ProcessorCurrentMhz = processor_current_mhz;
|
||
misc_info_.ProcessorMhzLimit = processor_mhz_limit;
|
||
misc_info_.ProcessorMaxIdleState = processor_max_idle_state;
|
||
misc_info_.ProcessorCurrentIdleState = processor_current_idle_state;
|
||
misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESSOR_POWER_INFO;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetProcessIntegrityLevel(
|
||
uint32_t process_integrity_level) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.ProcessIntegrityLevel = process_integrity_level;
|
||
misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_INTEGRITY;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetProcessExecuteFlags(
|
||
uint32_t process_execute_flags) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.ProcessExecuteFlags = process_execute_flags;
|
||
misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetProtectedProcess(uint32_t protected_process) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.ProtectedProcess = protected_process;
|
||
misc_info_.Flags1 |= MINIDUMP_MISC3_PROTECTED_PROCESS;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetTimeZone(uint32_t time_zone_id,
|
||
int32_t bias,
|
||
const std::string& standard_name,
|
||
const SYSTEMTIME& standard_date,
|
||
int32_t standard_bias,
|
||
const std::string& daylight_name,
|
||
const SYSTEMTIME& daylight_date,
|
||
int32_t daylight_bias) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.TimeZoneId = time_zone_id;
|
||
misc_info_.TimeZone.Bias = bias;
|
||
|
||
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
|
||
AsU16CStr(misc_info_.TimeZone.StandardName),
|
||
std::size(misc_info_.TimeZone.StandardName),
|
||
standard_name);
|
||
|
||
misc_info_.TimeZone.StandardDate = standard_date;
|
||
misc_info_.TimeZone.StandardBias = standard_bias;
|
||
|
||
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
|
||
AsU16CStr(misc_info_.TimeZone.DaylightName),
|
||
std::size(misc_info_.TimeZone.DaylightName),
|
||
daylight_name);
|
||
|
||
misc_info_.TimeZone.DaylightDate = daylight_date;
|
||
misc_info_.TimeZone.DaylightBias = daylight_bias;
|
||
|
||
misc_info_.Flags1 |= MINIDUMP_MISC3_TIMEZONE;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetBuildString(
|
||
const std::string& build_string,
|
||
const std::string& debug_build_string) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.Flags1 |= MINIDUMP_MISC4_BUILDSTRING;
|
||
|
||
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
|
||
AsU16CStr(misc_info_.BuildString),
|
||
std::size(misc_info_.BuildString),
|
||
build_string);
|
||
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
|
||
AsU16CStr(misc_info_.DbgBldStr),
|
||
std::size(misc_info_.DbgBldStr),
|
||
debug_build_string);
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetXStateData(
|
||
const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.XStateData = xstate_data;
|
||
has_xstate_data_ = true;
|
||
}
|
||
|
||
bool MinidumpMiscInfoWriter::HasXStateData() const {
|
||
return has_xstate_data_;
|
||
}
|
||
|
||
void MinidumpMiscInfoWriter::SetProcessCookie(uint32_t process_cookie) {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
misc_info_.ProcessCookie = process_cookie;
|
||
misc_info_.Flags1 |= MINIDUMP_MISC5_PROCESS_COOKIE;
|
||
}
|
||
|
||
bool MinidumpMiscInfoWriter::Freeze() {
|
||
DCHECK_EQ(state(), kStateMutable);
|
||
|
||
if (!MinidumpStreamWriter::Freeze()) {
|
||
return false;
|
||
}
|
||
|
||
size_t size = CalculateSizeOfObjectFromFlags();
|
||
if (!AssignIfInRange(&misc_info_.SizeOfInfo, size)) {
|
||
LOG(ERROR) << "size " << size << " out of range";
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
size_t MinidumpMiscInfoWriter::SizeOfObject() {
|
||
DCHECK_GE(state(), kStateFrozen);
|
||
|
||
return CalculateSizeOfObjectFromFlags();
|
||
}
|
||
|
||
bool MinidumpMiscInfoWriter::WriteObject(FileWriterInterface* file_writer) {
|
||
DCHECK_EQ(state(), kStateWritable);
|
||
|
||
return file_writer->Write(&misc_info_, CalculateSizeOfObjectFromFlags());
|
||
}
|
||
|
||
MinidumpStreamType MinidumpMiscInfoWriter::StreamType() const {
|
||
return kMinidumpStreamTypeMiscInfo;
|
||
}
|
||
|
||
size_t MinidumpMiscInfoWriter::CalculateSizeOfObjectFromFlags() const {
|
||
DCHECK_GE(state(), kStateFrozen);
|
||
|
||
if (has_xstate_data_ || (misc_info_.Flags1 & MINIDUMP_MISC5_PROCESS_COOKIE)) {
|
||
return sizeof(MINIDUMP_MISC_INFO_5);
|
||
}
|
||
if (misc_info_.Flags1 & MINIDUMP_MISC4_BUILDSTRING) {
|
||
return sizeof(MINIDUMP_MISC_INFO_4);
|
||
}
|
||
if (misc_info_.Flags1 &
|
||
(MINIDUMP_MISC3_PROCESS_INTEGRITY | MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS |
|
||
MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC3_PROTECTED_PROCESS)) {
|
||
return sizeof(MINIDUMP_MISC_INFO_3);
|
||
}
|
||
if (misc_info_.Flags1 & MINIDUMP_MISC1_PROCESSOR_POWER_INFO) {
|
||
return sizeof(MINIDUMP_MISC_INFO_2);
|
||
}
|
||
return sizeof(MINIDUMP_MISC_INFO);
|
||
}
|
||
|
||
} // namespace crashpad
|