Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 | |
| 15 #include "snapshot/system_snapshot_mac.h" | |
| 16 | |
| 17 #include <sys/sysctl.h> | |
| 18 #include <sys/types.h> | |
| 19 #include <sys/utsname.h> | |
| 20 #include <time.h> | |
| 21 | |
| 22 #include "base/logging.h" | |
| 23 #include "base/strings/stringprintf.h" | |
| 24 #include "build/build_config.h" | |
| 25 #include "util/mac/mac_util.h" | |
| 26 #include "util/mac/process_reader.h" | |
| 27 #include "util/numeric/in_range_cast.h" | |
| 28 #include "util/numeric/int128.h" | |
| 29 | |
| 30 namespace crashpad { | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 template <typename T> | |
| 35 T ReadIntSysctlByName(const char* name, T default_value) { | |
| 36 T value; | |
| 37 size_t value_len = sizeof(value); | |
| 38 if (sysctlbyname(name, &value, &value_len, NULL, 0) != 0) { | |
| 39 PLOG(WARNING) << "sysctlbyname " << name; | |
| 40 return default_value; | |
| 41 } | |
| 42 | |
| 43 return value; | |
| 44 } | |
| 45 | |
| 46 template <typename T> | |
| 47 T CastIntSysctlByName(const char* name, T default_value) { | |
| 48 int int_value = ReadIntSysctlByName<int>(name, default_value); | |
| 49 return InRangeCast<T>(int_value, default_value); | |
| 50 } | |
| 51 | |
| 52 std::string ReadStringSysctlByName(const char* name) { | |
| 53 size_t buf_len; | |
| 54 if (sysctlbyname(name, NULL, &buf_len, NULL, 0) != 0) { | |
| 55 PLOG(WARNING) << "sysctlbyname (size) " << name; | |
| 56 return std::string(); | |
| 57 } | |
| 58 | |
| 59 if (buf_len == 0) { | |
| 60 return std::string(); | |
| 61 } | |
| 62 | |
| 63 std::string value(buf_len - 1, '\0'); | |
| 64 if (sysctlbyname(name, &value[0], &buf_len, NULL, 0) != 0) { | |
| 65 PLOG(WARNING) << "sysctlbyname " << name; | |
| 66 return std::string(); | |
| 67 } | |
| 68 | |
| 69 return value; | |
| 70 } | |
| 71 | |
| 72 #if defined(ARCH_CPU_X86_FAMILY) | |
| 73 void CallCPUID(uint32_t leaf, | |
| 74 uint32_t* eax, | |
| 75 uint32_t* ebx, | |
| 76 uint32_t* ecx, | |
| 77 uint32_t* edx) { | |
| 78 asm("cpuid" | |
| 79 : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) | |
| 80 : "a"(leaf), "b"(0), "c"(0), "d"(0)); | |
| 81 } | |
| 82 #endif | |
| 83 | |
| 84 } // namespace | |
| 85 | |
| 86 namespace internal { | |
| 87 | |
| 88 SystemSnapshotMac::SystemSnapshotMac() | |
| 89 : SystemSnapshot(), | |
| 90 os_version_full_(), | |
| 91 os_version_build_(), | |
| 92 process_reader_(NULL), | |
| 93 snapshot_time_(NULL), | |
| 94 os_version_major_(0), | |
| 95 os_version_minor_(0), | |
| 96 os_version_bugfix_(0), | |
| 97 os_server_(false), | |
| 98 initialized_() { | |
| 99 } | |
| 100 | |
| 101 SystemSnapshotMac::~SystemSnapshotMac() { | |
| 102 } | |
| 103 | |
| 104 void SystemSnapshotMac::Initialize(ProcessReader* process_reader, | |
| 105 const timeval* snapshot_time) { | |
| 106 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | |
| 107 | |
| 108 process_reader_ = process_reader; | |
| 109 snapshot_time_ = snapshot_time; | |
| 110 | |
| 111 // MacOSXVersion() logs its own warnings if it can’t figure anything out. It’s | |
| 112 // not fatal if this happens. The default values are reasonable. | |
| 113 std::string os_version_string; | |
| 114 MacOSXVersion(&os_version_major_, | |
| 115 &os_version_minor_, | |
| 116 &os_version_bugfix_, | |
| 117 &os_version_build_, | |
| 118 &os_server_, | |
| 119 &os_version_string); | |
| 120 | |
| 121 std::string uname_string; | |
| 122 utsname uts; | |
| 123 if (uname(&uts) != 0) { | |
| 124 PLOG(WARNING) << "uname"; | |
| 125 } else { | |
| 126 uname_string = base::StringPrintf( | |
| 127 "%s %s %s %s", uts.sysname, uts.release, uts.version, uts.machine); | |
| 128 } | |
| 129 | |
| 130 if (!os_version_string.empty()) { | |
| 131 if (!uname_string.empty()) { | |
| 132 os_version_full_ = base::StringPrintf( | |
| 133 "%s; %s", os_version_string.c_str(), uname_string.c_str()); | |
| 134 } else { | |
| 135 os_version_full_ = os_version_string; | |
| 136 } | |
| 137 } else { | |
| 138 os_version_full_ = uname_string; | |
| 139 } | |
| 140 | |
| 141 INITIALIZATION_STATE_SET_VALID(initialized_); | |
| 142 } | |
| 143 | |
| 144 CPUArchitecture SystemSnapshotMac::GetCPUArchitecture() const { | |
| 145 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 146 | |
| 147 #if defined(ARCH_CPU_X86_FAMILY) | |
| 148 return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 | |
| 149 : kCPUArchitectureX86; | |
| 150 #endif | |
|
Robert Sesek
2014/10/03 18:05:45
#else NOTREACHED() or are you relying on the compi
Mark Mentovai
2014/10/03 18:34:57
Robert Sesek wrote:
| |
| 151 } | |
| 152 | |
| 153 uint32_t SystemSnapshotMac::CPURevision() const { | |
| 154 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 155 | |
| 156 #if defined(ARCH_CPU_X86_FAMILY) | |
| 157 // machdep.cpu.family and machdep.cpu.model already take the extended family | |
| 158 // and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c | |
| 159 // cpuid_set_generic_info(). | |
| 160 uint16_t family = CastIntSysctlByName<uint16_t>("machdep.cpu.family", 0); | |
| 161 uint8_t model = CastIntSysctlByName<uint8_t>("machdep.cpu.model", 0); | |
| 162 uint8_t stepping = CastIntSysctlByName<uint8_t>("machdep.cpu.stepping", 0); | |
| 163 | |
| 164 return (family << 16) | (model << 8) | stepping; | |
| 165 #endif | |
|
Robert Sesek
2014/10/03 18:05:44
#else NOTREACHED() ?
| |
| 166 } | |
| 167 | |
| 168 uint8_t SystemSnapshotMac::CPUCount() const { | |
| 169 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 170 return CastIntSysctlByName<uint8_t>("hw.ncpu", 1); | |
| 171 } | |
| 172 | |
| 173 std::string SystemSnapshotMac::CPUVendor() const { | |
| 174 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 175 | |
| 176 #if defined(ARCH_CPU_X86_FAMILY) | |
| 177 return ReadStringSysctlByName("machdep.cpu.vendor"); | |
| 178 #endif | |
| 179 } | |
| 180 | |
| 181 void SystemSnapshotMac::CPUFrequency( | |
| 182 uint64_t* current_hz, uint64_t* max_hz) const { | |
| 183 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 184 *current_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency", 0); | |
| 185 *max_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency_max", 0); | |
| 186 } | |
| 187 | |
| 188 uint32_t SystemSnapshotMac::CPUX86Signature() const { | |
| 189 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 190 | |
| 191 #if defined(ARCH_CPU_X86_FAMILY) | |
| 192 return ReadIntSysctlByName<uint32_t>("machdep.cpu.signature", 0); | |
| 193 #else | |
| 194 NOTREACHED(); | |
|
Mark Mentovai
2014/10/03 18:34:57
I did #else NOTREACHED() on the x86-family ones be
| |
| 195 return 0; | |
| 196 #endif | |
| 197 } | |
| 198 | |
| 199 uint64_t SystemSnapshotMac::CPUX86Features() const { | |
| 200 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 201 | |
| 202 #if defined(ARCH_CPU_X86_FAMILY) | |
| 203 return ReadIntSysctlByName<uint64_t>("machdep.cpu.feature_bits", 0); | |
| 204 #else | |
| 205 NOTREACHED(); | |
| 206 return 0; | |
| 207 #endif | |
| 208 } | |
| 209 | |
| 210 uint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const { | |
| 211 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 212 | |
| 213 #if defined(ARCH_CPU_X86_FAMILY) | |
| 214 return ReadIntSysctlByName<uint64_t>("machdep.cpu.extfeature_bits", 0); | |
| 215 #else | |
| 216 NOTREACHED(); | |
| 217 return 0; | |
| 218 #endif | |
| 219 } | |
| 220 | |
| 221 uint32_t SystemSnapshotMac::CPUX86Leaf7Features() const { | |
| 222 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 223 | |
| 224 #if defined(ARCH_CPU_X86_FAMILY) | |
| 225 // The machdep.cpu.leaf7_feature_bits sysctl isn’t supported prior to Mac OS X | |
| 226 // 10.7, so read this by calling cpuid directly. | |
| 227 // | |
| 228 // machdep.cpu.max_basic could be used to check whether to read the leaf, but | |
| 229 // that sysctl isn’t supported prior to Mac OS X 10.6, so read the maximum | |
| 230 // basic leaf by calling cpuid directly as well. All CPUs that Apple is known | |
| 231 // to have shipped should support a maximum basic leaf value of at least 0xa. | |
| 232 uint32_t eax, ebx, ecx, edx; | |
| 233 CallCPUID(0, &eax, &ebx, &ecx, &edx); | |
| 234 if (eax < 7) { | |
| 235 return 0; | |
| 236 } | |
| 237 | |
| 238 CallCPUID(7, &eax, &ebx, &ecx, &edx); | |
| 239 return ebx; | |
| 240 #else | |
| 241 NOTREACHED(); | |
| 242 return 0; | |
| 243 #endif | |
| 244 } | |
| 245 | |
| 246 bool SystemSnapshotMac::CPUX86SupportsDAZ() const { | |
| 247 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 248 | |
| 249 #if defined(ARCH_CPU_X86_FAMILY) | |
| 250 // The correct way to check for denormals-as-zeros (DAZ) support is to examine | |
| 251 // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s | |
| 252 // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the | |
| 253 // DAZ Flag in the MXCSR Register”. Note that since this function tests for | |
| 254 // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would | |
| 255 // indicate whether DAZ is actually enabled, which is a per-thread context | |
| 256 // concern. | |
| 257 // | |
| 258 // All CPUs that Apple is known to have shipped should support DAZ. | |
| 259 | |
| 260 // Test for fxsave support. | |
| 261 uint64_t features = CPUX86Features(); | |
| 262 if (!(features & (UINT64_C(1) << 24))) { | |
| 263 return false; | |
| 264 } | |
| 265 | |
| 266 // Call fxsave. | |
| 267 struct __attribute__((aligned(16))) { | |
|
Robert Sesek
2014/10/03 18:05:44
Can this not be a CPUContextX86::Fxsave ?
| |
| 268 uint16_t fcw; | |
| 269 uint16_t fsw; | |
| 270 uint8_t ftw; | |
| 271 uint8_t reserved_1; | |
| 272 uint16_t fop; | |
| 273 uint32_t fpu_ip; | |
| 274 uint16_t fpu_cs; | |
| 275 uint16_t reserved_2; | |
| 276 uint32_t fpu_dp; | |
| 277 uint16_t fpu_ds; | |
| 278 uint16_t reserved_3; | |
| 279 uint32_t mxcsr; | |
| 280 uint32_t mxcsr_mask; | |
| 281 uint128_struct mm[8]; | |
| 282 uint128_struct xmm[16]; | |
| 283 uint128_struct reserved_4[3]; | |
| 284 uint128_struct available[3]; | |
| 285 } fxsave = {}; | |
| 286 static_assert(sizeof(fxsave) == 512, "fxsave size"); | |
| 287 static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28, | |
| 288 "mxcsr_mask offset"); | |
| 289 asm("fxsave %0" : "=m"(fxsave)); | |
| 290 | |
| 291 // Test the DAZ bit. | |
| 292 return fxsave.mxcsr_mask & (1 << 6); | |
| 293 #else | |
| 294 NOTREACHED(); | |
| 295 return false; | |
| 296 #endif | |
| 297 } | |
| 298 | |
| 299 SystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const { | |
| 300 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 301 return kOperatingSystemMacOSX; | |
| 302 } | |
| 303 | |
| 304 bool SystemSnapshotMac::OSServer() const { | |
| 305 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 306 return os_server_; | |
| 307 } | |
| 308 | |
| 309 void SystemSnapshotMac::OSVersion(int* major, | |
| 310 int* minor, | |
| 311 int* bugfix, | |
| 312 std::string* build) const { | |
| 313 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 314 *major = os_version_major_; | |
| 315 *minor = os_version_minor_; | |
| 316 *bugfix = os_version_bugfix_; | |
| 317 build->assign(os_version_build_); | |
| 318 } | |
| 319 | |
| 320 std::string SystemSnapshotMac::OSVersionFull() const { | |
| 321 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 322 return os_version_full_; | |
| 323 } | |
| 324 | |
| 325 std::string SystemSnapshotMac::MachineDescription() const { | |
| 326 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 327 | |
| 328 std::string model; | |
| 329 std::string board_id; | |
| 330 MacModelAndBoard(&model, &board_id); | |
| 331 | |
| 332 if (!model.empty()) { | |
| 333 if (!board_id.empty()) { | |
| 334 return base::StringPrintf("%s (%s)", model.c_str(), board_id.c_str()); | |
| 335 } | |
| 336 return model; | |
| 337 } | |
| 338 if (!board_id.empty()) { | |
| 339 return base::StringPrintf("(%s)", board_id.c_str()); | |
| 340 } | |
| 341 return std::string(); | |
| 342 } | |
| 343 | |
| 344 bool SystemSnapshotMac::NXEnabled() const { | |
| 345 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 346 return ReadIntSysctlByName<int>("kern.nx", 0); | |
| 347 } | |
| 348 | |
| 349 void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status, | |
| 350 int* standard_offset_seconds, | |
| 351 int* daylight_offset_seconds, | |
| 352 std::string* standard_name, | |
| 353 std::string* daylight_name) const { | |
| 354 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 355 | |
| 356 tm local; | |
| 357 localtime_r(&snapshot_time_->tv_sec, &local); | |
| 358 | |
| 359 *standard_name = tzname[0]; | |
| 360 if (daylight) { | |
| 361 // This assumes that the offset between standard and daylight saving time is | |
|
Robert Sesek
2014/10/03 18:05:44
Technically this is not a safe assumption. http://
Mark Mentovai
2014/10/03 18:34:57
Robert Sesek wrote:
Robert Sesek
2014/10/03 18:44:26
I think it's probably fine.
| |
| 362 // globally a constant, where a time zone’s daylight saving time is one hour | |
| 363 // ahead of its standard time. | |
| 364 const int kSecondsPerHour = 60 * 60; | |
| 365 | |
| 366 *daylight_name = tzname[1]; | |
| 367 if (!local.tm_isdst) { | |
| 368 *dst_status = kObservingStandardTime; | |
| 369 *standard_offset_seconds = local.tm_gmtoff; | |
| 370 *daylight_offset_seconds = local.tm_gmtoff + kSecondsPerHour; | |
| 371 } else { | |
| 372 *dst_status = kObservingDaylightSavingTime; | |
| 373 *standard_offset_seconds = local.tm_gmtoff - kSecondsPerHour; | |
| 374 *daylight_offset_seconds = local.tm_gmtoff; | |
| 375 } | |
| 376 } else { | |
| 377 *daylight_name = tzname[0]; | |
| 378 *dst_status = kDoesNotObserveDaylightSavingTime; | |
| 379 *standard_offset_seconds = local.tm_gmtoff; | |
| 380 *daylight_offset_seconds = local.tm_gmtoff; | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 } // namespace internal | |
| 385 } // namespace crashpad | |
| OLD | NEW |