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 "snapshot/cpu_context.h" |
| 26 #include "util/mac/mac_util.h" |
| 27 #include "util/mac/process_reader.h" |
| 28 #include "util/numeric/in_range_cast.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 #else |
| 151 #error port to your architecture |
| 152 #endif |
| 153 } |
| 154 |
| 155 uint32_t SystemSnapshotMac::CPURevision() const { |
| 156 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 157 |
| 158 #if defined(ARCH_CPU_X86_FAMILY) |
| 159 // machdep.cpu.family and machdep.cpu.model already take the extended family |
| 160 // and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c |
| 161 // cpuid_set_generic_info(). |
| 162 uint16_t family = CastIntSysctlByName<uint16_t>("machdep.cpu.family", 0); |
| 163 uint8_t model = CastIntSysctlByName<uint8_t>("machdep.cpu.model", 0); |
| 164 uint8_t stepping = CastIntSysctlByName<uint8_t>("machdep.cpu.stepping", 0); |
| 165 |
| 166 return (family << 16) | (model << 8) | stepping; |
| 167 #else |
| 168 #error port to your architecture |
| 169 #endif |
| 170 } |
| 171 |
| 172 uint8_t SystemSnapshotMac::CPUCount() const { |
| 173 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 174 return CastIntSysctlByName<uint8_t>("hw.ncpu", 1); |
| 175 } |
| 176 |
| 177 std::string SystemSnapshotMac::CPUVendor() const { |
| 178 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 179 |
| 180 #if defined(ARCH_CPU_X86_FAMILY) |
| 181 return ReadStringSysctlByName("machdep.cpu.vendor"); |
| 182 #else |
| 183 #error port to your architecture |
| 184 #endif |
| 185 } |
| 186 |
| 187 void SystemSnapshotMac::CPUFrequency( |
| 188 uint64_t* current_hz, uint64_t* max_hz) const { |
| 189 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 190 *current_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency", 0); |
| 191 *max_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency_max", 0); |
| 192 } |
| 193 |
| 194 uint32_t SystemSnapshotMac::CPUX86Signature() const { |
| 195 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 196 |
| 197 #if defined(ARCH_CPU_X86_FAMILY) |
| 198 return ReadIntSysctlByName<uint32_t>("machdep.cpu.signature", 0); |
| 199 #else |
| 200 NOTREACHED(); |
| 201 return 0; |
| 202 #endif |
| 203 } |
| 204 |
| 205 uint64_t SystemSnapshotMac::CPUX86Features() const { |
| 206 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 207 |
| 208 #if defined(ARCH_CPU_X86_FAMILY) |
| 209 return ReadIntSysctlByName<uint64_t>("machdep.cpu.feature_bits", 0); |
| 210 #else |
| 211 NOTREACHED(); |
| 212 return 0; |
| 213 #endif |
| 214 } |
| 215 |
| 216 uint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const { |
| 217 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 218 |
| 219 #if defined(ARCH_CPU_X86_FAMILY) |
| 220 return ReadIntSysctlByName<uint64_t>("machdep.cpu.extfeature_bits", 0); |
| 221 #else |
| 222 NOTREACHED(); |
| 223 return 0; |
| 224 #endif |
| 225 } |
| 226 |
| 227 uint32_t SystemSnapshotMac::CPUX86Leaf7Features() const { |
| 228 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 229 |
| 230 #if defined(ARCH_CPU_X86_FAMILY) |
| 231 // The machdep.cpu.leaf7_feature_bits sysctl isn’t supported prior to Mac OS X |
| 232 // 10.7, so read this by calling cpuid directly. |
| 233 // |
| 234 // machdep.cpu.max_basic could be used to check whether to read the leaf, but |
| 235 // that sysctl isn’t supported prior to Mac OS X 10.6, so read the maximum |
| 236 // basic leaf by calling cpuid directly as well. All CPUs that Apple is known |
| 237 // to have shipped should support a maximum basic leaf value of at least 0xa. |
| 238 uint32_t eax, ebx, ecx, edx; |
| 239 CallCPUID(0, &eax, &ebx, &ecx, &edx); |
| 240 if (eax < 7) { |
| 241 return 0; |
| 242 } |
| 243 |
| 244 CallCPUID(7, &eax, &ebx, &ecx, &edx); |
| 245 return ebx; |
| 246 #else |
| 247 NOTREACHED(); |
| 248 return 0; |
| 249 #endif |
| 250 } |
| 251 |
| 252 bool SystemSnapshotMac::CPUX86SupportsDAZ() const { |
| 253 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 254 |
| 255 #if defined(ARCH_CPU_X86_FAMILY) |
| 256 // The correct way to check for denormals-as-zeros (DAZ) support is to examine |
| 257 // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s |
| 258 // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the |
| 259 // DAZ Flag in the MXCSR Register”. Note that since this function tests for |
| 260 // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would |
| 261 // indicate whether DAZ is actually enabled, which is a per-thread context |
| 262 // concern. |
| 263 // |
| 264 // All CPUs that Apple is known to have shipped should support DAZ. |
| 265 |
| 266 // Test for fxsave support. |
| 267 uint64_t features = CPUX86Features(); |
| 268 if (!(features & (UINT64_C(1) << 24))) { |
| 269 return false; |
| 270 } |
| 271 |
| 272 // Call fxsave. |
| 273 CPUContextX86::Fxsave fxsave __attribute__((aligned(16))) = {}; |
| 274 static_assert(sizeof(fxsave) == 512, "fxsave size"); |
| 275 static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28, |
| 276 "mxcsr_mask offset"); |
| 277 asm("fxsave %0" : "=m"(fxsave)); |
| 278 |
| 279 // Test the DAZ bit. |
| 280 return fxsave.mxcsr_mask & (1 << 6); |
| 281 #else |
| 282 NOTREACHED(); |
| 283 return false; |
| 284 #endif |
| 285 } |
| 286 |
| 287 SystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const { |
| 288 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 289 return kOperatingSystemMacOSX; |
| 290 } |
| 291 |
| 292 bool SystemSnapshotMac::OSServer() const { |
| 293 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 294 return os_server_; |
| 295 } |
| 296 |
| 297 void SystemSnapshotMac::OSVersion(int* major, |
| 298 int* minor, |
| 299 int* bugfix, |
| 300 std::string* build) const { |
| 301 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 302 *major = os_version_major_; |
| 303 *minor = os_version_minor_; |
| 304 *bugfix = os_version_bugfix_; |
| 305 build->assign(os_version_build_); |
| 306 } |
| 307 |
| 308 std::string SystemSnapshotMac::OSVersionFull() const { |
| 309 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 310 return os_version_full_; |
| 311 } |
| 312 |
| 313 std::string SystemSnapshotMac::MachineDescription() const { |
| 314 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 315 |
| 316 std::string model; |
| 317 std::string board_id; |
| 318 MacModelAndBoard(&model, &board_id); |
| 319 |
| 320 if (!model.empty()) { |
| 321 if (!board_id.empty()) { |
| 322 return base::StringPrintf("%s (%s)", model.c_str(), board_id.c_str()); |
| 323 } |
| 324 return model; |
| 325 } |
| 326 if (!board_id.empty()) { |
| 327 return base::StringPrintf("(%s)", board_id.c_str()); |
| 328 } |
| 329 return std::string(); |
| 330 } |
| 331 |
| 332 bool SystemSnapshotMac::NXEnabled() const { |
| 333 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 334 return ReadIntSysctlByName<int>("kern.nx", 0); |
| 335 } |
| 336 |
| 337 void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status, |
| 338 int* standard_offset_seconds, |
| 339 int* daylight_offset_seconds, |
| 340 std::string* standard_name, |
| 341 std::string* daylight_name) const { |
| 342 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 343 |
| 344 tm local; |
| 345 localtime_r(&snapshot_time_->tv_sec, &local); |
| 346 |
| 347 *standard_name = tzname[0]; |
| 348 if (daylight) { |
| 349 // This assumes that the offset between standard and daylight saving time is |
| 350 // globally a constant, where a time zone’s daylight saving time is one hour |
| 351 // ahead of its standard time. |
| 352 const int kSecondsPerHour = 60 * 60; |
| 353 |
| 354 *daylight_name = tzname[1]; |
| 355 if (!local.tm_isdst) { |
| 356 *dst_status = kObservingStandardTime; |
| 357 *standard_offset_seconds = local.tm_gmtoff; |
| 358 *daylight_offset_seconds = local.tm_gmtoff + kSecondsPerHour; |
| 359 } else { |
| 360 *dst_status = kObservingDaylightSavingTime; |
| 361 *standard_offset_seconds = local.tm_gmtoff - kSecondsPerHour; |
| 362 *daylight_offset_seconds = local.tm_gmtoff; |
| 363 } |
| 364 } else { |
| 365 *daylight_name = tzname[0]; |
| 366 *dst_status = kDoesNotObserveDaylightSavingTime; |
| 367 *standard_offset_seconds = local.tm_gmtoff; |
| 368 *daylight_offset_seconds = local.tm_gmtoff; |
| 369 } |
| 370 } |
| 371 |
| 372 } // namespace internal |
| 373 } // namespace crashpad |
OLD | NEW |