Add SystemSnapshotLinux

Bug: crashpad:30
Change-Id: Ic1162c6637708492a5a9903a221cdd9266d3fd97
Reviewed-on: https://chromium-review.googlesource.com/601028
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2017-08-08 15:04:00 -07:00 committed by Commit Bot
parent f16e4eb9ff
commit b953388b95
14 changed files with 1186 additions and 210 deletions

View File

@ -46,8 +46,8 @@ def main(args):
ARCH_TRIPLET_TO_ARCH = { ARCH_TRIPLET_TO_ARCH = {
'arm-linux-androideabi': 'arm', 'arm-linux-androideabi': 'arm',
'aarch64-linux-android': 'arm64', 'aarch64-linux-android': 'arm64',
'i686-linux-android': 'x86', 'i686-linux-android': 'ia32',
'x86_64-linux-android': 'x86_64', 'x86_64-linux-android': 'x64',
'mipsel-linux-android': 'mips', 'mipsel-linux-android': 'mips',
'mips64el-linux-android': 'mips64', 'mips64el-linux-android': 'mips64',
} }

View File

@ -0,0 +1,397 @@
// Copyright 2017 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/linux/system_snapshot_linux.h"
#include <stddef.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <algorithm>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "snapshot/cpu_context.h"
#include "snapshot/posix/timezone.h"
#include "util/file/file_io.h"
#include "util/numeric/in_range_cast.h"
#include "util/string/split_string.h"
#if defined(OS_ANDROID)
#include <sys/system_properties.h>
#endif
namespace crashpad {
namespace internal {
namespace {
bool ReadCPUsOnline(uint32_t* first_cpu, uint8_t* cpu_count) {
std::string contents;
if (!LoggingReadEntireFile(base::FilePath("/sys/devices/system/cpu/online"),
&contents)) {
return false;
}
if (contents.back() != '\n') {
LOG(ERROR) << "format error";
return false;
}
contents.pop_back();
unsigned int count = 0;
unsigned int first = 0;
bool have_first = false;
std::vector<std::string> ranges = SplitString(contents, ',');
for (const auto& range : ranges) {
std::string left, right;
if (SplitStringFirst(range, '-', &left, &right)) {
unsigned int start, end;
if (!StringToUint(base::StringPiece(left), &start) ||
!StringToUint(base::StringPiece(right), &end) || end <= start) {
LOG(ERROR) << "format error: " << range;
return false;
}
if (end <= start) {
LOG(ERROR) << "format error";
return false;
}
count += end - start + 1;
if (!have_first) {
first = start;
have_first = true;
}
} else {
unsigned int cpuno;
if (!StringToUint(base::StringPiece(range), &cpuno)) {
LOG(ERROR) << "format error";
return false;
}
if (!have_first) {
first = cpuno;
have_first = true;
}
++count;
}
}
if (!have_first) {
LOG(ERROR) << "no cpus online";
return false;
}
*cpu_count = InRangeCast<uint8_t>(count, std::numeric_limits<uint8_t>::max());
*first_cpu = first;
return true;
}
bool ReadFreqFile(const std::string& filename, uint64_t* hz) {
std::string contents;
if (!LoggingReadEntireFile(base::FilePath(filename), &contents)) {
return false;
}
if (contents.back() != '\n') {
LOG(ERROR) << "format error";
return false;
}
contents.pop_back();
uint64_t khz;
if (!base::StringToUint64(base::StringPiece(contents), &khz)) {
LOG(ERROR) << "format error";
return false;
}
*hz = khz * 1000;
return true;
}
#if defined(OS_ANDROID)
bool ReadProperty(const char* property, std::string* value) {
char value_buffer[PROP_VALUE_MAX];
int length = __system_property_get(property, value_buffer);
if (length <= 0) {
LOG(ERROR) << "Couldn't read property " << property;
return false;
}
*value = value_buffer;
return true;
}
#endif // OS_ANDROID
} // namespace
SystemSnapshotLinux::SystemSnapshotLinux()
: SystemSnapshot(),
os_version_full_(),
os_version_build_(),
process_reader_(nullptr),
snapshot_time_(nullptr),
#if defined(ARCH_CPU_X86_FAMILY)
cpuid_(),
#endif // ARCH_CPU_X86_FAMILY
os_version_major_(-1),
os_version_minor_(-1),
os_version_bugfix_(-1),
target_cpu_(0),
cpu_count_(0),
initialized_() {
}
SystemSnapshotLinux::~SystemSnapshotLinux() {}
void SystemSnapshotLinux::Initialize(ProcessReader* process_reader,
const timeval* snapshot_time) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
process_reader_ = process_reader;
snapshot_time_ = snapshot_time;
#if defined(OS_ANDROID)
std::string build_string;
if (ReadProperty("ro.build.fingerprint", &build_string)) {
os_version_build_ = build_string;
os_version_full_ = build_string;
}
#endif // OS_ANDROID
utsname uts;
if (uname(&uts) != 0) {
PLOG(WARNING) << "uname";
} else {
if (!os_version_full_.empty()) {
os_version_full_.push_back(' ');
}
os_version_full_ += base::StringPrintf(
"%s %s %s %s", uts.sysname, uts.release, uts.version, uts.machine);
}
ReadKernelVersion(uts.release);
if (!os_version_build_.empty()) {
os_version_build_.push_back(' ');
}
os_version_build_ += uts.version;
os_version_build_.push_back(' ');
os_version_build_ += uts.machine;
if (!ReadCPUsOnline(&target_cpu_, &cpu_count_)) {
target_cpu_ = 0;
cpu_count_ = 0;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
}
CPUArchitecture SystemSnapshotLinux::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 SystemSnapshotLinux::CPURevision() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.Revision();
#else
#error port to your architecture
#endif
}
uint8_t SystemSnapshotLinux::CPUCount() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return cpu_count_;
}
std::string SystemSnapshotLinux::CPUVendor() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.Vendor();
#else
#error port to your architecture
#endif
}
void SystemSnapshotLinux::CPUFrequency(uint64_t* current_hz,
uint64_t* max_hz) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*current_hz = 0;
*max_hz = 0;
ReadFreqFile(base::StringPrintf(
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq",
target_cpu_),
current_hz);
ReadFreqFile(base::StringPrintf(
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq",
target_cpu_),
max_hz);
}
uint32_t SystemSnapshotLinux::CPUX86Signature() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.Signature();
#else
NOTREACHED();
return 0;
#endif
}
uint64_t SystemSnapshotLinux::CPUX86Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.Features();
#else
NOTREACHED();
return 0;
#endif
}
uint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return cpuid_.ExtendedFeatures();
}
uint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.Leaf7Features();
#else
NOTREACHED();
return 0;
#endif
}
bool SystemSnapshotLinux::CPUX86SupportsDAZ() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.SupportsDAZ();
#else
NOTREACHED();
return false;
#endif // ARCH_CPU_X86_FMAILY
}
SystemSnapshot::OperatingSystem SystemSnapshotLinux::GetOperatingSystem()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(OS_ANDROID)
return kOperatingSystemAndroid;
#else
return kOperatingSystemLinux;
#endif // OS_ANDROID
}
bool SystemSnapshotLinux::OSServer() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return false;
}
void SystemSnapshotLinux::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 SystemSnapshotLinux::OSVersionFull() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return os_version_full_;
}
std::string SystemSnapshotLinux::MachineDescription() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(OS_ANDROID)
std::string description;
std::string prop;
if (ReadProperty("ro.product.model", &prop)) {
description += prop;
}
if (ReadProperty("ro.product.board", &prop)) {
if (!description.empty()) {
description.push_back(' ');
}
description += prop;
}
return description;
#else
return std::string();
#endif // OS_ANDROID
}
bool SystemSnapshotLinux::NXEnabled() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return cpuid_.NXEnabled();
}
void SystemSnapshotLinux::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_);
internal::TimeZone(*snapshot_time_,
dst_status,
standard_offset_seconds,
daylight_offset_seconds,
standard_name,
daylight_name);
}
void SystemSnapshotLinux::ReadKernelVersion(const std::string& version_string) {
std::vector<std::string> versions = SplitString(version_string, '.');
if (versions.size() < 3) {
LOG(WARNING) << "format error";
return;
}
if (!StringToInt(base::StringPiece(versions[0]), &os_version_major_)) {
LOG(WARNING) << "no kernel version";
return;
}
DCHECK_GE(os_version_major_, 3);
if (!StringToInt(base::StringPiece(versions[1]), &os_version_minor_)) {
LOG(WARNING) << "no major revision";
return;
}
DCHECK_GE(os_version_minor_, 0);
size_t minor_rev_end = versions[2].find_first_not_of("0123456789");
if (minor_rev_end == std::string::npos) {
minor_rev_end = versions[2].size();
}
if (!StringToInt(base::StringPiece(versions[2].c_str(), minor_rev_end),
&os_version_bugfix_)) {
LOG(WARNING) << "no minor revision";
return;
}
DCHECK_GE(os_version_bugfix_, 0);
if (!os_version_build_.empty()) {
os_version_build_.push_back(' ');
}
os_version_build_ += versions[2].substr(minor_rev_end);
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,112 @@
// Copyright 2017 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_LINUX_SYSTEM_SNAPSHOT_LINUX_H_
#define CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_
#include <stdint.h>
#include <time.h>
#include <string>
#include "base/macros.h"
#include "build/build_config.h"
#include "snapshot/linux/process_reader.h"
#include "snapshot/system_snapshot.h"
#include "util/misc/initialization_state_dcheck.h"
#if defined(ARCH_CPU_X86_FAMILY)
#include "snapshot/x86/cpuid_reader.h"
#endif // ARCH_CPU_X86_FAMILY
namespace crashpad {
namespace internal {
//! \brief A SystemSnapshot of the running system, when the system runs Linux.
class SystemSnapshotLinux final : public SystemSnapshot {
public:
SystemSnapshotLinux();
~SystemSnapshotLinux() override;
//! \brief Initializes the object.
//!
//! \param[in] process_reader A reader for the process being snapshotted.
//! \n\n
//! It seems odd that a system snapshot implementation would need a
//! ProcessReader, but some of the information reported about the system
//! depends on the process its being reported for. For example, the
//! architecture returned by GetCPUArchitecture() should be the
//! architecture of the process, which may be different than the native
//! architecture of the system: an x86_64 system can run both x86_64 and
//! 32-bit x86 processes.
//! \param[in] snapshot_time The time of the snapshot being taken.
//! \n\n
//! This parameter is necessary for TimeZone() to determine whether
//! daylight saving time was in effect at the time the snapshot was taken.
//! Otherwise, it would need to base its determination on the current
//! time, which may be different than the snapshot time for snapshots
//! generated around the daylight saving transition time.
void Initialize(ProcessReader* process_reader, const timeval* snapshot_time);
// SystemSnapshot:
CPUArchitecture GetCPUArchitecture() const override;
uint32_t CPURevision() const override;
uint8_t CPUCount() const override;
std::string CPUVendor() const override;
void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;
uint32_t CPUX86Signature() const override;
uint64_t CPUX86Features() const override;
uint64_t CPUX86ExtendedFeatures() const override;
uint32_t CPUX86Leaf7Features() const override;
bool CPUX86SupportsDAZ() const override;
OperatingSystem GetOperatingSystem() const override;
bool OSServer() const override;
void OSVersion(int* major,
int* minor,
int* bugfix,
std::string* build) const override;
std::string OSVersionFull() const override;
bool NXEnabled() const override;
std::string MachineDescription() const override;
void TimeZone(DaylightSavingTimeStatus* dst_status,
int* standard_offset_seconds,
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
private:
void ReadKernelVersion(const std::string& version_string);
std::string os_version_full_;
std::string os_version_build_;
ProcessReader* process_reader_; // weak
const timeval* snapshot_time_; // weak
#if defined(ARCH_CPU_X86_FAMILY)
CpuidReader cpuid_;
#endif // ARCH_CPU_X86_FAMILY
int os_version_major_;
int os_version_minor_;
int os_version_bugfix_;
uint32_t target_cpu_;
uint8_t cpu_count_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(SystemSnapshotLinux);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_

View File

@ -0,0 +1,85 @@
// Copyright 2017 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/linux/system_snapshot_linux.h"
#include <sys/time.h>
#include <string>
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "snapshot/linux/process_reader.h"
#include "test/errors.h"
namespace crashpad {
namespace test {
namespace {
TEST(SystemSnapshotLinux, Basic) {
ProcessReader process_reader;
ASSERT_TRUE(process_reader.Initialize(getpid()));
timeval snapshot_time;
ASSERT_EQ(gettimeofday(&snapshot_time, nullptr), 0)
<< ErrnoMessage("gettimeofday");
internal::SystemSnapshotLinux system;
system.Initialize(&process_reader, &snapshot_time);
EXPECT_GT(system.CPUCount(), 0u);
uint64_t current_hz, max_hz;
system.CPUFrequency(&current_hz, &max_hz);
EXPECT_GE(max_hz, current_hz);
int major, minor, bugfix;
std::string build;
system.OSVersion(&major, &minor, &bugfix, &build);
EXPECT_GE(major, 3);
EXPECT_GE(minor, 0);
EXPECT_GE(bugfix, 0);
EXPECT_FALSE(build.empty());
EXPECT_FALSE(system.OSVersionFull().empty());
// No expectations; just make sure these can be called successfully.
system.CPURevision();
system.NXEnabled();
#if defined(OS_ANDROID)
EXPECT_FALSE(system.MachineDescription().empty());
#else
system.MachineDescription();
#endif // OS_ANDROID
#if defined(ARCH_CPU_X86_FAMILY)
system.CPUX86Signature();
system.CPUX86Features();
system.CPUX86ExtendedFeatures();
system.CPUX86Leaf7Features();
EXPECT_PRED1(
[](std::string vendor) {
return vendor == "GenuineIntel" || vendor == "AuthenticAMD";
},
system.CPUVendor());
EXPECT_TRUE(system.CPUX86SupportsDAZ());
#endif // ARCH_CPU_X86_FAMILY
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -18,7 +18,6 @@
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <time.h>
#include <algorithm> #include <algorithm>
@ -27,6 +26,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "snapshot/cpu_context.h" #include "snapshot/cpu_context.h"
#include "snapshot/mac/process_reader.h" #include "snapshot/mac/process_reader.h"
#include "snapshot/posix/timezone.h"
#include "util/mac/mac_util.h" #include "util/mac/mac_util.h"
#include "util/numeric/in_range_cast.h" #include "util/numeric/in_range_cast.h"
@ -348,64 +348,12 @@ void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,
std::string* daylight_name) const { std::string* daylight_name) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
tm local; internal::TimeZone(*snapshot_time_,
PCHECK(localtime_r(&snapshot_time_->tv_sec, &local)) << "localtime_r"; dst_status,
standard_offset_seconds,
*standard_name = tzname[0]; daylight_offset_seconds,
standard_name,
bool found_transition = false; daylight_name);
long probe_gmtoff = local.tm_gmtoff;
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|. Its possible that no such instance will be found even with
// |daylight| set. This can happen in locations where daylight saving time
// was once observed or is expected to be observed in the future, but where
// no transitions to or from daylight saving time occurred or will occur
// within a year of the current date. Arizona, which last observed daylight
// saving time in 1967, is an example.
static constexpr 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) && !found_transition;
++index) {
// Look at a 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 = std::min(local.tm_mday, 28);
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) {
found_transition = true;
probe_gmtoff = probe_tm.tm_gmtoff;
}
}
}
if (found_transition) {
*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 internal

View File

@ -14,13 +14,10 @@
#include "snapshot/mac/system_snapshot_mac.h" #include "snapshot/mac/system_snapshot_mac.h"
#include <stdlib.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h>
#include <string> #include <string>
#include "base/strings/stringprintf.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "snapshot/mac/process_reader.h" #include "snapshot/mac/process_reader.h"
@ -129,152 +126,6 @@ TEST_F(SystemSnapshotMacTest, MachineDescription) {
EXPECT_FALSE(system_snapshot().MachineDescription().empty()); EXPECT_FALSE(system_snapshot().MachineDescription().empty());
} }
class ScopedSetTZ {
public:
ScopedSetTZ(const std::string& tz) {
const char* old_tz = getenv(kTZ);
old_tz_set_ = old_tz;
if (old_tz_set_) {
old_tz_.assign(old_tz);
}
EXPECT_EQ(setenv(kTZ, tz.c_str(), 1), 0) << ErrnoMessage("setenv");
tzset();
}
~ScopedSetTZ() {
if (old_tz_set_) {
EXPECT_EQ(setenv(kTZ, old_tz_.c_str(), 1), 0) << ErrnoMessage("setenv");
} else {
EXPECT_EQ(unsetenv(kTZ), 0) << ErrnoMessage("unsetenv");
}
tzset();
}
private:
std::string old_tz_;
bool old_tz_set_;
static constexpr char kTZ[] = "TZ";
DISALLOW_COPY_AND_ASSIGN(ScopedSetTZ);
};
constexpr char ScopedSetTZ::kTZ[];
TEST_F(SystemSnapshotMacTest, TimeZone) {
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| gives seconds east of UTC, and |timezone| gives
// seconds west of UTC.
EXPECT_EQ(standard_offset_seconds, -timezone);
// In contemporary usage, most time zones have an integer hour offset from
// UTC, although several are at a half-hour offset, and two are at 15-minute
// offsets. Throughout history, other variations existed. See
// http://www.timeanddate.com/time/time-zones-interesting.html.
EXPECT_EQ(standard_offset_seconds % (15 * 60), 0)
<< "standard_offset_seconds " << standard_offset_seconds;
if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) {
EXPECT_EQ(daylight_offset_seconds, standard_offset_seconds);
EXPECT_EQ(daylight_name, standard_name);
} else {
EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0)
<< "daylight_offset_seconds " << daylight_offset_seconds;
// In contemporary usage, dst_delta_seconds will almost always be one hour,
// except for Lord Howe Island, Australia, which uses a 30-minute
// delta. Throughout history, other variations existed. See
// http://www.timeanddate.com/time/dst/#brief.
int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds;
if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) {
FAIL() << "dst_delta_seconds " << dst_delta_seconds;
}
EXPECT_NE(standard_name, daylight_name);
}
// Test a variety of time zones. Some of these observe daylight saving time,
// some dont. Some used to but no longer do. Some have uncommon UTC offsets.
// standard_name and daylight_name can be nullptr where no name exists to
// verify, as may happen when some versions of the timezone database carry
// invented names and others do not.
static constexpr struct {
const char* tz;
bool observes_dst;
float standard_offset_hours;
float daylight_offset_hours;
const char* standard_name;
const char* daylight_name;
} kTestTimeZones[] = {
{"America/Anchorage", true, -9, -8, "AKST", "AKDT"},
{"America/Chicago", true, -6, -5, "CST", "CDT"},
{"America/Denver", true, -7, -6, "MST", "MDT"},
{"America/Halifax", true, -4, -3, "AST", "ADT"},
{"America/Los_Angeles", true, -8, -7, "PST", "PDT"},
{"America/New_York", true, -5, -4, "EST", "EDT"},
{"America/Phoenix", false, -7, -7, "MST", "MST"},
{"Asia/Karachi", false, 5, 5, "PKT", "PKT"},
{"Asia/Kolkata", false, 5.5, 5.5, "IST", "IST"},
{"Asia/Shanghai", false, 8, 8, "CST", "CST"},
{"Asia/Tokyo", false, 9, 9, "JST", "JST"},
{"Australia/Adelaide", true, 9.5, 10.5, "ACST", "ACDT"},
{"Australia/Brisbane", false, 10, 10, "AEST", "AEST"},
{"Australia/Darwin", false, 9.5, 9.5, "ACST", "ACST"},
{"Australia/Eucla", false, 8.75, 8.75, nullptr, nullptr},
{"Australia/Lord_Howe", true, 10.5, 11, nullptr, nullptr},
{"Australia/Perth", false, 8, 8, "AWST", "AWST"},
{"Australia/Sydney", true, 10, 11, "AEST", "AEDT"},
{"Europe/Bucharest", true, 2, 3, "EET", "EEST"},
{"Europe/London", true, 0, 1, "GMT", "BST"},
{"Europe/Moscow", false, 3, 3, "MSK", "MSK"},
{"Europe/Paris", true, 1, 2, "CET", "CEST"},
{"Europe/Reykjavik", false, 0, 0, "UTC", "UTC"},
{"Pacific/Auckland", true, 12, 13, "NZST", "NZDT"},
{"Pacific/Honolulu", false, -10, -10, "HST", "HST"},
{"UTC", false, 0, 0, "UTC", "UTC"},
};
for (size_t index = 0; index < arraysize(kTestTimeZones); ++index) {
const auto& test_time_zone = kTestTimeZones[index];
const char* tz = test_time_zone.tz;
SCOPED_TRACE(base::StringPrintf("index %zu, tz %s", index, tz));
{
ScopedSetTZ set_tz(tz);
system_snapshot().TimeZone(&dst_status,
&standard_offset_seconds,
&daylight_offset_seconds,
&standard_name,
&daylight_name);
}
EXPECT_EQ(dst_status != SystemSnapshot::kDoesNotObserveDaylightSavingTime,
test_time_zone.observes_dst);
EXPECT_EQ(standard_offset_seconds,
test_time_zone.standard_offset_hours * 60 * 60);
EXPECT_EQ(daylight_offset_seconds,
test_time_zone.daylight_offset_hours * 60 * 60);
if (test_time_zone.standard_name) {
EXPECT_EQ(standard_name, test_time_zone.standard_name);
}
if (test_time_zone.daylight_name) {
EXPECT_EQ(daylight_name, test_time_zone.daylight_name);
}
}
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

118
snapshot/posix/timezone.cc Normal file
View File

@ -0,0 +1,118 @@
// Copyright 2017 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/posix/timezone.h"
#include <stddef.h>
#include <time.h>
#include "base/logging.h"
#include "build/build_config.h"
namespace crashpad {
namespace internal {
void TimeZone(const timeval& snapshot_time,
SystemSnapshot::DaylightSavingTimeStatus* dst_status,
int* standard_offset_seconds,
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) {
tzset();
tm local;
PCHECK(localtime_r(&snapshot_time.tv_sec, &local)) << "localtime_r";
*standard_name = tzname[0];
bool found_transition = false;
long probe_gmtoff = local.tm_gmtoff;
#if defined(OS_ANDROID)
// Some versions of the timezone database on Android have incorrect
// information (e.g. Asia/Kolkata and Pacific/Honolulu). These timezones set
// daylight to a non-zero value and return incorrect, >= 0 values for tm_isdst
// in the probes below. If tzname[1] is set to a bogus value, assume the
// timezone does not actually use daylight saving time.
if (daylight && strncmp(tzname[1], "_TZif", 5) != 0) {
#else
if (daylight) {
#endif
// 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|. Its possible that no such instance will be found even with
// |daylight| set. This can happen in locations where daylight saving time
// was once observed or is expected to be observed in the future, but where
// no transitions to or from daylight saving time occurred or will occur
// within a year of the current date. Arizona, which last observed daylight
// saving time in 1967, is an example.
static constexpr 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) && !found_transition;
++index) {
// Look at a 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 = std::min(local.tm_mday, 28);
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) {
PLOG(WARNING) << "mktime";
continue;
}
if (probe_tm.tm_isdst < 0 || local.tm_isdst < 0) {
LOG(WARNING) << "dst status not available";
continue;
}
if (probe_tm.tm_isdst != local.tm_isdst) {
found_transition = true;
probe_gmtoff = probe_tm.tm_gmtoff;
}
}
}
if (found_transition) {
*daylight_name = tzname[1];
if (!local.tm_isdst) {
*dst_status = SystemSnapshot::kObservingStandardTime;
*standard_offset_seconds = local.tm_gmtoff;
*daylight_offset_seconds = probe_gmtoff;
} else {
*dst_status = SystemSnapshot::kObservingDaylightSavingTime;
*standard_offset_seconds = probe_gmtoff;
*daylight_offset_seconds = local.tm_gmtoff;
}
} else {
*daylight_name = tzname[0];
*dst_status = SystemSnapshot::kDoesNotObserveDaylightSavingTime;
#if defined(OS_ANDROID)
// timezone is more reliably set correctly on Android.
*standard_offset_seconds = -timezone;
*daylight_offset_seconds = -timezone;
#else
*standard_offset_seconds = local.tm_gmtoff;
*daylight_offset_seconds = local.tm_gmtoff;
#endif // OS_ANDROID
}
}
} // namespace internal
} // namespace crashpad

55
snapshot/posix/timezone.h Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2017 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_POSIX_TIMEZONE_H_
#define CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_
#include <sys/time.h>
#include <string>
#include "snapshot/system_snapshot.h"
namespace crashpad {
namespace internal {
//! \brief Returns time zone information from the snapshot system, based on
//! its locale configuration and \a snapshot_time.
//!
//! \param[in] snapshot_time The time to use collect daylight saving time status
//! for, given in time since Epoch.
//! \param[out] dst_status Whether the location observes daylight saving time,
//! and if so, whether it or standard time is currently being observed.
//! \param[out] standard_offset_seconds The number of seconds that the
//! locations time zone is east (ahead) of UTC during standard time.
//! \param[out] daylight_offset_seconds The number of seconds that the
//! locations time zone is east (ahead) of UTC during daylight saving.
//! time.
//! \param[out] standard_name The name of the time zone while standard time is
//! being observed.
//! \param[out] daylight_name The name of the time zone while daylight saving
//! time is being observed.
//!
//! \sa SystemSnapshot::TimeZone
void TimeZone(const timeval& snapshot_time,
SystemSnapshot::DaylightSavingTimeStatus* dst_status,
int* standard_offset_seconds,
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name);
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_

View File

@ -0,0 +1,196 @@
// Copyright 2017 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/posix/timezone.h"
#include <stdlib.h>
#include <sys/cdefs.h>
#include <sys/time.h>
#include <time.h>
#include <string>
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "test/errors.h"
namespace crashpad {
namespace test {
namespace {
class ScopedSetTZ {
public:
ScopedSetTZ(const std::string& tz) {
const char* old_tz = getenv(kTZ);
old_tz_set_ = old_tz;
if (old_tz_set_) {
old_tz_.assign(old_tz);
}
EXPECT_EQ(setenv(kTZ, tz.c_str(), 1), 0) << ErrnoMessage("setenv");
tzset();
}
~ScopedSetTZ() {
if (old_tz_set_) {
EXPECT_EQ(setenv(kTZ, old_tz_.c_str(), 1), 0) << ErrnoMessage("setenv");
} else {
EXPECT_EQ(unsetenv(kTZ), 0) << ErrnoMessage("unsetenv");
}
tzset();
}
private:
std::string old_tz_;
bool old_tz_set_;
static constexpr char kTZ[] = "TZ";
DISALLOW_COPY_AND_ASSIGN(ScopedSetTZ);
};
constexpr char ScopedSetTZ::kTZ[];
TEST(TimeZone, Basic) {
SystemSnapshot::DaylightSavingTimeStatus dst_status;
int standard_offset_seconds;
int daylight_offset_seconds;
std::string standard_name;
std::string daylight_name;
timeval snapshot_time;
ASSERT_EQ(gettimeofday(&snapshot_time, nullptr), 0);
internal::TimeZone(snapshot_time,
&dst_status,
&standard_offset_seconds,
&daylight_offset_seconds,
&standard_name,
&daylight_name);
// |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives
// seconds west of UTC.
EXPECT_EQ(standard_offset_seconds, -timezone);
// In contemporary usage, most time zones have an integer hour offset from
// UTC, although several are at a half-hour offset, and two are at 15-minute
// offsets. Throughout history, other variations existed. See
// http://www.timeanddate.com/time/time-zones-interesting.html.
EXPECT_EQ(standard_offset_seconds % (15 * 60), 0)
<< "standard_offset_seconds " << standard_offset_seconds;
if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) {
EXPECT_EQ(daylight_offset_seconds, standard_offset_seconds);
EXPECT_EQ(daylight_name, standard_name);
} else {
EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0)
<< "daylight_offset_seconds " << daylight_offset_seconds;
// In contemporary usage, dst_delta_seconds will almost always be one hour,
// except for Lord Howe Island, Australia, which uses a 30-minute
// delta. Throughout history, other variations existed. See
// http://www.timeanddate.com/time/dst/#brief.
int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds;
if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) {
FAIL() << "dst_delta_seconds " << dst_delta_seconds;
}
EXPECT_NE(standard_name, daylight_name);
}
// Test a variety of time zones. Some of these observe daylight saving time,
// some dont. Some used to but no longer do. Some have uncommon UTC offsets.
// standard_name and daylight_name can be nullptr where no name exists to
// verify, as may happen when some versions of the timezone database carry
// invented names and others do not.
static constexpr struct {
const char* tz;
bool observes_dst;
float standard_offset_hours;
float daylight_offset_hours;
const char* standard_name;
const char* daylight_name;
} kTestTimeZones[] = {
{"America/Anchorage", true, -9, -8, "AKST", "AKDT"},
{"America/Chicago", true, -6, -5, "CST", "CDT"},
{"America/Denver", true, -7, -6, "MST", "MDT"},
{"America/Halifax", true, -4, -3, "AST", "ADT"},
{"America/Los_Angeles", true, -8, -7, "PST", "PDT"},
{"America/New_York", true, -5, -4, "EST", "EDT"},
{"America/Phoenix", false, -7, -7, "MST", "MST"},
{"Asia/Karachi", false, 5, 5, "PKT", "PKT"},
{"Asia/Kolkata", false, 5.5, 5.5, "IST", "IST"},
{"Asia/Shanghai", false, 8, 8, "CST", "CST"},
{"Asia/Tokyo", false, 9, 9, "JST", "JST"},
// Australian timezone names have an optional "A" prefix, which is
// present for glibc and macOS, but missing on Android.
{"Australia/Adelaide", true, 9.5, 10.5, nullptr, nullptr},
{"Australia/Brisbane", false, 10, 10, nullptr, nullptr},
{"Australia/Darwin", false, 9.5, 9.5, nullptr, nullptr},
{"Australia/Eucla", false, 8.75, 8.75, nullptr, nullptr},
{"Australia/Lord_Howe", true, 10.5, 11, nullptr, nullptr},
{"Australia/Perth", false, 8, 8, nullptr, nullptr},
{"Australia/Sydney", true, 10, 11, nullptr, nullptr},
{"Europe/Bucharest", true, 2, 3, "EET", "EEST"},
{"Europe/London", true, 0, 1, "GMT", "BST"},
{"Europe/Paris", true, 1, 2, "CET", "CEST"},
{"Europe/Reykjavik", false, 0, 0, nullptr, nullptr},
{"Pacific/Auckland", true, 12, 13, "NZST", "NZDT"},
{"Pacific/Honolulu", false, -10, -10, "HST", "HST"},
{"UTC", false, 0, 0, "UTC", "UTC"},
};
for (size_t index = 0; index < arraysize(kTestTimeZones); ++index) {
const auto& test_time_zone = kTestTimeZones[index];
const char* tz = test_time_zone.tz;
SCOPED_TRACE(base::StringPrintf("index %zu, tz %s", index, tz));
{
ScopedSetTZ set_tz(tz);
internal::TimeZone(snapshot_time,
&dst_status,
&standard_offset_seconds,
&daylight_offset_seconds,
&standard_name,
&daylight_name);
}
EXPECT_PRED2(
[](SystemSnapshot::DaylightSavingTimeStatus dst, bool observes) {
return (dst != SystemSnapshot::kDoesNotObserveDaylightSavingTime) ==
observes;
},
dst_status,
test_time_zone.observes_dst);
EXPECT_EQ(standard_offset_seconds,
test_time_zone.standard_offset_hours * 60 * 60);
EXPECT_EQ(daylight_offset_seconds,
test_time_zone.daylight_offset_hours * 60 * 60);
if (test_time_zone.standard_name) {
EXPECT_EQ(standard_name, test_time_zone.standard_name);
}
if (test_time_zone.daylight_name) {
EXPECT_EQ(daylight_name, test_time_zone.daylight_name);
}
}
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -57,6 +57,8 @@
'linux/process_reader.cc', 'linux/process_reader.cc',
'linux/process_reader.h', 'linux/process_reader.h',
'linux/signal_context.h', 'linux/signal_context.h',
'linux/system_snapshot_linux.cc',
'linux/system_snapshot_linux.h',
'linux/thread_snapshot_linux.cc', 'linux/thread_snapshot_linux.cc',
'linux/thread_snapshot_linux.h', 'linux/thread_snapshot_linux.h',
'mac/cpu_context_mac.cc', 'mac/cpu_context_mac.cc',
@ -107,6 +109,8 @@
'minidump/process_snapshot_minidump.cc', 'minidump/process_snapshot_minidump.cc',
'minidump/process_snapshot_minidump.h', 'minidump/process_snapshot_minidump.h',
'module_snapshot.h', 'module_snapshot.h',
'posix/timezone.cc',
'posix/timezone.h',
'process_snapshot.h', 'process_snapshot.h',
'system_snapshot.h', 'system_snapshot.h',
'thread_snapshot.h', 'thread_snapshot.h',
@ -140,6 +144,8 @@
'win/system_snapshot_win.h', 'win/system_snapshot_win.h',
'win/thread_snapshot_win.cc', 'win/thread_snapshot_win.cc',
'win/thread_snapshot_win.h', 'win/thread_snapshot_win.h',
'x86/cpuid_reader.cc',
'x86/cpuid_reader.h',
], ],
'conditions': [ 'conditions': [
['OS=="win"', { ['OS=="win"', {
@ -155,6 +161,11 @@
'capture_memory.h', 'capture_memory.h',
], ],
}], }],
['target_arch!="ia32" and target_arch!="x64"', {
'sources/': [
['exclude', '^x86/'],
],
}],
], ],
'target_conditions': [ 'target_conditions': [
['OS=="android"', { ['OS=="android"', {

View File

@ -74,6 +74,7 @@
'linux/elf_image_reader_test.cc', 'linux/elf_image_reader_test.cc',
'linux/exception_snapshot_linux_test.cc', 'linux/exception_snapshot_linux_test.cc',
'linux/process_reader_test.cc', 'linux/process_reader_test.cc',
'linux/system_snapshot_linux_test.cc',
'mac/cpu_context_mac_test.cc', 'mac/cpu_context_mac_test.cc',
'mac/mach_o_image_annotations_reader_test.cc', 'mac/mach_o_image_annotations_reader_test.cc',
'mac/mach_o_image_reader_test.cc', 'mac/mach_o_image_reader_test.cc',
@ -82,6 +83,7 @@
'mac/process_types_test.cc', 'mac/process_types_test.cc',
'mac/system_snapshot_mac_test.cc', 'mac/system_snapshot_mac_test.cc',
'minidump/process_snapshot_minidump_test.cc', 'minidump/process_snapshot_minidump_test.cc',
'posix/timezone_test.cc',
'win/cpu_context_win_test.cc', 'win/cpu_context_win_test.cc',
'win/exception_snapshot_win_test.cc', 'win/exception_snapshot_win_test.cc',
'win/extra_memory_ranges_test.cc', 'win/extra_memory_ranges_test.cc',

View File

@ -41,6 +41,12 @@ class SystemSnapshot {
//! \brief Windows. //! \brief Windows.
kOperatingSystemWindows, kOperatingSystemWindows,
//! \brief Linux.
kOperatingSystemLinux,
//! \brief Android.
kOperatingSystemAndroid,
}; };
//! \brief A systems daylight saving time status. //! \brief A systems daylight saving time status.

View File

@ -0,0 +1,132 @@
// Copyright 2017 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/x86/cpuid_reader.h"
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#if defined(OS_WIN)
#include <immintrin.h>
#include <intrin.h>
#endif // OS_WIN
namespace crashpad {
namespace internal {
CpuidReader::CpuidReader()
: features_(0),
extended_features_(0),
vendor_(),
max_leaf_(0),
signature_(0) {
uint32_t cpuinfo[4];
Cpuid(cpuinfo, 0);
max_leaf_ = cpuinfo[0];
vendor_.append(reinterpret_cast<char*>(&cpuinfo[1]), 4);
vendor_.append(reinterpret_cast<char*>(&cpuinfo[3]), 4);
vendor_.append(reinterpret_cast<char*>(&cpuinfo[2]), 4);
Cpuid(cpuinfo, 1);
signature_ = cpuinfo[0];
features_ = (static_cast<uint64_t>(cpuinfo[2]) << 32) |
static_cast<uint64_t>(cpuinfo[3]);
Cpuid(cpuinfo, 0x80000001);
extended_features_ = (static_cast<uint64_t>(cpuinfo[2]) << 32) |
static_cast<uint64_t>(cpuinfo[3]);
}
CpuidReader::~CpuidReader() {}
uint32_t CpuidReader::Revision() const {
uint8_t stepping = signature_ & 0xf;
uint8_t model = (signature_ & 0xf0) >> 4;
uint8_t family = (signature_ & 0xf00) >> 8;
uint8_t extended_model = static_cast<uint8_t>((signature_ & 0xf0000) >> 16);
uint16_t extended_family = (signature_ & 0xff00000) >> 20;
// For families before 15, extended_family are simply reserved bits.
if (family < 15)
extended_family = 0;
// extended_model is only used for families 6 and 15.
if (family != 6 && family != 15)
extended_model = 0;
uint16_t adjusted_family = family + extended_family;
uint8_t adjusted_model = model + (extended_model << 4);
return (adjusted_family << 16) | (adjusted_model << 8) | stepping;
}
uint32_t CpuidReader::Leaf7Features() const {
if (max_leaf_ < 7) {
return 0;
}
uint32_t cpuinfo[4];
Cpuid(cpuinfo, 7);
return cpuinfo[1];
}
bool CpuidReader::SupportsDAZ() const {
// 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.
// Test for fxsave support.
if (!(features_ & (UINT64_C(1) << 24))) {
return false;
}
#if defined(ARCH_CPU_X86)
using Fxsave = CPUContextX86::Fxsave;
#elif defined(ARCH_CPU_X86_64)
using Fxsave = CPUContextX86_64::Fxsave;
#endif
#if defined(OS_WIN)
__declspec(align(16)) Fxsave fxsave = {};
#else
Fxsave fxsave __attribute__((aligned(16))) = {};
#endif
static_assert(sizeof(fxsave) == 512, "fxsave size");
static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28,
"mxcsr_mask offset");
#if defined(OS_WIN)
_fxsave(&fxsave);
#else
asm("fxsave %0" : "=m"(fxsave));
#endif
// Test the DAZ bit.
return (fxsave.mxcsr_mask & (1 << 6)) != 0;
}
void CpuidReader::Cpuid(uint32_t cpuinfo[4], uint32_t leaf) const {
#if defined(OS_WIN)
__cpuid(reinterpret_cast<int*>(cpuinfo), leaf);
#else
asm("cpuid"
: "=a"(cpuinfo[0]), "=b"(cpuinfo[1]), "=c"(cpuinfo[2]), "=d"(cpuinfo[3])
: "a"(leaf), "b"(0), "c"(0), "d"(0));
#endif // OS_WIN
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,63 @@
// Copyright 2017 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 <stdint.h>
#include <string>
namespace crashpad {
namespace internal {
//! \brief Reads x86-family CPU information by calling `cpuid`.
class CpuidReader {
public:
CpuidReader();
~CpuidReader();
//! \see SystemSnapshot::CPURevision
uint32_t Revision() const;
//! \see SystemSnapshot::CPUVendor
std::string Vendor() const { return vendor_; }
//! \see SystemSnapshot::CPUX86Signature
uint32_t Signature() const { return signature_; }
//! \see SystemSnapshot::CPUX86Features
uint64_t Features() const { return features_; }
//! \see SystemSnapshot::CPUX86ExtendedFeatures
uint64_t ExtendedFeatures() const { return extended_features_; }
//! \see SystemSnapshot::CPUX86Leaf7Features
uint32_t Leaf7Features() const;
//! \see SystemSnapshot::NXEnabled
bool NXEnabled() const { return (ExtendedFeatures() & (1 << 20)) != 0; }
//! \see SystemSnapshot::CPUX86SupportsDAZ
bool SupportsDAZ() const;
private:
void Cpuid(uint32_t cpuinfo[4], uint32_t leaf) const;
uint64_t features_;
uint64_t extended_features_;
std::string vendor_;
uint32_t max_leaf_;
uint32_t signature_;
};
} // namespace internal
} // namespace crashpad