OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/win/system_snapshot_win.h" |
| 16 |
| 17 #include <intrin.h> |
| 18 #include <powrprof.h> |
| 19 #include <windows.h> |
| 20 #include <winnt.h> |
| 21 |
| 22 #include <algorithm> |
| 23 #include <utility> |
| 24 #include <vector> |
| 25 |
| 26 #include "base/memory/scoped_ptr.h" |
| 27 #include "base/numerics/safe_conversions.h" |
| 28 #include "base/strings/stringprintf.h" |
| 29 #include "base/strings/utf_string_conversions.h" |
| 30 |
| 31 namespace crashpad { |
| 32 |
| 33 namespace { |
| 34 |
| 35 //! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileFlags |
| 36 //! value. |
| 37 std::string GetStringForFileFlags(uint32_t file_flags) { |
| 38 std::string result; |
| 39 DCHECK_EQ(file_flags & VS_FF_INFOINFERRED, 0u); |
| 40 if (file_flags & VS_FF_DEBUG) |
| 41 result += "Debug,"; |
| 42 if (file_flags & VS_FF_PATCHED) |
| 43 result += "Patched,"; |
| 44 if (file_flags & VS_FF_PRERELEASE) |
| 45 result += "Prerelease,"; |
| 46 if (file_flags & VS_FF_PRIVATEBUILD) |
| 47 result += "Private,"; |
| 48 if (file_flags & VS_FF_SPECIALBUILD) |
| 49 result += "Special,"; |
| 50 if (!result.empty()) |
| 51 return result.substr(0, result.size() - 1); // Remove trailing comma. |
| 52 return result; |
| 53 } |
| 54 |
| 55 //! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileOS value. |
| 56 std::string GetStringForFileOS(uint32_t file_os) { |
| 57 // There are a variety of ancient things this could theoretically be. In |
| 58 // practice, we're always going to get VOS_NT_WINDOWS32 here. |
| 59 if ((file_os & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32) |
| 60 return "Windows NT"; |
| 61 else |
| 62 return "Unknown"; |
| 63 } |
| 64 |
| 65 } // namespace |
| 66 |
| 67 namespace internal { |
| 68 |
| 69 SystemSnapshotWin::SystemSnapshotWin() |
| 70 : SystemSnapshot(), |
| 71 os_version_full_(), |
| 72 os_version_build_(), |
| 73 process_reader_(nullptr), |
| 74 os_version_major_(0), |
| 75 os_version_minor_(0), |
| 76 os_version_bugfix_(0), |
| 77 os_server_(false), |
| 78 initialized_() { |
| 79 } |
| 80 |
| 81 SystemSnapshotWin::~SystemSnapshotWin() { |
| 82 } |
| 83 |
| 84 void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) { |
| 85 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| 86 |
| 87 process_reader_ = process_reader; |
| 88 |
| 89 // We use both GetVersionEx and VerQueryValue. GetVersionEx is not trustworthy |
| 90 // after Windows 8 (depending on the application manifest) so its data is used |
| 91 // only to fill the os_server_ field, and the rest comes from the version |
| 92 // information stamped on kernel32.dll. |
| 93 OSVERSIONINFOEX version_info = {sizeof(version_info)}; |
| 94 if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info))) { |
| 95 PLOG(WARNING) << "GetVersionEx"; |
| 96 } else { |
| 97 const wchar_t kSystemDll[] = L"kernel32.dll"; |
| 98 DWORD size = GetFileVersionInfoSize(kSystemDll, nullptr); |
| 99 if (!size) { |
| 100 PLOG(WARNING) << "GetFileVersionInfoSize"; |
| 101 } else { |
| 102 scoped_ptr<uint8_t[]> data(new uint8_t[size]); |
| 103 if (!GetFileVersionInfo(kSystemDll, 0, size, data.get())) { |
| 104 PLOG(WARNING) << "GetFileVersionInfo"; |
| 105 } else { |
| 106 VS_FIXEDFILEINFO* fixed_file_info; |
| 107 UINT size; |
| 108 if (!VerQueryValue(data.get(), |
| 109 L"\\", |
| 110 reinterpret_cast<void**>(&fixed_file_info), |
| 111 &size)) { |
| 112 PLOG(WARNING) << "VerQueryValue"; |
| 113 } else { |
| 114 uint32_t valid_flags = |
| 115 fixed_file_info->dwFileFlags & fixed_file_info->dwFileFlagsMask; |
| 116 std::string flags_string = GetStringForFileFlags(valid_flags); |
| 117 os_version_major_ = |
| 118 (fixed_file_info->dwFileVersionMS & 0xffff0000) >> 16; |
| 119 os_version_minor_ = fixed_file_info->dwFileVersionMS & 0xffff; |
| 120 os_version_bugfix_ = |
| 121 (fixed_file_info->dwFileVersionLS & 0xffff0000) >> 16; |
| 122 os_version_build_ = base::StringPrintf( |
| 123 "%d", fixed_file_info->dwFileVersionLS & 0xffff); |
| 124 os_server_ = version_info.wProductType != VER_NT_WORKSTATION; |
| 125 std::string os_name = GetStringForFileOS(fixed_file_info->dwFileOS); |
| 126 os_version_full_ = base::StringPrintf( |
| 127 "%s %d.%d.%d.%s%s", |
| 128 os_name.c_str(), |
| 129 os_version_major_, |
| 130 os_version_minor_, |
| 131 os_version_bugfix_, |
| 132 os_version_build_.c_str(), |
| 133 flags_string.empty() ? "" : (std::string(" (") + flags_string + |
| 134 ")").c_str()); |
| 135 } |
| 136 } |
| 137 } |
| 138 } |
| 139 |
| 140 INITIALIZATION_STATE_SET_VALID(initialized_); |
| 141 } |
| 142 |
| 143 CPUArchitecture SystemSnapshotWin::GetCPUArchitecture() const { |
| 144 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 145 |
| 146 return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 |
| 147 : kCPUArchitectureX86; |
| 148 } |
| 149 |
| 150 uint32_t SystemSnapshotWin::CPURevision() const { |
| 151 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 152 |
| 153 uint32_t raw = CPUX86Signature(); |
| 154 uint8_t stepping = raw & 0xf; |
| 155 uint8_t model = (raw & 0xf0) >> 4; |
| 156 uint8_t family = (raw & 0xf00) >> 8; |
| 157 uint8_t extended_model = static_cast<uint8_t>((raw & 0xf0000) >> 16); |
| 158 uint16_t extended_family = (raw & 0xff00000) >> 20; |
| 159 |
| 160 // For families before 15, extended_family are simply reserved bits. |
| 161 if (family < 15) |
| 162 extended_family = 0; |
| 163 // extended_model is only used for families 6 and 15. |
| 164 if (family != 6 && family != 15) |
| 165 extended_model = 0; |
| 166 |
| 167 uint16_t adjusted_family = family + extended_family; |
| 168 uint8_t adjusted_model = model + (extended_model << 4); |
| 169 return (adjusted_family << 16) | (adjusted_model << 8) | stepping; |
| 170 } |
| 171 |
| 172 uint8_t SystemSnapshotWin::CPUCount() const { |
| 173 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 174 |
| 175 SYSTEM_INFO system_info; |
| 176 GetSystemInfo(&system_info); |
| 177 if (!base::IsValueInRangeForNumericType<uint8_t>( |
| 178 system_info.dwNumberOfProcessors)) { |
| 179 LOG(WARNING) << "dwNumberOfProcessors exceeds uint8_t storage"; |
| 180 } |
| 181 return base::saturated_cast<uint8_t>(system_info.dwNumberOfProcessors); |
| 182 } |
| 183 |
| 184 std::string SystemSnapshotWin::CPUVendor() const { |
| 185 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 186 |
| 187 int cpu_info[4]; |
| 188 __cpuid(cpu_info, 0); |
| 189 char vendor[12]; |
| 190 *reinterpret_cast<int*>(vendor) = cpu_info[1]; |
| 191 *reinterpret_cast<int*>(vendor + 4) = cpu_info[3]; |
| 192 *reinterpret_cast<int*>(vendor + 8) = cpu_info[2]; |
| 193 return std::string(vendor, sizeof(vendor)); |
| 194 } |
| 195 |
| 196 void SystemSnapshotWin::CPUFrequency(uint64_t* current_hz, |
| 197 uint64_t* max_hz) const { |
| 198 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 199 |
| 200 int num_cpus = CPUCount(); |
| 201 DCHECK_GT(num_cpus, 0); |
| 202 std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpus); |
| 203 if (CallNtPowerInformation(ProcessorInformation, |
| 204 nullptr, |
| 205 0, |
| 206 &info[0], |
| 207 sizeof(PROCESSOR_POWER_INFORMATION) * num_cpus) != |
| 208 0) { |
| 209 *current_hz = 0; |
| 210 *max_hz = 0; |
| 211 return; |
| 212 } |
| 213 const uint64_t kMhzToHz = static_cast<uint64_t>(1E6); |
| 214 *current_hz = std::max_element(info.begin(), |
| 215 info.end(), |
| 216 [](const PROCESSOR_POWER_INFORMATION& a, |
| 217 const PROCESSOR_POWER_INFORMATION& b) { |
| 218 return a.CurrentMhz < b.CurrentMhz; |
| 219 })->CurrentMhz * |
| 220 kMhzToHz; |
| 221 *max_hz = std::max_element(info.begin(), |
| 222 info.end(), |
| 223 [](const PROCESSOR_POWER_INFORMATION& a, |
| 224 const PROCESSOR_POWER_INFORMATION& b) { |
| 225 return a.MaxMhz < b.MaxMhz; |
| 226 })->MaxMhz * |
| 227 kMhzToHz; |
| 228 } |
| 229 |
| 230 uint32_t SystemSnapshotWin::CPUX86Signature() const { |
| 231 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 232 |
| 233 int cpu_info[4]; |
| 234 // We will never run on any processors that don't support at least function 1. |
| 235 __cpuid(cpu_info, 1); |
| 236 return cpu_info[0]; |
| 237 } |
| 238 |
| 239 uint64_t SystemSnapshotWin::CPUX86Features() const { |
| 240 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 241 |
| 242 int cpu_info[4]; |
| 243 // We will never run on any processors that don't support at least function 1. |
| 244 __cpuid(cpu_info, 1); |
| 245 return (static_cast<uint64_t>(cpu_info[2]) << 32) | |
| 246 static_cast<uint64_t>(cpu_info[3]); |
| 247 } |
| 248 |
| 249 uint64_t SystemSnapshotWin::CPUX86ExtendedFeatures() const { |
| 250 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 251 |
| 252 int cpu_info[4]; |
| 253 // We will never run on any processors that don't support at least extended |
| 254 // function 1. |
| 255 __cpuid(cpu_info, 0x80000001); |
| 256 return (static_cast<uint64_t>(cpu_info[2]) << 32) | |
| 257 static_cast<uint64_t>(cpu_info[3]); |
| 258 } |
| 259 |
| 260 uint32_t SystemSnapshotWin::CPUX86Leaf7Features() const { |
| 261 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 262 |
| 263 int cpu_info[4]; |
| 264 |
| 265 // Make sure leaf 7 can be called. |
| 266 __cpuid(cpu_info, 0); |
| 267 if (cpu_info[0] < 7) |
| 268 return 0; |
| 269 |
| 270 __cpuidex(cpu_info, 7, 0); |
| 271 return cpu_info[1]; |
| 272 } |
| 273 |
| 274 bool SystemSnapshotWin::CPUX86SupportsDAZ() const { |
| 275 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 276 |
| 277 // The correct way to check for denormals-as-zeros (DAZ) support is to examine |
| 278 // mxcsr mask, which can be done with fxsave. See Intel Software Developer's |
| 279 // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 "Checking for the |
| 280 // DAZ Flag in the MXCSR Register". Note that since this function tests for |
| 281 // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would |
| 282 // indicate whether DAZ is actually enabled, which is a per-thread context |
| 283 // concern. |
| 284 |
| 285 // Test for fxsave support. |
| 286 uint64_t features = CPUX86Features(); |
| 287 if (!(features & (UINT64_C(1) << 24))) { |
| 288 return false; |
| 289 } |
| 290 |
| 291 // Call fxsave. |
| 292 __declspec(align(16)) uint32_t extended_registers[128]; |
| 293 _fxsave(&extended_registers); |
| 294 uint32_t mxcsr_mask = extended_registers[7]; |
| 295 |
| 296 // Test the DAZ bit. |
| 297 return mxcsr_mask & (1 << 6); |
| 298 } |
| 299 |
| 300 SystemSnapshot::OperatingSystem SystemSnapshotWin::GetOperatingSystem() const { |
| 301 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 302 return kOperatingSystemWindows; |
| 303 } |
| 304 |
| 305 bool SystemSnapshotWin::OSServer() const { |
| 306 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 307 return os_server_; |
| 308 } |
| 309 |
| 310 void SystemSnapshotWin::OSVersion(int* major, |
| 311 int* minor, |
| 312 int* bugfix, |
| 313 std::string* build) const { |
| 314 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 315 *major = os_version_major_; |
| 316 *minor = os_version_minor_; |
| 317 *bugfix = os_version_bugfix_; |
| 318 build->assign(os_version_build_); |
| 319 } |
| 320 |
| 321 std::string SystemSnapshotWin::OSVersionFull() const { |
| 322 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 323 return os_version_full_; |
| 324 } |
| 325 |
| 326 std::string SystemSnapshotWin::MachineDescription() const { |
| 327 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 328 // TODO(scottmg): Not sure if there's anything sensible to put here. |
| 329 return std::string(); |
| 330 } |
| 331 |
| 332 bool SystemSnapshotWin::NXEnabled() const { |
| 333 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 334 return IsProcessorFeaturePresent(PF_NX_ENABLED); |
| 335 } |
| 336 |
| 337 void SystemSnapshotWin::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 // This returns the current time zone status rather than the status at the |
| 343 // time of the snapshot. This differs from the Mac implementation. |
| 344 TIME_ZONE_INFORMATION time_zone_information; |
| 345 *dst_status = static_cast<DaylightSavingTimeStatus>( |
| 346 GetTimeZoneInformation(&time_zone_information)); |
| 347 *standard_offset_seconds = |
| 348 (time_zone_information.Bias + time_zone_information.StandardBias) * -60; |
| 349 *daylight_offset_seconds = |
| 350 (time_zone_information.Bias + time_zone_information.DaylightBias) * -60; |
| 351 *standard_name = base::UTF16ToUTF8(time_zone_information.StandardName); |
| 352 *daylight_name = base::UTF16ToUTF8(time_zone_information.DaylightName); |
| 353 } |
| 354 |
| 355 } // namespace internal |
| 356 } // namespace crashpad |
OLD | NEW |