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 |