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>
diff --git a/build/gyp_crashpad_android.py b/build/gyp_crashpad_android.py
index a6ad1b4..4461425 100755
--- a/build/gyp_crashpad_android.py
+++ b/build/gyp_crashpad_android.py
@@ -46,8 +46,8 @@
ARCH_TRIPLET_TO_ARCH = {
'arm-linux-androideabi': 'arm',
'aarch64-linux-android': 'arm64',
- 'i686-linux-android': 'x86',
- 'x86_64-linux-android': 'x86_64',
+ 'i686-linux-android': 'ia32',
+ 'x86_64-linux-android': 'x64',
'mipsel-linux-android': 'mips',
'mips64el-linux-android': 'mips64',
}
diff --git a/snapshot/linux/system_snapshot_linux.cc b/snapshot/linux/system_snapshot_linux.cc
new file mode 100644
index 0000000..f3ed99b
--- /dev/null
+++ b/snapshot/linux/system_snapshot_linux.cc
@@ -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
diff --git a/snapshot/linux/system_snapshot_linux.h b/snapshot/linux/system_snapshot_linux.h
new file mode 100644
index 0000000..a991450
--- /dev/null
+++ b/snapshot/linux/system_snapshot_linux.h
@@ -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 it’s 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_
diff --git a/snapshot/linux/system_snapshot_linux_test.cc b/snapshot/linux/system_snapshot_linux_test.cc
new file mode 100644
index 0000000..f55036e
--- /dev/null
+++ b/snapshot/linux/system_snapshot_linux_test.cc
@@ -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(¤t_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
diff --git a/snapshot/mac/system_snapshot_mac.cc b/snapshot/mac/system_snapshot_mac.cc
index 2bafb27..140e7f4 100644
--- a/snapshot/mac/system_snapshot_mac.cc
+++ b/snapshot/mac/system_snapshot_mac.cc
@@ -18,7 +18,6 @@
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/utsname.h>
-#include <time.h>
#include <algorithm>
@@ -27,6 +26,7 @@
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#include "snapshot/mac/process_reader.h"
+#include "snapshot/posix/timezone.h"
#include "util/mac/mac_util.h"
#include "util/numeric/in_range_cast.h"
@@ -348,64 +348,12 @@
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];
-
- bool found_transition = false;
- 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|. It’s 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;
- }
+ internal::TimeZone(*snapshot_time_,
+ dst_status,
+ standard_offset_seconds,
+ daylight_offset_seconds,
+ standard_name,
+ daylight_name);
}
} // namespace internal
diff --git a/snapshot/mac/system_snapshot_mac_test.cc b/snapshot/mac/system_snapshot_mac_test.cc
index fb4166f..646021b 100644
--- a/snapshot/mac/system_snapshot_mac_test.cc
+++ b/snapshot/mac/system_snapshot_mac_test.cc
@@ -14,13 +14,10 @@
#include "snapshot/mac/system_snapshot_mac.h"
-#include <stdlib.h>
#include <sys/time.h>
-#include <time.h>
#include <string>
-#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "snapshot/mac/process_reader.h"
@@ -129,152 +126,6 @@
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 don’t. 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 test
} // namespace crashpad
diff --git a/snapshot/posix/timezone.cc b/snapshot/posix/timezone.cc
new file mode 100644
index 0000000..47c6ecf
--- /dev/null
+++ b/snapshot/posix/timezone.cc
@@ -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|. It’s 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
diff --git a/snapshot/posix/timezone.h b/snapshot/posix/timezone.h
new file mode 100644
index 0000000..bd404a3
--- /dev/null
+++ b/snapshot/posix/timezone.h
@@ -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
+//! location’s time zone is east (ahead) of UTC during standard time.
+//! \param[out] daylight_offset_seconds The number of seconds that the
+//! location’s 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_
diff --git a/snapshot/posix/timezone_test.cc b/snapshot/posix/timezone_test.cc
new file mode 100644
index 0000000..01bdff5
--- /dev/null
+++ b/snapshot/posix/timezone_test.cc
@@ -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 don’t. 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
diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp
index 24ab7a7..1c63be2 100644
--- a/snapshot/snapshot.gyp
+++ b/snapshot/snapshot.gyp
@@ -57,6 +57,8 @@
'linux/process_reader.cc',
'linux/process_reader.h',
'linux/signal_context.h',
+ 'linux/system_snapshot_linux.cc',
+ 'linux/system_snapshot_linux.h',
'linux/thread_snapshot_linux.cc',
'linux/thread_snapshot_linux.h',
'mac/cpu_context_mac.cc',
@@ -107,6 +109,8 @@
'minidump/process_snapshot_minidump.cc',
'minidump/process_snapshot_minidump.h',
'module_snapshot.h',
+ 'posix/timezone.cc',
+ 'posix/timezone.h',
'process_snapshot.h',
'system_snapshot.h',
'thread_snapshot.h',
@@ -140,6 +144,8 @@
'win/system_snapshot_win.h',
'win/thread_snapshot_win.cc',
'win/thread_snapshot_win.h',
+ 'x86/cpuid_reader.cc',
+ 'x86/cpuid_reader.h',
],
'conditions': [
['OS=="win"', {
@@ -155,6 +161,11 @@
'capture_memory.h',
],
}],
+ ['target_arch!="ia32" and target_arch!="x64"', {
+ 'sources/': [
+ ['exclude', '^x86/'],
+ ],
+ }],
],
'target_conditions': [
['OS=="android"', {
diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp
index d81c4b5..80a66f4 100644
--- a/snapshot/snapshot_test.gyp
+++ b/snapshot/snapshot_test.gyp
@@ -74,6 +74,7 @@
'linux/elf_image_reader_test.cc',
'linux/exception_snapshot_linux_test.cc',
'linux/process_reader_test.cc',
+ 'linux/system_snapshot_linux_test.cc',
'mac/cpu_context_mac_test.cc',
'mac/mach_o_image_annotations_reader_test.cc',
'mac/mach_o_image_reader_test.cc',
@@ -82,6 +83,7 @@
'mac/process_types_test.cc',
'mac/system_snapshot_mac_test.cc',
'minidump/process_snapshot_minidump_test.cc',
+ 'posix/timezone_test.cc',
'win/cpu_context_win_test.cc',
'win/exception_snapshot_win_test.cc',
'win/extra_memory_ranges_test.cc',
diff --git a/snapshot/system_snapshot.h b/snapshot/system_snapshot.h
index 549f0b6..6da3d37 100644
--- a/snapshot/system_snapshot.h
+++ b/snapshot/system_snapshot.h
@@ -41,6 +41,12 @@
//! \brief Windows.
kOperatingSystemWindows,
+
+ //! \brief Linux.
+ kOperatingSystemLinux,
+
+ //! \brief Android.
+ kOperatingSystemAndroid,
};
//! \brief A system’s daylight saving time status.
diff --git a/snapshot/x86/cpuid_reader.cc b/snapshot/x86/cpuid_reader.cc
new file mode 100644
index 0000000..ccedf92
--- /dev/null
+++ b/snapshot/x86/cpuid_reader.cc
@@ -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 Developer’s
+ // 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
diff --git a/snapshot/x86/cpuid_reader.h b/snapshot/x86/cpuid_reader.h
new file mode 100644
index 0000000..0fd02cf
--- /dev/null
+++ b/snapshot/x86/cpuid_reader.h
@@ -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