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, nullptr, 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, nullptr, &buf_len, nullptr, 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, nullptr, 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_(nullptr), | |
93 snapshot_time_(nullptr), | |
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 #if defined(ARCH_CPU_X86) | |
274 CPUContextX86::Fxsave fxsave __attribute__((aligned(16))) = {}; | |
275 #elif defined(ARCH_CPU_X86_64) | |
276 CPUContextX86_64::Fxsave fxsave __attribute__((aligned(16))) = {}; | |
277 #endif | |
278 static_assert(sizeof(fxsave) == 512, "fxsave size"); | |
279 static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28, | |
280 "mxcsr_mask offset"); | |
281 asm("fxsave %0" : "=m"(fxsave)); | |
282 | |
283 // Test the DAZ bit. | |
284 return fxsave.mxcsr_mask & (1 << 6); | |
285 #else | |
286 NOTREACHED(); | |
287 return false; | |
288 #endif | |
289 } | |
290 | |
291 SystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const { | |
292 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
293 return kOperatingSystemMacOSX; | |
294 } | |
295 | |
296 bool SystemSnapshotMac::OSServer() const { | |
297 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
298 return os_server_; | |
299 } | |
300 | |
301 void SystemSnapshotMac::OSVersion(int* major, | |
302 int* minor, | |
303 int* bugfix, | |
304 std::string* build) const { | |
305 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
306 *major = os_version_major_; | |
307 *minor = os_version_minor_; | |
308 *bugfix = os_version_bugfix_; | |
309 build->assign(os_version_build_); | |
310 } | |
311 | |
312 std::string SystemSnapshotMac::OSVersionFull() const { | |
313 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
314 return os_version_full_; | |
315 } | |
316 | |
317 std::string SystemSnapshotMac::MachineDescription() const { | |
318 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
319 | |
320 std::string model; | |
321 std::string board_id; | |
322 MacModelAndBoard(&model, &board_id); | |
323 | |
324 if (!model.empty()) { | |
325 if (!board_id.empty()) { | |
326 return base::StringPrintf("%s (%s)", model.c_str(), board_id.c_str()); | |
327 } | |
328 return model; | |
329 } | |
330 if (!board_id.empty()) { | |
331 return base::StringPrintf("(%s)", board_id.c_str()); | |
332 } | |
333 return std::string(); | |
334 } | |
335 | |
336 bool SystemSnapshotMac::NXEnabled() const { | |
337 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
338 return ReadIntSysctlByName<int>("kern.nx", 0); | |
339 } | |
340 | |
341 void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status, | |
342 int* standard_offset_seconds, | |
343 int* daylight_offset_seconds, | |
344 std::string* standard_name, | |
345 std::string* daylight_name) const { | |
346 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
347 | |
348 tm local; | |
349 PCHECK(localtime_r(&snapshot_time_->tv_sec, &local)) << "localtime_r"; | |
350 | |
351 *standard_name = tzname[0]; | |
352 if (daylight) { | |
353 // Scan forward and backward, one month at a time, looking for an instance | |
354 // when the observance of daylight saving time is different than it is in | |
355 // |local|. | |
356 long probe_gmtoff = local.tm_gmtoff; | |
357 | |
358 const int kMonthDeltas[] = | |
359 { 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, | |
360 7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12 }; | |
361 for (size_t index = 0; index < arraysize(kMonthDeltas); ++index) { | |
362 // Look at the 15th day of each month at local noon. Set tm_isdst to -1 to | |
363 // avoid giving mktime() any hints about whether to consider daylight | |
364 // saving time in effect. mktime() accepts values of tm_mon that are | |
365 // outside of its normal range and behaves as expected: if tm_mon is -1, | |
366 // it references December of the preceding year, and if it is 12, it | |
367 // references January of the following year. | |
368 tm probe_tm = {}; | |
369 probe_tm.tm_hour = 12; | |
370 probe_tm.tm_mday = 15; | |
371 probe_tm.tm_mon = local.tm_mon + kMonthDeltas[index]; | |
372 probe_tm.tm_year = local.tm_year; | |
373 probe_tm.tm_isdst = -1; | |
374 if (mktime(&probe_tm) != -1 && probe_tm.tm_isdst != local.tm_isdst) { | |
375 probe_gmtoff = probe_tm.tm_gmtoff; | |
376 break; | |
377 } | |
378 } | |
379 | |
380 *daylight_name = tzname[1]; | |
381 if (!local.tm_isdst) { | |
382 *dst_status = kObservingStandardTime; | |
383 *standard_offset_seconds = local.tm_gmtoff; | |
384 *daylight_offset_seconds = probe_gmtoff; | |
385 } else { | |
386 *dst_status = kObservingDaylightSavingTime; | |
387 *standard_offset_seconds = probe_gmtoff; | |
388 *daylight_offset_seconds = local.tm_gmtoff; | |
389 } | |
390 } else { | |
391 *daylight_name = tzname[0]; | |
392 *dst_status = kDoesNotObserveDaylightSavingTime; | |
393 *standard_offset_seconds = local.tm_gmtoff; | |
394 *daylight_offset_seconds = local.tm_gmtoff; | |
395 } | |
396 } | |
397 | |
398 } // namespace internal | |
399 } // namespace crashpad | |
OLD | NEW |