crashpad/minidump/minidump_misc_info_writer.cc
Mark Mentovai bdfd147a47 Add MinidumpMiscInfoWriter::InitializeFromSnapshot() and its test.
TEST=minidump_test MinidumpMiscInfoWriter.InitializeFromSnapshot
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/701783004
2014-11-05 18:15:19 -05:00

368 lines
13 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. 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 <limits>
#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_writer_util.h"
#include "package.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h"
#include "util/file/file_writer.h"
#include "util/numeric/in_range_cast.h"
#include "util/numeric/safe_assignment.h"
#if defined(OS_MACOSX)
#include <AvailabilityMacros.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());
const int kMicrosecondsPerSecond = 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 defined(OS_MACOSX)
// Converts the value of the MAC_OS_VERSION_MIN_REQUIRED or
// MAC_OS_X_VERSION_MAX_ALLOWED macro from <AvailabilityMacros.h> to a number
// identifying the minor Mac OS X version that it represents. For example, with
// an argument of MAC_OS_X_VERSION_10_6, this function will return 6.
int AvailabilityVersionToMacOSXMinorVersion(int availability) {
// Through MAC_OS_X_VERSION_10_9, the minor version is the tens digit.
if (availability >= 1000 && availability <= 1099) {
return (availability / 10) % 10;
}
// After MAC_OS_X_VERSION_10_9, the older format was insufficient to represent
// versions. Since then, the minor version is the thousands and hundreds
// digits.
if (availability >= 100000 && availability <= 109999) {
return (availability / 100) % 100;
}
return 0;
}
#endif
} // 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. Dont let strings get longer than this, or
// they will be truncated and a message will be logged.
#if defined(OS_MACOSX)
const char kOS[] = "mac";
#elif defined(OS_LINUX)
const char kOS[] = "linux";
#else
#error define kOS for this operating system
#endif
#if defined(ARCH_CPU_X86)
const char kCPU[] = "i386";
#elif defined(ARCH_CPU_X86_64)
const char kCPU[] = "amd64";
#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 defined(OS_MACOSX)
debug_build_string += base::StringPrintf(
",%d,%d",
AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MIN_REQUIRED),
AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MAX_ALLOWED));
#endif
return debug_build_string;
}
} // namespace internal
MinidumpMiscInfoWriter::MinidumpMiscInfoWriter()
: MinidumpStreamWriter(), misc_info_() {
}
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_mhz;
uint64_t max_mhz;
system_snapshot->CPUFrequency(&current_mhz, &max_mhz);
const uint32_t kHzPerMHz = 1E6;
SetProcessorPowerInfo(
InRangeCast<uint32_t>(current_mhz / kHzPerMHz,
std::numeric_limits<uint32_t>::max()),
InRangeCast<uint32_t>(max_mhz / 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 systems 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());
}
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(
misc_info_.TimeZone.StandardName,
arraysize(misc_info_.TimeZone.StandardName),
standard_name);
misc_info_.TimeZone.StandardDate = standard_date;
misc_info_.TimeZone.StandardBias = standard_bias;
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
misc_info_.TimeZone.DaylightName,
arraysize(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(
misc_info_.BuildString, arraysize(misc_info_.BuildString), build_string);
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
misc_info_.DbgBldStr,
arraysize(misc_info_.DbgBldStr),
debug_build_string);
}
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 (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