mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +08:00
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:
parent
f16e4eb9ff
commit
b953388b95
@ -46,8 +46,8 @@ def main(args):
|
||||
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',
|
||||
}
|
||||
|
397
snapshot/linux/system_snapshot_linux.cc
Normal file
397
snapshot/linux/system_snapshot_linux.cc
Normal 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
|
112
snapshot/linux/system_snapshot_linux.h
Normal file
112
snapshot/linux/system_snapshot_linux.h
Normal 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 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_
|
85
snapshot/linux/system_snapshot_linux_test.cc
Normal file
85
snapshot/linux/system_snapshot_linux_test.cc
Normal 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(¤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
|
@ -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 @@ void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,
|
||||
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
|
||||
|
@ -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 @@ TEST_F(SystemSnapshotMacTest, MachineDescription) {
|
||||
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
|
||||
|
118
snapshot/posix/timezone.cc
Normal file
118
snapshot/posix/timezone.cc
Normal 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|. 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
|
55
snapshot/posix/timezone.h
Normal file
55
snapshot/posix/timezone.h
Normal 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
|
||||
//! 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_
|
196
snapshot/posix/timezone_test.cc
Normal file
196
snapshot/posix/timezone_test.cc
Normal 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 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
|
@ -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"', {
|
||||
|
@ -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',
|
||||
|
@ -41,6 +41,12 @@ class SystemSnapshot {
|
||||
|
||||
//! \brief Windows.
|
||||
kOperatingSystemWindows,
|
||||
|
||||
//! \brief Linux.
|
||||
kOperatingSystemLinux,
|
||||
|
||||
//! \brief Android.
|
||||
kOperatingSystemAndroid,
|
||||
};
|
||||
|
||||
//! \brief A system’s daylight saving time status.
|
||||
|
132
snapshot/x86/cpuid_reader.cc
Normal file
132
snapshot/x86/cpuid_reader.cc
Normal 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 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
|
63
snapshot/x86/cpuid_reader.h
Normal file
63
snapshot/x86/cpuid_reader.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user