crashpad/snapshot/mac/system_snapshot_mac.cc
2014-10-17 13:41:45 -04:00

400 lines
12 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 "snapshot/mac/system_snapshot_mac.h"
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <time.h>
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#include "snapshot/mac/process_reader.h"
#include "util/mac/mac_util.h"
#include "util/numeric/in_range_cast.h"
namespace crashpad {
namespace {
template <typename T>
T ReadIntSysctlByName(const char* name, T default_value) {
T value;
size_t value_len = sizeof(value);
if (sysctlbyname(name, &value, &value_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname " << name;
return default_value;
}
return value;
}
template <typename T>
T CastIntSysctlByName(const char* name, T default_value) {
int int_value = ReadIntSysctlByName<int>(name, default_value);
return InRangeCast<T>(int_value, default_value);
}
std::string ReadStringSysctlByName(const char* name) {
size_t buf_len;
if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname (size) " << name;
return std::string();
}
if (buf_len == 0) {
return std::string();
}
std::string value(buf_len - 1, '\0');
if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname " << name;
return std::string();
}
return value;
}
#if defined(ARCH_CPU_X86_FAMILY)
void CallCPUID(uint32_t leaf,
uint32_t* eax,
uint32_t* ebx,
uint32_t* ecx,
uint32_t* edx) {
asm("cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "a"(leaf), "b"(0), "c"(0), "d"(0));
}
#endif
} // namespace
namespace internal {
SystemSnapshotMac::SystemSnapshotMac()
: SystemSnapshot(),
os_version_full_(),
os_version_build_(),
process_reader_(nullptr),
snapshot_time_(nullptr),
os_version_major_(0),
os_version_minor_(0),
os_version_bugfix_(0),
os_server_(false),
initialized_() {
}
SystemSnapshotMac::~SystemSnapshotMac() {
}
void SystemSnapshotMac::Initialize(ProcessReader* process_reader,
const timeval* snapshot_time) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
process_reader_ = process_reader;
snapshot_time_ = snapshot_time;
// MacOSXVersion() logs its own warnings if it cant figure anything out. Its
// not fatal if this happens. The default values are reasonable.
std::string os_version_string;
MacOSXVersion(&os_version_major_,
&os_version_minor_,
&os_version_bugfix_,
&os_version_build_,
&os_server_,
&os_version_string);
std::string uname_string;
utsname uts;
if (uname(&uts) != 0) {
PLOG(WARNING) << "uname";
} else {
uname_string = base::StringPrintf(
"%s %s %s %s", uts.sysname, uts.release, uts.version, uts.machine);
}
if (!os_version_string.empty()) {
if (!uname_string.empty()) {
os_version_full_ = base::StringPrintf(
"%s; %s", os_version_string.c_str(), uname_string.c_str());
} else {
os_version_full_ = os_version_string;
}
} else {
os_version_full_ = uname_string;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
}
CPUArchitecture SystemSnapshotMac::GetCPUArchitecture() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return process_reader_->Is64Bit() ? kCPUArchitectureX86_64
: kCPUArchitectureX86;
#else
#error port to your architecture
#endif
}
uint32_t SystemSnapshotMac::CPURevision() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
// machdep.cpu.family and machdep.cpu.model already take the extended family
// and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c
// cpuid_set_generic_info().
uint16_t family = CastIntSysctlByName<uint16_t>("machdep.cpu.family", 0);
uint8_t model = CastIntSysctlByName<uint8_t>("machdep.cpu.model", 0);
uint8_t stepping = CastIntSysctlByName<uint8_t>("machdep.cpu.stepping", 0);
return (family << 16) | (model << 8) | stepping;
#else
#error port to your architecture
#endif
}
uint8_t SystemSnapshotMac::CPUCount() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return CastIntSysctlByName<uint8_t>("hw.ncpu", 1);
}
std::string SystemSnapshotMac::CPUVendor() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return ReadStringSysctlByName("machdep.cpu.vendor");
#else
#error port to your architecture
#endif
}
void SystemSnapshotMac::CPUFrequency(
uint64_t* current_hz, uint64_t* max_hz) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*current_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency", 0);
*max_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency_max", 0);
}
uint32_t SystemSnapshotMac::CPUX86Signature() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return ReadIntSysctlByName<uint32_t>("machdep.cpu.signature", 0);
#else
NOTREACHED();
return 0;
#endif
}
uint64_t SystemSnapshotMac::CPUX86Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return ReadIntSysctlByName<uint64_t>("machdep.cpu.feature_bits", 0);
#else
NOTREACHED();
return 0;
#endif
}
uint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return ReadIntSysctlByName<uint64_t>("machdep.cpu.extfeature_bits", 0);
#else
NOTREACHED();
return 0;
#endif
}
uint32_t SystemSnapshotMac::CPUX86Leaf7Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
// The machdep.cpu.leaf7_feature_bits sysctl isnt supported prior to Mac OS X
// 10.7, so read this by calling cpuid directly.
//
// machdep.cpu.max_basic could be used to check whether to read the leaf, but
// that sysctl isnt supported prior to Mac OS X 10.6, so read the maximum
// basic leaf by calling cpuid directly as well. All CPUs that Apple is known
// to have shipped should support a maximum basic leaf value of at least 0xa.
uint32_t eax, ebx, ecx, edx;
CallCPUID(0, &eax, &ebx, &ecx, &edx);
if (eax < 7) {
return 0;
}
CallCPUID(7, &eax, &ebx, &ecx, &edx);
return ebx;
#else
NOTREACHED();
return 0;
#endif
}
bool SystemSnapshotMac::CPUX86SupportsDAZ() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
// The correct way to check for denormals-as-zeros (DAZ) support is to examine
// mxcsr mask, which can be done with fxsave. See Intel Software Developers
// Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the
// DAZ Flag in the MXCSR Register”. Note that since this function tests for
// DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would
// indicate whether DAZ is actually enabled, which is a per-thread context
// concern.
//
// All CPUs that Apple is known to have shipped should support DAZ.
// Test for fxsave support.
uint64_t features = CPUX86Features();
if (!(features & (UINT64_C(1) << 24))) {
return false;
}
// Call fxsave.
#if defined(ARCH_CPU_X86)
CPUContextX86::Fxsave fxsave __attribute__((aligned(16))) = {};
#elif defined(ARCH_CPU_X86_64)
CPUContextX86_64::Fxsave fxsave __attribute__((aligned(16))) = {};
#endif
static_assert(sizeof(fxsave) == 512, "fxsave size");
static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28,
"mxcsr_mask offset");
asm("fxsave %0" : "=m"(fxsave));
// Test the DAZ bit.
return fxsave.mxcsr_mask & (1 << 6);
#else
NOTREACHED();
return false;
#endif
}
SystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return kOperatingSystemMacOSX;
}
bool SystemSnapshotMac::OSServer() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return os_server_;
}
void SystemSnapshotMac::OSVersion(int* major,
int* minor,
int* bugfix,
std::string* build) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*major = os_version_major_;
*minor = os_version_minor_;
*bugfix = os_version_bugfix_;
build->assign(os_version_build_);
}
std::string SystemSnapshotMac::OSVersionFull() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return os_version_full_;
}
std::string SystemSnapshotMac::MachineDescription() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::string model;
std::string board_id;
MacModelAndBoard(&model, &board_id);
if (!model.empty()) {
if (!board_id.empty()) {
return base::StringPrintf("%s (%s)", model.c_str(), board_id.c_str());
}
return model;
}
if (!board_id.empty()) {
return base::StringPrintf("(%s)", board_id.c_str());
}
return std::string();
}
bool SystemSnapshotMac::NXEnabled() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return ReadIntSysctlByName<int>("kern.nx", 0);
}
void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,
int* standard_offset_seconds,
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
tm local;
PCHECK(localtime_r(&snapshot_time_->tv_sec, &local)) << "localtime_r";
*standard_name = tzname[0];
if (daylight) {
// Scan forward and backward, one month at a time, looking for an instance
// when the observance of daylight saving time is different than it is in
// |local|.
long probe_gmtoff = local.tm_gmtoff;
const int kMonthDeltas[] =
{ 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6,
7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12 };
for (size_t index = 0; index < arraysize(kMonthDeltas); ++index) {
// Look at the 15th day of each month at local noon. Set tm_isdst to -1 to
// avoid giving mktime() any hints about whether to consider daylight
// saving time in effect. mktime() accepts values of tm_mon that are
// outside of its normal range and behaves as expected: if tm_mon is -1,
// it references December of the preceding year, and if it is 12, it
// references January of the following year.
tm probe_tm = {};
probe_tm.tm_hour = 12;
probe_tm.tm_mday = 15;
probe_tm.tm_mon = local.tm_mon + kMonthDeltas[index];
probe_tm.tm_year = local.tm_year;
probe_tm.tm_isdst = -1;
if (mktime(&probe_tm) != -1 && probe_tm.tm_isdst != local.tm_isdst) {
probe_gmtoff = probe_tm.tm_gmtoff;
break;
}
}
*daylight_name = tzname[1];
if (!local.tm_isdst) {
*dst_status = kObservingStandardTime;
*standard_offset_seconds = local.tm_gmtoff;
*daylight_offset_seconds = probe_gmtoff;
} else {
*dst_status = kObservingDaylightSavingTime;
*standard_offset_seconds = probe_gmtoff;
*daylight_offset_seconds = local.tm_gmtoff;
}
} else {
*daylight_name = tzname[0];
*dst_status = kDoesNotObserveDaylightSavingTime;
*standard_offset_seconds = local.tm_gmtoff;
*daylight_offset_seconds = local.tm_gmtoff;
}
}
} // namespace internal
} // namespace crashpad