Bug 1910362 - Create new Nimbus helper r=aaronmt,ohorvath
[gecko.git] / xpcom / base / nsSystemInfo.cpp
blob84700ed853ff94fdaffeed0c5e91c2a953765bce
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
9 #include "nsAppRunner.h"
10 #include "nsSystemInfo.h"
11 #include "prsystem.h"
12 #include "prio.h"
13 #include "mozilla/SSE.h"
14 #include "mozilla/arm.h"
15 #include "mozilla/Hal.h"
16 #include "mozilla/LazyIdleThread.h"
17 #include "mozilla/LookAndFeel.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/Try.h"
20 #include "mozilla/Vector.h"
21 #include "jsapi.h"
22 #include "js/PropertyAndElement.h" // JS_SetProperty
23 #include "mozilla/dom/Promise.h"
25 #ifdef XP_WIN
26 # include <comutil.h>
27 # include <time.h>
28 # ifndef __MINGW32__
29 # include <iwscapi.h>
30 # endif // __MINGW32__
31 # include <windows.h>
32 # include <winioctl.h>
33 # ifndef __MINGW32__
34 # include <wrl.h>
35 # include <wscapi.h>
36 # endif // __MINGW32__
37 # include "base/scoped_handle_win.h"
38 # include "mozilla/DynamicallyLinkedFunctionPtr.h"
39 # include "mozilla/WindowsVersion.h"
40 # include "nsAppDirectoryServiceDefs.h"
41 # include "nsDirectoryServiceDefs.h"
42 # include "nsDirectoryServiceUtils.h"
43 # include "nsWindowsHelpers.h"
44 # include "WinUtils.h"
45 # include "mozilla/NotNull.h"
47 #endif
49 #ifdef XP_MACOSX
50 # include "MacHelpers.h"
51 #endif
53 #ifdef MOZ_WIDGET_GTK
54 # include <gtk/gtk.h>
55 # include <dlfcn.h>
56 # include "mozilla/WidgetUtilsGtk.h"
57 #endif
59 #if defined(XP_LINUX) && !defined(ANDROID)
60 # include <unistd.h>
61 # include <fstream>
62 # include "mozilla/Tokenizer.h"
63 # include "mozilla/widget/LSBUtils.h"
64 # include "nsCharSeparatedTokenizer.h"
66 # include <map>
67 # include <string>
68 #endif
70 #ifdef MOZ_WIDGET_ANDROID
71 # include "AndroidBuild.h"
72 # include "mozilla/java/GeckoAppShellWrappers.h"
73 # include "mozilla/jni/Utils.h"
74 #endif
76 #ifdef XP_MACOSX
77 # include <sys/sysctl.h>
78 #endif
80 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
81 # include "mozilla/SandboxInfo.h"
82 #endif
84 // Slot for NS_InitXPCOM to pass information to nsSystemInfo::Init.
85 // Only set to nonzero (potentially) if XP_UNIX. On such systems, the
86 // system call to discover the appropriate value is not thread-safe,
87 // so we must call it before going multithreaded, but nsSystemInfo::Init
88 // only happens well after that point.
89 uint32_t nsSystemInfo::gUserUmask = 0;
91 using namespace mozilla::dom;
93 #if defined(XP_WIN)
94 # define RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy \
95 L"Windows.System.Profile.WindowsIntegrityPolicy"
96 # ifndef __MINGW32__
97 using namespace Microsoft::WRL;
98 using namespace Microsoft::WRL::Wrappers;
99 using namespace ABI::Windows::Foundation;
100 # endif // __MINGW32__
101 #endif
103 #if defined(XP_LINUX) && !defined(ANDROID)
104 static void SimpleParseKeyValuePairs(
105 const std::string& aFilename,
106 std::map<nsCString, nsCString>& aKeyValuePairs) {
107 std::ifstream input(aFilename.c_str());
108 for (std::string line; std::getline(input, line);) {
109 nsAutoCString key, value;
111 nsCCharSeparatedTokenizer tokens(nsDependentCString(line.c_str()), ':');
112 if (tokens.hasMoreTokens()) {
113 key = tokens.nextToken();
114 if (tokens.hasMoreTokens()) {
115 value = tokens.nextToken();
117 // We want the value even if there was just one token, to cover the
118 // case where we had the key, and the value was blank (seems to be
119 // a valid scenario some files.)
120 aKeyValuePairs[key] = value;
124 #endif
126 #ifdef XP_WIN
127 // Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
128 // so keeping the _ instead of switching to camel case for now.
129 static void GetProcessorInformation(int* physical_cpus, int* cache_size_L2,
130 int* cache_size_L3) {
131 MOZ_ASSERT(physical_cpus && cache_size_L2 && cache_size_L3);
133 *physical_cpus = 0;
134 *cache_size_L2 = 0; // This will be in kbytes
135 *cache_size_L3 = 0; // This will be in kbytes
137 // Determine buffer size, allocate and get processor information.
138 // Size can change between calls (unlikely), so a loop is done.
139 SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer[32];
140 SYSTEM_LOGICAL_PROCESSOR_INFORMATION* infos = &info_buffer[0];
141 DWORD return_length = sizeof(info_buffer);
142 while (!::GetLogicalProcessorInformation(infos, &return_length)) {
143 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
144 infos == &info_buffer[0]) {
145 infos = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION
146 [return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)];
147 } else {
148 return;
152 for (size_t i = 0;
153 i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
154 if (infos[i].Relationship == RelationProcessorCore) {
155 ++*physical_cpus;
156 } else if (infos[i].Relationship == RelationCache) {
157 // Only care about L2 and L3 cache
158 switch (infos[i].Cache.Level) {
159 case 2:
160 *cache_size_L2 = static_cast<int>(infos[i].Cache.Size / 1024);
161 break;
162 case 3:
163 *cache_size_L3 = static_cast<int>(infos[i].Cache.Size / 1024);
164 break;
165 default:
166 break;
170 if (infos != &info_buffer[0]) {
171 delete[] infos;
173 return;
175 #endif
177 #if defined(XP_WIN)
178 namespace {
179 static nsresult GetFolderDiskInfo(nsIFile* file, FolderDiskInfo& info) {
180 info.model.Truncate();
181 info.revision.Truncate();
182 info.isSSD = false;
184 nsAutoString filePath;
185 nsresult rv = file->GetPath(filePath);
186 NS_ENSURE_SUCCESS(rv, rv);
187 wchar_t volumeMountPoint[MAX_PATH] = {L'\\', L'\\', L'.', L'\\'};
188 const size_t PREFIX_LEN = 4;
189 if (!::GetVolumePathNameW(filePath.get(), volumeMountPoint + PREFIX_LEN,
190 std::size(volumeMountPoint) - PREFIX_LEN)) {
191 return NS_ERROR_UNEXPECTED;
193 size_t volumeMountPointLen = wcslen(volumeMountPoint);
194 // Since we would like to open a drive and not a directory, we need to
195 // remove any trailing backslash. A drive handle is valid for
196 // DeviceIoControl calls, a directory handle is not.
197 if (volumeMountPoint[volumeMountPointLen - 1] == L'\\') {
198 volumeMountPoint[volumeMountPointLen - 1] = L'\0';
200 ScopedHandle handle(::CreateFileW(volumeMountPoint, 0,
201 FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
202 OPEN_EXISTING, 0, nullptr));
203 if (!handle.IsValid()) {
204 return NS_ERROR_UNEXPECTED;
206 STORAGE_PROPERTY_QUERY queryParameters = {StorageDeviceProperty,
207 PropertyStandardQuery};
208 STORAGE_DEVICE_DESCRIPTOR outputHeader = {sizeof(STORAGE_DEVICE_DESCRIPTOR)};
209 DWORD bytesRead = 0;
210 if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &queryParameters,
211 sizeof(queryParameters), &outputHeader,
212 sizeof(outputHeader), &bytesRead, nullptr)) {
213 return NS_ERROR_FAILURE;
215 PSTORAGE_DEVICE_DESCRIPTOR deviceOutput =
216 (PSTORAGE_DEVICE_DESCRIPTOR)malloc(outputHeader.Size);
217 if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &queryParameters,
218 sizeof(queryParameters), deviceOutput,
219 outputHeader.Size, &bytesRead, nullptr)) {
220 free(deviceOutput);
221 return NS_ERROR_FAILURE;
224 queryParameters.PropertyId = StorageDeviceTrimProperty;
225 bytesRead = 0;
226 bool isSSD = false;
227 DEVICE_TRIM_DESCRIPTOR trimDescriptor = {sizeof(DEVICE_TRIM_DESCRIPTOR)};
228 if (::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &queryParameters,
229 sizeof(queryParameters), &trimDescriptor,
230 sizeof(trimDescriptor), &bytesRead, nullptr)) {
231 if (trimDescriptor.TrimEnabled) {
232 isSSD = true;
236 if (isSSD) {
237 // Get Seek Penalty
238 queryParameters.PropertyId = StorageDeviceSeekPenaltyProperty;
239 bytesRead = 0;
240 DEVICE_SEEK_PENALTY_DESCRIPTOR seekPenaltyDescriptor = {
241 sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR)};
242 if (::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY,
243 &queryParameters, sizeof(queryParameters),
244 &seekPenaltyDescriptor, sizeof(seekPenaltyDescriptor),
245 &bytesRead, nullptr)) {
246 // It is possible that the disk has TrimEnabled, but also
247 // IncursSeekPenalty; In this case, this is an HDD
248 if (seekPenaltyDescriptor.IncursSeekPenalty) {
249 isSSD = false;
254 // Some HDDs are including product ID info in the vendor field. Since PNP
255 // IDs include vendor info and product ID concatenated together, we'll do
256 // that here and interpret the result as a unique ID for the HDD model.
257 if (deviceOutput->VendorIdOffset) {
258 info.model =
259 reinterpret_cast<char*>(deviceOutput) + deviceOutput->VendorIdOffset;
261 if (deviceOutput->ProductIdOffset) {
262 info.model +=
263 reinterpret_cast<char*>(deviceOutput) + deviceOutput->ProductIdOffset;
265 info.model.CompressWhitespace();
266 if (deviceOutput->ProductRevisionOffset) {
267 info.revision = reinterpret_cast<char*>(deviceOutput) +
268 deviceOutput->ProductRevisionOffset;
269 info.revision.CompressWhitespace();
271 info.isSSD = isSSD;
272 free(deviceOutput);
273 return NS_OK;
276 static nsresult CollectDiskInfo(nsIFile* greDir, nsIFile* winDir,
277 nsIFile* profDir, DiskInfo& info) {
278 nsresult rv = GetFolderDiskInfo(greDir, info.binary);
279 if (NS_FAILED(rv)) {
280 return rv;
282 rv = GetFolderDiskInfo(winDir, info.system);
283 if (NS_FAILED(rv)) {
284 return rv;
286 return GetFolderDiskInfo(profDir, info.profile);
289 static nsresult CollectOSInfo(OSInfo& info) {
290 HKEY installYearHKey;
291 LONG status = RegOpenKeyExW(
292 HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
293 KEY_READ | KEY_WOW64_64KEY, &installYearHKey);
295 if (status != ERROR_SUCCESS) {
296 return NS_ERROR_UNEXPECTED;
299 nsAutoRegKey installYearKey(installYearHKey);
301 DWORD type = 0;
302 time_t raw_time = 0;
303 DWORD time_size = sizeof(time_t);
305 status = RegQueryValueExW(installYearHKey, L"InstallDate", nullptr, &type,
306 (LPBYTE)&raw_time, &time_size);
308 if (status != ERROR_SUCCESS) {
309 return NS_ERROR_UNEXPECTED;
312 if (type != REG_DWORD) {
313 return NS_ERROR_UNEXPECTED;
316 tm time;
317 if (localtime_s(&time, &raw_time) != 0) {
318 return NS_ERROR_UNEXPECTED;
321 info.installYear = 1900UL + time.tm_year;
323 nsAutoServiceHandle scm(
324 OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT));
326 if (!scm) {
327 return NS_ERROR_UNEXPECTED;
330 bool superfetchServiceRunning = false;
332 // Superfetch was introduced in Windows Vista as a service with the name
333 // SysMain. The service display name was also renamed to SysMain after Windows
334 // 10 build 1809.
335 nsAutoServiceHandle hService(OpenService(scm, L"SysMain", GENERIC_READ));
337 if (hService) {
338 SERVICE_STATUS superfetchStatus;
339 LPSERVICE_STATUS pSuperfetchStatus = &superfetchStatus;
341 if (!QueryServiceStatus(hService, pSuperfetchStatus)) {
342 return NS_ERROR_UNEXPECTED;
345 superfetchServiceRunning =
346 superfetchStatus.dwCurrentState == SERVICE_RUNNING;
349 // If the SysMain (Superfetch) service is available, but not configured using
350 // the defaults, then it's disabled for our purposes, since it's not going to
351 // be operating as expected.
352 bool superfetchUsingDefaultParams = true;
353 bool prefetchUsingDefaultParams = true;
355 static const WCHAR prefetchParamsKeyName[] =
356 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory "
357 L"Management\\PrefetchParameters";
358 static const DWORD SUPERFETCH_DEFAULT_PARAM = 3;
359 static const DWORD PREFETCH_DEFAULT_PARAM = 3;
361 HKEY prefetchParamsHKey;
363 LONG prefetchParamsStatus =
364 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefetchParamsKeyName, 0,
365 KEY_READ | KEY_WOW64_64KEY, &prefetchParamsHKey);
367 if (prefetchParamsStatus == ERROR_SUCCESS) {
368 DWORD valueSize = sizeof(DWORD);
369 DWORD superfetchValue = 0;
370 nsAutoRegKey prefetchParamsKey(prefetchParamsHKey);
371 LONG superfetchParamStatus = RegQueryValueExW(
372 prefetchParamsHKey, L"EnableSuperfetch", nullptr, &type,
373 reinterpret_cast<LPBYTE>(&superfetchValue), &valueSize);
375 // If the EnableSuperfetch registry key doesn't exist, then it's using the
376 // default configuration.
377 if (superfetchParamStatus == ERROR_SUCCESS &&
378 superfetchValue != SUPERFETCH_DEFAULT_PARAM) {
379 superfetchUsingDefaultParams = false;
382 DWORD prefetchValue = 0;
384 LONG prefetchParamStatus = RegQueryValueExW(
385 prefetchParamsHKey, L"EnablePrefetcher", nullptr, &type,
386 reinterpret_cast<LPBYTE>(&prefetchValue), &valueSize);
388 // If the EnablePrefetcher registry key doesn't exist, then we interpret
389 // that as the Prefetcher being disabled (since Prefetch behaviour when
390 // the key is not available appears to be undefined).
391 if (prefetchParamStatus != ERROR_SUCCESS ||
392 prefetchValue != PREFETCH_DEFAULT_PARAM) {
393 prefetchUsingDefaultParams = false;
397 info.hasSuperfetch = superfetchServiceRunning && superfetchUsingDefaultParams;
398 info.hasPrefetch = prefetchUsingDefaultParams;
400 return NS_OK;
403 nsresult CollectCountryCode(nsAString& aCountryCode) {
404 GEOID geoid = GetUserGeoID(GEOCLASS_NATION);
405 if (geoid == GEOID_NOT_AVAILABLE) {
406 return NS_ERROR_NOT_AVAILABLE;
408 // Get required length
409 int numChars = GetGeoInfoW(geoid, GEO_ISO2, nullptr, 0, 0);
410 if (!numChars) {
411 return NS_ERROR_FAILURE;
413 // Now get the string for real
414 aCountryCode.SetLength(numChars);
415 numChars =
416 GetGeoInfoW(geoid, GEO_ISO2, char16ptr_t(aCountryCode.BeginWriting()),
417 aCountryCode.Length(), 0);
418 if (!numChars) {
419 return NS_ERROR_FAILURE;
422 // numChars includes null terminator
423 aCountryCode.Truncate(numChars - 1);
424 return NS_OK;
427 } // namespace
429 # ifndef __MINGW32__
431 static HRESULT EnumWSCProductList(
432 nsAString& aOutput, mozilla::NotNull<IWSCProductList*> aProdList) {
433 MOZ_ASSERT(aOutput.IsEmpty());
435 LONG count;
436 HRESULT hr = aProdList->get_Count(&count);
437 if (FAILED(hr)) {
438 return hr;
441 for (LONG index = 0; index < count; ++index) {
442 RefPtr<IWscProduct> product;
443 hr = aProdList->get_Item(index, getter_AddRefs(product));
444 if (FAILED(hr)) {
445 return hr;
448 WSC_SECURITY_PRODUCT_STATE state;
449 hr = product->get_ProductState(&state);
450 if (FAILED(hr)) {
451 return hr;
454 // We only care about products that are active
455 if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
456 state == WSC_SECURITY_PRODUCT_STATE_SNOOZED) {
457 continue;
460 _bstr_t bName;
461 hr = product->get_ProductName(bName.GetAddress());
462 if (FAILED(hr)) {
463 return hr;
466 if (!aOutput.IsEmpty()) {
467 aOutput.AppendLiteral(u";");
470 aOutput.Append((wchar_t*)bName, bName.length());
473 return S_OK;
476 static nsresult GetWindowsSecurityCenterInfo(nsAString& aAVInfo,
477 nsAString& aAntiSpyInfo,
478 nsAString& aFirewallInfo) {
479 aAVInfo.Truncate();
480 aAntiSpyInfo.Truncate();
481 aFirewallInfo.Truncate();
483 if (!XRE_IsParentProcess()) {
484 return NS_ERROR_NOT_AVAILABLE;
487 const CLSID clsid = __uuidof(WSCProductList);
488 const IID iid = __uuidof(IWSCProductList);
490 // NB: A separate instance of IWSCProductList is needed for each distinct
491 // security provider type; MSDN says that we cannot reuse the same object
492 // and call Initialize() to pave over the previous data.
494 WSC_SECURITY_PROVIDER providerTypes[] = {WSC_SECURITY_PROVIDER_ANTIVIRUS,
495 WSC_SECURITY_PROVIDER_ANTISPYWARE,
496 WSC_SECURITY_PROVIDER_FIREWALL};
498 // Each output must match the corresponding entry in providerTypes.
499 nsAString* outputs[] = {&aAVInfo, &aAntiSpyInfo, &aFirewallInfo};
501 static_assert(std::size(providerTypes) == std::size(outputs),
502 "Length of providerTypes and outputs arrays must match");
504 for (uint32_t index = 0; index < std::size(providerTypes); ++index) {
505 RefPtr<IWSCProductList> prodList;
506 HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid,
507 getter_AddRefs(prodList));
508 if (FAILED(hr)) {
509 return NS_ERROR_NOT_AVAILABLE;
512 hr = prodList->Initialize(providerTypes[index]);
513 if (FAILED(hr)) {
514 return NS_ERROR_UNEXPECTED;
517 hr = EnumWSCProductList(*outputs[index],
518 mozilla::WrapNotNull(prodList.get()));
519 if (FAILED(hr)) {
520 return NS_ERROR_UNEXPECTED;
524 return NS_OK;
527 # endif // __MINGW32__
529 #endif // defined(XP_WIN)
531 #ifdef XP_MACOSX
532 static nsresult GetAppleModelId(nsAutoCString& aModelId) {
533 size_t numChars = 0;
534 size_t result = sysctlbyname("hw.model", nullptr, &numChars, nullptr, 0);
535 if (result != 0 || !numChars) {
536 return NS_ERROR_FAILURE;
538 aModelId.SetLength(numChars);
539 result =
540 sysctlbyname("hw.model", aModelId.BeginWriting(), &numChars, nullptr, 0);
541 if (result != 0) {
542 return NS_ERROR_FAILURE;
544 // numChars includes null terminator
545 aModelId.Truncate(numChars - 1);
546 return NS_OK;
549 static nsresult ProcessIsRosettaTranslated(bool& isRosetta) {
550 # if defined(__aarch64__)
551 // There is no need to call sysctlbyname() if we are running as arm64.
552 isRosetta = false;
553 # else
554 int ret = 0;
555 size_t size = sizeof(ret);
556 if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
557 if (errno != ENOENT) {
558 fprintf(stderr, "Failed to check for translation environment\n");
560 isRosetta = false;
561 } else {
562 isRosetta = (ret == 1);
564 # endif
565 return NS_OK;
567 #endif
569 using namespace mozilla;
571 nsSystemInfo::nsSystemInfo() = default;
573 nsSystemInfo::~nsSystemInfo() = default;
575 // CPU-specific information.
576 static const struct PropItems {
577 const char* name;
578 bool (*propfun)(void);
579 } cpuPropItems[] = {
580 // x86-specific bits.
581 {"hasMMX", mozilla::supports_mmx},
582 {"hasSSE", mozilla::supports_sse},
583 {"hasSSE2", mozilla::supports_sse2},
584 {"hasSSE3", mozilla::supports_sse3},
585 {"hasSSSE3", mozilla::supports_ssse3},
586 {"hasSSE4A", mozilla::supports_sse4a},
587 {"hasSSE4_1", mozilla::supports_sse4_1},
588 {"hasSSE4_2", mozilla::supports_sse4_2},
589 {"hasAVX", mozilla::supports_avx},
590 {"hasAVX2", mozilla::supports_avx2},
591 {"hasAES", mozilla::supports_aes},
592 // ARM-specific bits.
593 {"hasEDSP", mozilla::supports_edsp},
594 {"hasARMv6", mozilla::supports_armv6},
595 {"hasARMv7", mozilla::supports_armv7},
596 {"hasNEON", mozilla::supports_neon}};
598 nsresult CollectProcessInfo(ProcessInfo& info) {
599 nsAutoCString cpuVendor;
600 nsAutoCString cpuName;
601 int cpuSpeed = -1;
602 int cpuFamily = -1;
603 int cpuModel = -1;
604 int cpuStepping = -1;
605 int logicalCPUs = -1;
606 int physicalCPUs = -1;
607 int cacheSizeL2 = -1;
608 int cacheSizeL3 = -1;
610 #if defined(XP_WIN)
611 // IsWow64Process2 is only available on Windows 10+, so we have to dynamically
612 // check for its existence.
613 typedef BOOL(WINAPI * LPFN_IWP2)(HANDLE, USHORT*, USHORT*);
614 LPFN_IWP2 iwp2 = reinterpret_cast<LPFN_IWP2>(
615 GetProcAddress(GetModuleHandle(L"kernel32"), "IsWow64Process2"));
616 BOOL isWow64 = FALSE;
617 USHORT processMachine = IMAGE_FILE_MACHINE_UNKNOWN;
618 USHORT nativeMachine = IMAGE_FILE_MACHINE_UNKNOWN;
619 BOOL gotWow64Value;
620 if (iwp2) {
621 gotWow64Value = iwp2(GetCurrentProcess(), &processMachine, &nativeMachine);
622 if (gotWow64Value) {
623 isWow64 = (processMachine != IMAGE_FILE_MACHINE_UNKNOWN);
625 } else {
626 gotWow64Value = IsWow64Process(GetCurrentProcess(), &isWow64);
627 // The function only indicates a WOW64 environment if it's 32-bit x86
628 // running on x86-64, so emulate what IsWow64Process2 would have given.
629 if (gotWow64Value && isWow64) {
630 processMachine = IMAGE_FILE_MACHINE_I386;
631 nativeMachine = IMAGE_FILE_MACHINE_AMD64;
634 NS_WARNING_ASSERTION(gotWow64Value, "IsWow64Process failed");
635 if (gotWow64Value) {
636 // Set this always, even for the x86-on-arm64 case.
637 info.isWow64 = !!isWow64;
638 // Additional information if we're running x86-on-arm64
639 info.isWowARM64 = (processMachine == IMAGE_FILE_MACHINE_I386 &&
640 nativeMachine == IMAGE_FILE_MACHINE_ARM64);
643 // S Mode
645 # ifndef __MINGW32__
646 // WindowsIntegrityPolicy is only available on newer versions
647 // of Windows 10, so there's no point in trying to check this
648 // on earlier versions. We know GetActivationFactory crashes on
649 // Windows 7 when trying to retrieve this class, and may also
650 // crash on very old versions of Windows 10.
651 if (IsWin10Sep2018UpdateOrLater()) {
652 ComPtr<IWindowsIntegrityPolicyStatics> wip;
653 HRESULT hr = GetActivationFactory(
654 HStringReference(
655 RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy)
656 .Get(),
657 &wip);
658 if (SUCCEEDED(hr)) {
659 // info.isWindowsSMode ends up true if Windows is in S mode, otherwise
660 // false
661 // https://docs.microsoft.com/en-us/uwp/api/windows.system.profile.windowsintegritypolicy.isenabled?view=winrt-22000
662 hr = wip->get_IsEnabled(&info.isWindowsSMode);
663 NS_WARNING_ASSERTION(SUCCEEDED(hr),
664 "WindowsIntegrityPolicy.IsEnabled failed");
667 # endif // __MINGW32__
669 // CPU speed
670 HKEY key;
671 static const WCHAR keyName[] =
672 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
674 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &key) ==
675 ERROR_SUCCESS) {
676 DWORD data, len, vtype;
677 len = sizeof(data);
679 if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data),
680 &len) == ERROR_SUCCESS) {
681 cpuSpeed = static_cast<int>(data);
684 // Limit to 64 double byte characters, should be plenty, but create
685 // a buffer one larger as the result may not be null terminated. If
686 // it is more than 64, we will not get the value.
687 wchar_t cpuVendorStr[64 + 1];
688 len = sizeof(cpuVendorStr) - 2;
689 if (RegQueryValueExW(key, L"VendorIdentifier", 0, &vtype,
690 reinterpret_cast<LPBYTE>(cpuVendorStr),
691 &len) == ERROR_SUCCESS &&
692 vtype == REG_SZ && len % 2 == 0 && len > 1) {
693 cpuVendorStr[len / 2] = 0; // In case it isn't null terminated
694 CopyUTF16toUTF8(nsDependentString(cpuVendorStr), cpuVendor);
697 // Limit to 64 double byte characters, should be plenty, but create
698 // a buffer one larger as the result may not be null terminated. If
699 // it is more than 64, we will not get the value.
700 // The expected string size is 48 characters or less.
701 wchar_t cpuNameStr[64 + 1];
702 len = sizeof(cpuNameStr) - 2;
703 if (RegQueryValueExW(key, L"ProcessorNameString", 0, &vtype,
704 reinterpret_cast<LPBYTE>(cpuNameStr),
705 &len) == ERROR_SUCCESS &&
706 vtype == REG_SZ && len % 2 == 0 && len > 1) {
707 cpuNameStr[len / 2] = 0; // In case it isn't null terminated
708 CopyUTF16toUTF8(nsDependentString(cpuNameStr), cpuName);
711 RegCloseKey(key);
714 // Other CPU attributes:
715 SYSTEM_INFO si;
716 GetNativeSystemInfo(&si);
717 logicalCPUs = si.dwNumberOfProcessors;
718 GetProcessorInformation(&physicalCPUs, &cacheSizeL2, &cacheSizeL3);
719 if (physicalCPUs <= 0) {
720 physicalCPUs = logicalCPUs;
722 cpuFamily = si.wProcessorLevel;
723 cpuModel = si.wProcessorRevision >> 8;
724 cpuStepping = si.wProcessorRevision & 0xFF;
725 #elif defined(XP_MACOSX)
726 // CPU speed
727 uint64_t sysctlValue64 = 0;
728 uint32_t sysctlValue32 = 0;
729 size_t len = 0;
730 len = sizeof(sysctlValue64);
731 if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64, &len, NULL, 0)) {
732 cpuSpeed = static_cast<int>(sysctlValue64 / 1000000);
734 MOZ_ASSERT(sizeof(sysctlValue64) == len);
736 len = sizeof(sysctlValue32);
737 if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
738 physicalCPUs = static_cast<int>(sysctlValue32);
740 MOZ_ASSERT(sizeof(sysctlValue32) == len);
742 len = sizeof(sysctlValue32);
743 if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
744 logicalCPUs = static_cast<int>(sysctlValue32);
746 MOZ_ASSERT(sizeof(sysctlValue32) == len);
748 len = sizeof(sysctlValue64);
749 if (!sysctlbyname("hw.l2cachesize", &sysctlValue64, &len, NULL, 0)) {
750 cacheSizeL2 = static_cast<int>(sysctlValue64 / 1024);
752 MOZ_ASSERT(sizeof(sysctlValue64) == len);
754 len = sizeof(sysctlValue64);
755 if (!sysctlbyname("hw.l3cachesize", &sysctlValue64, &len, NULL, 0)) {
756 cacheSizeL3 = static_cast<int>(sysctlValue64 / 1024);
758 MOZ_ASSERT(sizeof(sysctlValue64) == len);
760 if (!sysctlbyname("machdep.cpu.vendor", NULL, &len, NULL, 0)) {
761 char* cpuVendorStr = new char[len];
762 if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr, &len, NULL, 0)) {
763 cpuVendor = cpuVendorStr;
765 delete[] cpuVendorStr;
768 if (!sysctlbyname("machdep.cpu.brand_string", NULL, &len, NULL, 0)) {
769 char* cpuNameStr = new char[len];
770 if (!sysctlbyname("machdep.cpu.brand_string", cpuNameStr, &len, NULL, 0)) {
771 cpuName = cpuNameStr;
773 delete[] cpuNameStr;
776 len = sizeof(sysctlValue32);
777 if (!sysctlbyname("machdep.cpu.family", &sysctlValue32, &len, NULL, 0)) {
778 cpuFamily = static_cast<int>(sysctlValue32);
780 MOZ_ASSERT(sizeof(sysctlValue32) == len);
782 len = sizeof(sysctlValue32);
783 if (!sysctlbyname("machdep.cpu.model", &sysctlValue32, &len, NULL, 0)) {
784 cpuModel = static_cast<int>(sysctlValue32);
786 MOZ_ASSERT(sizeof(sysctlValue32) == len);
788 len = sizeof(sysctlValue32);
789 if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) {
790 cpuStepping = static_cast<int>(sysctlValue32);
792 MOZ_ASSERT(sizeof(sysctlValue32) == len);
794 #elif defined(XP_LINUX) && !defined(ANDROID)
795 // Get vendor, family, model, stepping, physical cores
796 // from /proc/cpuinfo file
798 std::map<nsCString, nsCString> keyValuePairs;
799 SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
801 # if defined(__arm__) || defined(__aarch64__)
802 // The tables below were taken from
803 // https://raw.githubusercontent.com/util-linux/util-linux/e3192bfd1dd129c70f5416e1464135d8cd22c956/sys-utils/lscpu-arm.c
805 /* clang-format off */
806 struct id_part {
807 const int id;
808 const char* name;
811 static const struct id_part arm_part[] = {
812 { 0x810, "ARM810" },
813 { 0x920, "ARM920" },
814 { 0x922, "ARM922" },
815 { 0x926, "ARM926" },
816 { 0x940, "ARM940" },
817 { 0x946, "ARM946" },
818 { 0x966, "ARM966" },
819 { 0xa20, "ARM1020" },
820 { 0xa22, "ARM1022" },
821 { 0xa26, "ARM1026" },
822 { 0xb02, "ARM11 MPCore" },
823 { 0xb36, "ARM1136" },
824 { 0xb56, "ARM1156" },
825 { 0xb76, "ARM1176" },
826 { 0xc05, "Cortex-A5" },
827 { 0xc07, "Cortex-A7" },
828 { 0xc08, "Cortex-A8" },
829 { 0xc09, "Cortex-A9" },
830 { 0xc0d, "Cortex-A17" }, /* Originally A12 */
831 { 0xc0f, "Cortex-A15" },
832 { 0xc0e, "Cortex-A17" },
833 { 0xc14, "Cortex-R4" },
834 { 0xc15, "Cortex-R5" },
835 { 0xc17, "Cortex-R7" },
836 { 0xc18, "Cortex-R8" },
837 { 0xc20, "Cortex-M0" },
838 { 0xc21, "Cortex-M1" },
839 { 0xc23, "Cortex-M3" },
840 { 0xc24, "Cortex-M4" },
841 { 0xc27, "Cortex-M7" },
842 { 0xc60, "Cortex-M0+" },
843 { 0xd01, "Cortex-A32" },
844 { 0xd02, "Cortex-A34" },
845 { 0xd03, "Cortex-A53" },
846 { 0xd04, "Cortex-A35" },
847 { 0xd05, "Cortex-A55" },
848 { 0xd06, "Cortex-A65" },
849 { 0xd07, "Cortex-A57" },
850 { 0xd08, "Cortex-A72" },
851 { 0xd09, "Cortex-A73" },
852 { 0xd0a, "Cortex-A75" },
853 { 0xd0b, "Cortex-A76" },
854 { 0xd0c, "Neoverse-N1" },
855 { 0xd0d, "Cortex-A77" },
856 { 0xd0e, "Cortex-A76AE" },
857 { 0xd13, "Cortex-R52" },
858 { 0xd15, "Cortex-R82" },
859 { 0xd16, "Cortex-R52+" },
860 { 0xd20, "Cortex-M23" },
861 { 0xd21, "Cortex-M33" },
862 { 0xd22, "Cortex-M55" },
863 { 0xd23, "Cortex-M85" },
864 { 0xd40, "Neoverse-V1" },
865 { 0xd41, "Cortex-A78" },
866 { 0xd42, "Cortex-A78AE" },
867 { 0xd43, "Cortex-A65AE" },
868 { 0xd44, "Cortex-X1" },
869 { 0xd46, "Cortex-A510" },
870 { 0xd47, "Cortex-A710" },
871 { 0xd48, "Cortex-X2" },
872 { 0xd49, "Neoverse-N2" },
873 { 0xd4a, "Neoverse-E1" },
874 { 0xd4b, "Cortex-A78C" },
875 { 0xd4c, "Cortex-X1C" },
876 { 0xd4d, "Cortex-A715" },
877 { 0xd4e, "Cortex-X3" },
878 { 0xd4f, "Neoverse-V2" },
879 { 0xd80, "Cortex-A520" },
880 { 0xd81, "Cortex-A720" },
881 { 0xd82, "Cortex-X4" },
882 { 0xd84, "Neoverse-V3" },
883 { 0xd8e, "Neoverse-N3" },
884 { -1, "unknown" },
887 static const struct id_part brcm_part[] = {
888 { 0x0f, "Brahma-B15" },
889 { 0x100, "Brahma-B53" },
890 { 0x516, "ThunderX2" },
891 { -1, "unknown" },
894 static const struct id_part dec_part[] = {
895 { 0xa10, "SA110" },
896 { 0xa11, "SA1100" },
897 { -1, "unknown" },
900 static const struct id_part cavium_part[] = {
901 { 0x0a0, "ThunderX" },
902 { 0x0a1, "ThunderX-88XX" },
903 { 0x0a2, "ThunderX-81XX" },
904 { 0x0a3, "ThunderX-83XX" },
905 { 0x0af, "ThunderX2-99xx" },
906 { 0x0b0, "OcteonTX2" },
907 { 0x0b1, "OcteonTX2-98XX" },
908 { 0x0b2, "OcteonTX2-96XX" },
909 { 0x0b3, "OcteonTX2-95XX" },
910 { 0x0b4, "OcteonTX2-95XXN" },
911 { 0x0b5, "OcteonTX2-95XXMM" },
912 { 0x0b6, "OcteonTX2-95XXO" },
913 { 0x0b8, "ThunderX3-T110" },
914 { -1, "unknown" },
917 static const struct id_part apm_part[] = {
918 { 0x000, "X-Gene" },
919 { -1, "unknown" },
922 static const struct id_part qcom_part[] = {
923 { 0x00f, "Scorpion" },
924 { 0x02d, "Scorpion" },
925 { 0x04d, "Krait" },
926 { 0x06f, "Krait" },
927 { 0x201, "Kryo" },
928 { 0x205, "Kryo" },
929 { 0x211, "Kryo" },
930 { 0x800, "Falkor-V1/Kryo" },
931 { 0x801, "Kryo-V2" },
932 { 0x802, "Kryo-3XX-Gold" },
933 { 0x803, "Kryo-3XX-Silver" },
934 { 0x804, "Kryo-4XX-Gold" },
935 { 0x805, "Kryo-4XX-Silver" },
936 { 0xc00, "Falkor" },
937 { 0xc01, "Saphira" },
938 { -1, "unknown" },
941 static const struct id_part samsung_part[] = {
942 { 0x001, "exynos-m1" },
943 { 0x002, "exynos-m3" },
944 { 0x003, "exynos-m4" },
945 { 0x004, "exynos-m5" },
946 { -1, "unknown" },
949 static const struct id_part nvidia_part[] = {
950 { 0x000, "Denver" },
951 { 0x003, "Denver 2" },
952 { 0x004, "Carmel" },
953 { -1, "unknown" },
956 static const struct id_part marvell_part[] = {
957 { 0x131, "Feroceon-88FR131" },
958 { 0x581, "PJ4/PJ4b" },
959 { 0x584, "PJ4B-MP" },
960 { -1, "unknown" },
963 static const struct id_part apple_part[] = {
964 { 0x000, "Swift" },
965 { 0x001, "Cyclone" },
966 { 0x002, "Typhoon" },
967 { 0x003, "Typhoon/Capri" },
968 { 0x004, "Twister" },
969 { 0x005, "Twister/Elba/Malta" },
970 { 0x006, "Hurricane" },
971 { 0x007, "Hurricane/Myst" },
972 { 0x008, "Monsoon" },
973 { 0x009, "Mistral" },
974 { 0x00b, "Vortex" },
975 { 0x00c, "Tempest" },
976 { 0x00f, "Tempest-M9" },
977 { 0x010, "Vortex/Aruba" },
978 { 0x011, "Tempest/Aruba" },
979 { 0x012, "Lightning" },
980 { 0x013, "Thunder" },
981 { 0x020, "Icestorm-A14" },
982 { 0x021, "Firestorm-A14" },
983 { 0x022, "Icestorm-M1" },
984 { 0x023, "Firestorm-M1" },
985 { 0x024, "Icestorm-M1-Pro" },
986 { 0x025, "Firestorm-M1-Pro" },
987 { 0x026, "Thunder-M10" },
988 { 0x028, "Icestorm-M1-Max" },
989 { 0x029, "Firestorm-M1-Max" },
990 { 0x030, "Blizzard-A15" },
991 { 0x031, "Avalanche-A15" },
992 { 0x032, "Blizzard-M2" },
993 { 0x033, "Avalanche-M2" },
994 { 0x034, "Blizzard-M2-Pro" },
995 { 0x035, "Avalanche-M2-Pro" },
996 { 0x036, "Sawtooth-A16" },
997 { 0x037, "Everest-A16" },
998 { 0x038, "Blizzard-M2-Max" },
999 { 0x039, "Avalanche-M2-Max" },
1000 { -1, "unknown" },
1003 static const struct id_part faraday_part[] = {
1004 { 0x526, "FA526" },
1005 { 0x626, "FA626" },
1006 { -1, "unknown" },
1009 static const struct id_part intel_part[] = {
1010 { 0x200, "i80200" },
1011 { 0x210, "PXA250A" },
1012 { 0x212, "PXA210A" },
1013 { 0x242, "i80321-400" },
1014 { 0x243, "i80321-600" },
1015 { 0x290, "PXA250B/PXA26x" },
1016 { 0x292, "PXA210B" },
1017 { 0x2c2, "i80321-400-B0" },
1018 { 0x2c3, "i80321-600-B0" },
1019 { 0x2d0, "PXA250C/PXA255/PXA26x" },
1020 { 0x2d2, "PXA210C" },
1021 { 0x411, "PXA27x" },
1022 { 0x41c, "IPX425-533" },
1023 { 0x41d, "IPX425-400" },
1024 { 0x41f, "IPX425-266" },
1025 { 0x682, "PXA32x" },
1026 { 0x683, "PXA930/PXA935" },
1027 { 0x688, "PXA30x" },
1028 { 0x689, "PXA31x" },
1029 { 0xb11, "SA1110" },
1030 { 0xc12, "IPX1200" },
1031 { -1, "unknown" },
1034 static const struct id_part fujitsu_part[] = {
1035 { 0x001, "A64FX" },
1036 { -1, "unknown" },
1039 static const struct id_part hisi_part[] = {
1040 { 0xd01, "TaiShan-v110" }, /* used in Kunpeng-920 SoC */
1041 { 0xd02, "TaiShan-v120" }, /* used in Kirin 990A and 9000S SoCs */
1042 { 0xd40, "Cortex-A76" }, /* HiSilicon uses this ID though advertises A76 */
1043 { 0xd41, "Cortex-A77" }, /* HiSilicon uses this ID though advertises A77 */
1044 { -1, "unknown" },
1047 static const struct id_part ampere_part[] = {
1048 { 0xac3, "Ampere-1" },
1049 { 0xac4, "Ampere-1a" },
1050 { -1, "unknown" },
1053 static const struct id_part ft_part[] = {
1054 { 0x303, "FTC310" },
1055 { 0x660, "FTC660" },
1056 { 0x661, "FTC661" },
1057 { 0x662, "FTC662" },
1058 { 0x663, "FTC663" },
1059 { 0x664, "FTC664" },
1060 { 0x862, "FTC862" },
1061 { -1, "unknown" },
1064 static const struct id_part ms_part[] = {
1065 { 0xd49, "Azure-Cobalt-100" },
1066 { -1, "unknown" },
1069 static const struct id_part unknown_part[] = {
1070 { -1, "unknown" },
1073 struct hw_impl {
1074 const int id;
1075 const struct id_part *parts;
1076 const char *name;
1079 static const struct hw_impl hw_implementer[] = {
1080 { 0x41, arm_part, "ARM" },
1081 { 0x42, brcm_part, "Broadcom" },
1082 { 0x43, cavium_part, "Cavium" },
1083 { 0x44, dec_part, "DEC" },
1084 { 0x46, fujitsu_part, "FUJITSU" },
1085 { 0x48, hisi_part, "HiSilicon" },
1086 { 0x49, unknown_part, "Infineon" },
1087 { 0x4d, unknown_part, "Motorola/Freescale" },
1088 { 0x4e, nvidia_part, "NVIDIA" },
1089 { 0x50, apm_part, "APM" },
1090 { 0x51, qcom_part, "Qualcomm" },
1091 { 0x53, samsung_part, "Samsung" },
1092 { 0x56, marvell_part, "Marvell" },
1093 { 0x61, apple_part, "Apple" },
1094 { 0x66, faraday_part, "Faraday" },
1095 { 0x69, intel_part, "Intel" },
1096 { 0x6d, ms_part, "Microsoft" },
1097 { 0x70, ft_part, "Phytium" },
1098 { 0xc0, ampere_part, "Ampere" },
1099 { -1, unknown_part, "unknown" },
1101 /* clang-format on */
1103 // cpuFamily from "CPU implementer". Technically, this is only the vendor,
1104 // but this is the closed to a family we can get.
1105 (void)Tokenizer(keyValuePairs["CPU implementer"_ns])
1106 .ReadHexadecimal(&cpuFamily);
1108 // cpuModel from "CPU part". Not exactly a model number, but close enough,
1109 // and that's what lscpu uses.
1110 (void)Tokenizer(keyValuePairs["CPU part"_ns]).ReadHexadecimal(&cpuModel);
1112 // cpuStepping from "CPU variant" (that's what lscpu uses).
1113 (void)Tokenizer(keyValuePairs["CPU variant"_ns])
1114 .ReadHexadecimal(&cpuStepping);
1116 for (auto& hw_impl : hw_implementer) {
1117 if (hw_impl.id == (int)cpuFamily) {
1118 info.cpuVendor.Assign(hw_impl.name);
1119 for (auto* p = &hw_impl.parts[0]; p->id != -1; ++p) {
1120 if (p->id == (int)cpuModel) {
1121 info.cpuName.Assign(p->name);
1126 # else
1127 // cpuVendor from "vendor_id"
1128 info.cpuVendor.Assign(keyValuePairs["vendor_id"_ns]);
1130 // cpuName from "model name"
1131 info.cpuName.Assign(keyValuePairs["model name"_ns]);
1133 // cpuFamily from "cpu family"
1134 (void)Tokenizer(keyValuePairs["cpu family"_ns]).ReadInteger(&cpuFamily);
1136 // cpuModel from "model"
1137 (void)Tokenizer(keyValuePairs["model"_ns]).ReadInteger(&cpuModel);
1139 // cpuStepping from "stepping"
1140 (void)Tokenizer(keyValuePairs["stepping"_ns]).ReadInteger(&cpuStepping);
1141 # endif
1145 // Get cpuSpeed from another file.
1146 std::ifstream input(
1147 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
1148 std::string line;
1149 if (getline(input, line)) {
1150 (void)Tokenizer(line.c_str()).ReadInteger(&cpuSpeed);
1151 cpuSpeed /= 1000;
1156 // Get cacheSizeL2 from yet another file
1157 std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index2/size");
1158 std::string line;
1159 if (getline(input, line)) {
1160 (void)Tokenizer(line.c_str(), nullptr, "K").ReadInteger(&cacheSizeL2);
1165 // Get cacheSizeL3 from yet another file
1166 std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index3/size");
1167 std::string line;
1168 if (getline(input, line)) {
1169 (void)Tokenizer(line.c_str(), nullptr, "K").ReadInteger(&cacheSizeL3);
1173 info.cpuCount = PR_GetNumberOfProcessors();
1174 int max_cpu_bits = [&] {
1175 // PR_GetNumberOfProcessors gets the value from
1176 // /sys/devices/system/cpu/present, but the number of bits in the CPU masks
1177 // we're going to read below can be larger (for instance, on the 32-core
1178 // 64-threads Threadripper 3970X, PR_GetNumberOfProcessors returns 64, but
1179 // the number of bits in the CPU masks is 128). That number of bits is
1180 // correlated with the number of CPUs possible (which is different from the
1181 // number of CPUs present).
1182 std::ifstream input("/sys/devices/system/cpu/possible");
1183 std::string line;
1184 if (getline(input, line)) {
1185 int num;
1186 Tokenizer p(line.c_str());
1187 // The expected format is `0-n` where n is the number of CPUs possible
1188 // - 1.
1189 if (p.ReadInteger(&num) && num == 0 && p.CheckChar('-') &&
1190 p.ReadInteger(&num) && p.CheckEOF()) {
1191 return num + 1;
1194 // If we weren't able to get the value from /sys/devices/system/cpu/possible
1195 // from some reason, fallback to cpuCount, it might work.
1196 return info.cpuCount;
1197 }();
1199 // /proc/cpuinfo doesn't have a cross-architecture way of counting physical
1200 // cores. On x86, one could look at the number of unique combinations of
1201 // `physical id` and `core id` or `cpu cores`, but those are not present on
1202 // e.g. aarch64. (and that might not even be enough for NUMA nodes, but
1203 // realistically, there probably aren't a lot of people running this code
1204 // on such machines)
1205 // As a shortcut on x86, you'd think you could just multiply the last
1206 // physical id + 1 with the last core id + 1, but at least core ids are not
1207 // even necessarily adjacent. (notably, on 13th or 14th generation Intel
1208 // CPUs, they go in increments of 4 for performance cores, and then 1 after
1209 // hitting the first efficiency core)
1210 // /sys/devices/system/cpu/cpu*/topology/core_cpus does show which logical
1211 // cores are associated together, such that running the command:
1212 // sort -u /sys/devices/system/cpu/cpu*/topology/core_cpus | wc -l
1213 // gives a count of physical cores.
1214 // There are cpuCount /sys/devices/system/cpu/cpu* directories, and they
1215 // are monotonically increasing.
1216 // We're going to kind of do that, but reading the actual bitmasks contained
1217 // in those files.
1218 constexpr int mask_bits = sizeof(uint32_t) * 8;
1220 Vector<uint32_t> cpumasks;
1221 physicalCPUs = [&] {
1222 int cores = 0;
1223 if (!cpumasks.appendN(0, (max_cpu_bits + mask_bits - 1) / mask_bits)) {
1224 return -1;
1226 for (int32_t cpu = 0; cpu < info.cpuCount; ++cpu) {
1227 nsPrintfCString core_cpus(
1228 "/sys/devices/system/cpu/cpu%d/topology/core_cpus", cpu);
1229 std::ifstream input(core_cpus.Data());
1230 // Kernel versions before 5.3 didn't have core_cpus, they had
1231 // thread_siblings instead, with the same content. As of writing, kernel
1232 // version 6.9 still has both, but thread_siblings has been deprecated
1233 // since the introduction of core_cpus.
1234 if (input.fail()) {
1235 core_cpus.Truncate(core_cpus.Length() - sizeof("core_cpus") + 1);
1236 core_cpus.AppendLiteral("thread_siblings");
1237 input.open(core_cpus.Data());
1239 std::string line;
1240 if (!getline(input, line)) {
1241 return -1;
1243 Tokenizer p(line.c_str());
1244 bool unknown_core = false;
1245 // The format of the file is `bitmask0,bitmask1,..,bitmaskn`
1246 // where each bitmask is 32-bits wide, and there are as many as
1247 // necessary to print max_cpu_bits bits.
1248 for (auto& mask : cpumasks) {
1249 uint32_t m;
1250 if (NS_WARN_IF(!p.ReadHexadecimal(&m, /* aPrefixed = */ false))) {
1251 return -1;
1253 if (!p.CheckEOF() && !p.CheckChar(',')) {
1254 return -1;
1256 // We're keeping track of all the CPU bits we've seen so far.
1257 // If we're now seeing one that has never been set, it means
1258 // we're seeing a new physical core (as opposed to a logical
1259 // core). We don't want to end the loop now, though, because
1260 // we also want to track all the bits we're seeing, in case
1261 // subsequent masks have new bits as well.
1262 if ((mask & m) != m) {
1263 unknown_core = true;
1265 mask |= m;
1267 if (unknown_core) {
1268 cores++;
1271 return cores;
1272 }();
1274 #else
1275 info.cpuCount = PR_GetNumberOfProcessors();
1276 #endif
1277 if (Maybe<hal::HeterogeneousCpuInfo> hetCpuInfo =
1278 hal::GetHeterogeneousCpuInfo()) {
1279 info.cpuPCount = int32_t(hetCpuInfo->mBigCpus.Count());
1280 info.cpuMCount = int32_t(hetCpuInfo->mMediumCpus.Count());
1281 info.cpuECount = int32_t(hetCpuInfo->mLittleCpus.Count());
1282 } else {
1283 info.cpuPCount = physicalCPUs;
1284 info.cpuMCount = 0;
1285 info.cpuECount = 0;
1288 if (cpuSpeed >= 0) {
1289 info.cpuSpeed = cpuSpeed;
1290 } else {
1291 info.cpuSpeed = 0;
1293 if (!cpuVendor.IsEmpty()) {
1294 info.cpuVendor = cpuVendor;
1296 if (!cpuName.IsEmpty()) {
1297 info.cpuName = cpuName;
1299 if (cpuFamily >= 0) {
1300 info.cpuFamily = cpuFamily;
1302 if (cpuModel >= 0) {
1303 info.cpuModel = cpuModel;
1305 if (cpuStepping >= 0) {
1306 info.cpuStepping = cpuStepping;
1309 if (logicalCPUs >= 0) {
1310 info.cpuCount = logicalCPUs;
1312 if (physicalCPUs >= 0) {
1313 info.cpuCores = physicalCPUs;
1316 if (cacheSizeL2 >= 0) {
1317 info.l2cacheKB = cacheSizeL2;
1319 if (cacheSizeL3 >= 0) {
1320 info.l3cacheKB = cacheSizeL3;
1323 return NS_OK;
1326 #if defined(__MINGW32__)
1327 WINBASEAPI
1328 BOOL WINAPI IsUserCetAvailableInEnvironment(_In_ DWORD UserCetEnvironment);
1330 # define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
1331 #endif
1333 nsresult nsSystemInfo::Init() {
1334 // check that it is called from the main thread on all platforms.
1335 MOZ_ASSERT(NS_IsMainThread());
1337 nsresult rv;
1339 static const struct {
1340 PRSysInfo cmd;
1341 const char* name;
1342 } items[] = {{PR_SI_SYSNAME, "name"},
1343 {PR_SI_ARCHITECTURE, "arch"},
1344 {PR_SI_RELEASE, "version"},
1345 {PR_SI_RELEASE_BUILD, "build"}};
1347 for (uint32_t i = 0; i < (sizeof(items) / sizeof(items[0])); i++) {
1348 char buf[SYS_INFO_BUFFER_LENGTH];
1349 if (PR_GetSystemInfo(items[i].cmd, buf, sizeof(buf)) == PR_SUCCESS) {
1350 rv = SetPropertyAsACString(NS_ConvertASCIItoUTF16(items[i].name),
1351 nsDependentCString(buf));
1352 if (NS_WARN_IF(NS_FAILED(rv))) {
1353 return rv;
1355 } else {
1356 NS_WARNING("PR_GetSystemInfo failed");
1360 SetPropertyAsBool(u"isPackagedApp"_ns, false);
1362 // Additional informations not available through PR_GetSystemInfo.
1363 SetInt32Property(u"pagesize"_ns, PR_GetPageSize());
1364 SetInt32Property(u"pageshift"_ns, PR_GetPageShift());
1365 SetInt32Property(u"memmapalign"_ns, PR_GetMemMapAlignment());
1366 SetUint64Property(u"memsize"_ns, PR_GetPhysicalMemorySize());
1367 SetUint32Property(u"umask"_ns, nsSystemInfo::gUserUmask);
1369 #ifdef HAVE_64BIT_BUILD
1370 SetUint32Property(u"archbits"_ns, 64);
1371 #else
1372 SetUint32Property(u"archbits"_ns, 32);
1373 #endif
1375 uint64_t virtualMem = 0;
1377 #if defined(XP_WIN)
1378 // Virtual memory:
1379 MEMORYSTATUSEX memStat;
1380 memStat.dwLength = sizeof(memStat);
1381 if (GlobalMemoryStatusEx(&memStat)) {
1382 virtualMem = memStat.ullTotalVirtual;
1384 #endif
1385 if (virtualMem) SetUint64Property(u"virtualmemsize"_ns, virtualMem);
1387 for (uint32_t i = 0; i < std::size(cpuPropItems); i++) {
1388 rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems[i].name),
1389 cpuPropItems[i].propfun());
1390 if (NS_WARN_IF(NS_FAILED(rv))) {
1391 return rv;
1395 #ifdef XP_WIN
1396 bool isMinGW =
1397 # ifdef __MINGW32__
1398 true;
1399 # else
1400 false;
1401 # endif
1402 rv = SetPropertyAsBool(u"isMinGW"_ns, !!isMinGW);
1403 if (NS_WARN_IF(NS_FAILED(rv))) {
1404 return rv;
1407 boolean hasPackageIdentity = widget::WinUtils::HasPackageIdentity();
1409 rv = SetPropertyAsBool(u"hasWinPackageId"_ns, hasPackageIdentity);
1410 if (NS_WARN_IF(NS_FAILED(rv))) {
1411 return rv;
1414 rv = SetPropertyAsAString(u"winPackageFamilyName"_ns,
1415 widget::WinUtils::GetPackageFamilyName());
1416 if (NS_WARN_IF(NS_FAILED(rv))) {
1417 return rv;
1420 rv = SetPropertyAsBool(u"isPackagedApp"_ns, hasPackageIdentity);
1421 if (NS_WARN_IF(NS_FAILED(rv))) {
1422 return rv;
1425 # ifndef __MINGW32__
1426 nsAutoString avInfo, antiSpyInfo, firewallInfo;
1427 if (NS_SUCCEEDED(
1428 GetWindowsSecurityCenterInfo(avInfo, antiSpyInfo, firewallInfo))) {
1429 if (!avInfo.IsEmpty()) {
1430 rv = SetPropertyAsAString(u"registeredAntiVirus"_ns, avInfo);
1431 if (NS_WARN_IF(NS_FAILED(rv))) {
1432 return rv;
1436 if (!antiSpyInfo.IsEmpty()) {
1437 rv = SetPropertyAsAString(u"registeredAntiSpyware"_ns, antiSpyInfo);
1438 if (NS_WARN_IF(NS_FAILED(rv))) {
1439 return rv;
1443 if (!firewallInfo.IsEmpty()) {
1444 rv = SetPropertyAsAString(u"registeredFirewall"_ns, firewallInfo);
1445 if (NS_WARN_IF(NS_FAILED(rv))) {
1446 return rv;
1450 # endif // __MINGW32__
1452 mozilla::DynamicallyLinkedFunctionPtr<
1453 decltype(&IsUserCetAvailableInEnvironment)>
1454 isUserCetAvailable(L"api-ms-win-core-sysinfo-l1-2-6.dll",
1455 "IsUserCetAvailableInEnvironment");
1456 bool hasUserCET = isUserCetAvailable &&
1457 isUserCetAvailable(USER_CET_ENVIRONMENT_WIN32_PROCESS);
1458 rv = SetPropertyAsBool(u"hasUserCET"_ns, hasUserCET);
1459 if (NS_WARN_IF(NS_FAILED(rv))) {
1460 return rv;
1463 #endif
1465 #if defined(XP_WIN) || defined(ANDROID)
1466 // TODO(krosylight): Enable this on other platforms too when implemented
1467 if (XRE_IsParentProcess()) {
1468 auto kinds = static_cast<LookAndFeel::PointingDeviceKinds>(
1469 LookAndFeel::GetInt(LookAndFeel::IntID::PointingDeviceKinds, 0));
1470 MOZ_TRY(SetPropertyAsBool(
1471 u"hasMouse"_ns, !!(kinds & LookAndFeel::PointingDeviceKinds::Mouse)));
1472 MOZ_TRY(SetPropertyAsBool(
1473 u"hasTouch"_ns, !!(kinds & LookAndFeel::PointingDeviceKinds::Touch)));
1474 MOZ_TRY(SetPropertyAsBool(
1475 u"hasPen"_ns, !!(kinds & LookAndFeel::PointingDeviceKinds::Pen)));
1477 #endif
1479 #if defined(XP_MACOSX)
1480 nsAutoCString modelId;
1481 if (NS_SUCCEEDED(GetAppleModelId(modelId))) {
1482 rv = SetPropertyAsACString(u"appleModelId"_ns, modelId);
1483 NS_ENSURE_SUCCESS(rv, rv);
1485 bool isRosetta;
1486 if (NS_SUCCEEDED(ProcessIsRosettaTranslated(isRosetta))) {
1487 rv = SetPropertyAsBool(u"rosettaStatus"_ns, isRosetta);
1488 NS_ENSURE_SUCCESS(rv, rv);
1490 #endif
1493 nsAutoCString themeInfo;
1494 LookAndFeel::GetThemeInfo(themeInfo);
1495 MOZ_TRY(SetPropertyAsACString(u"osThemeInfo"_ns, themeInfo));
1498 #if defined(MOZ_WIDGET_GTK)
1499 // This must be done here because NSPR can only separate OS's when compiled,
1500 // not libraries. 64 bytes is going to be well enough for "GTK " followed by 3
1501 // integers separated with dots.
1502 char gtkver[64];
1503 ssize_t gtkver_len = 0;
1505 if (gtkver_len <= 0) {
1506 gtkver_len = SprintfLiteral(gtkver, "GTK %u.%u.%u", gtk_major_version,
1507 gtk_minor_version, gtk_micro_version);
1510 nsAutoCString secondaryLibrary;
1511 if (gtkver_len > 0 && gtkver_len < int(sizeof(gtkver))) {
1512 secondaryLibrary.Append(nsDependentCSubstring(gtkver, gtkver_len));
1515 # ifndef MOZ_TSAN
1516 // With TSan, avoid loading libpulse here because we cannot unload it
1517 // afterwards due to restrictions from TSan about unloading libraries
1518 // matched by the suppression list.
1519 void* libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
1520 const char* libpulseVersion = "not-available";
1521 if (libpulse) {
1522 auto pa_get_library_version = reinterpret_cast<const char* (*)()>(
1523 dlsym(libpulse, "pa_get_library_version"));
1525 if (pa_get_library_version) {
1526 libpulseVersion = pa_get_library_version();
1530 secondaryLibrary.AppendPrintf(",libpulse %s", libpulseVersion);
1532 if (libpulse) {
1533 dlclose(libpulse);
1535 # endif
1537 rv = SetPropertyAsACString(u"secondaryLibrary"_ns, secondaryLibrary);
1538 if (NS_WARN_IF(NS_FAILED(rv))) {
1539 return rv;
1541 rv = SetPropertyAsBool(u"isPackagedApp"_ns,
1542 widget::IsRunningUnderFlatpakOrSnap() ||
1543 widget::IsPackagedAppFileExists());
1544 if (NS_WARN_IF(NS_FAILED(rv))) {
1545 return rv;
1547 #endif
1549 #ifdef MOZ_WIDGET_ANDROID
1550 AndroidSystemInfo info;
1551 GetAndroidSystemInfo(&info);
1552 SetupAndroidInfo(info);
1553 #endif
1555 #if defined(XP_LINUX) && !defined(ANDROID)
1556 nsCString dist, desc, release, codename;
1557 if (widget::lsb::GetLSBRelease(dist, desc, release, codename)) {
1558 SetPropertyAsACString(u"distro"_ns, dist);
1559 SetPropertyAsACString(u"distroVersion"_ns, release);
1561 #endif
1563 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
1564 SandboxInfo sandInfo = SandboxInfo::Get();
1566 SetPropertyAsBool(u"hasSeccompBPF"_ns,
1567 sandInfo.Test(SandboxInfo::kHasSeccompBPF));
1568 SetPropertyAsBool(u"hasSeccompTSync"_ns,
1569 sandInfo.Test(SandboxInfo::kHasSeccompTSync));
1570 SetPropertyAsBool(u"hasUserNamespaces"_ns,
1571 sandInfo.Test(SandboxInfo::kHasUserNamespaces));
1572 SetPropertyAsBool(u"hasPrivilegedUserNamespaces"_ns,
1573 sandInfo.Test(SandboxInfo::kHasPrivilegedUserNamespaces));
1575 if (sandInfo.Test(SandboxInfo::kEnabledForContent)) {
1576 SetPropertyAsBool(u"canSandboxContent"_ns, sandInfo.CanSandboxContent());
1579 if (sandInfo.Test(SandboxInfo::kEnabledForMedia)) {
1580 SetPropertyAsBool(u"canSandboxMedia"_ns, sandInfo.CanSandboxMedia());
1582 #endif // XP_LINUX && MOZ_SANDBOX
1584 return NS_OK;
1587 #ifdef MOZ_WIDGET_ANDROID
1588 // Prerelease versions of Android use a letter instead of version numbers.
1589 // Unfortunately this breaks websites due to the user agent.
1590 // Chrome works around this by hardcoding an Android version when a
1591 // numeric version can't be obtained. We're doing the same.
1592 // This version will need to be updated whenever there is a new official
1593 // Android release. Search for "kDefaultAndroidMajorVersion" in:
1594 // https://source.chromium.org/chromium/chromium/src/+/master:base/system/sys_info_android.cc
1595 # define DEFAULT_ANDROID_VERSION u"10.0.99"
1597 /* static */
1598 void nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo* aInfo) {
1599 if (!jni::IsAvailable()) {
1600 // called from xpcshell etc.
1601 aInfo->sdk_version() = 0;
1602 return;
1605 jni::String::LocalRef model = java::sdk::Build::MODEL();
1606 aInfo->device() = model->ToString();
1608 jni::String::LocalRef manufacturer =
1609 mozilla::java::sdk::Build::MANUFACTURER();
1610 aInfo->manufacturer() = manufacturer->ToString();
1612 jni::String::LocalRef hardware = java::sdk::Build::HARDWARE();
1613 aInfo->hardware() = hardware->ToString();
1615 jni::String::LocalRef release = java::sdk::Build::VERSION::RELEASE();
1616 nsString str(release->ToString());
1617 int major_version;
1618 int minor_version;
1619 int bugfix_version;
1620 int num_read = sscanf(NS_ConvertUTF16toUTF8(str).get(), "%d.%d.%d",
1621 &major_version, &minor_version, &bugfix_version);
1622 if (num_read == 0) {
1623 aInfo->release_version() = nsLiteralString(DEFAULT_ANDROID_VERSION);
1624 } else {
1625 aInfo->release_version() = str;
1628 aInfo->sdk_version() = jni::GetAPIVersion();
1629 aInfo->isTablet() = java::GeckoAppShell::IsTablet();
1632 void nsSystemInfo::SetupAndroidInfo(const AndroidSystemInfo& aInfo) {
1633 if (!aInfo.device().IsEmpty()) {
1634 SetPropertyAsAString(u"device"_ns, aInfo.device());
1636 if (!aInfo.manufacturer().IsEmpty()) {
1637 SetPropertyAsAString(u"manufacturer"_ns, aInfo.manufacturer());
1639 if (!aInfo.release_version().IsEmpty()) {
1640 SetPropertyAsAString(u"release_version"_ns, aInfo.release_version());
1642 SetPropertyAsBool(u"tablet"_ns, aInfo.isTablet());
1643 // NSPR "version" is the kernel version. For Android we want the Android
1644 // version. Rename SDK version to version and put the kernel version into
1645 // kernel_version.
1646 nsAutoString str;
1647 nsresult rv = GetPropertyAsAString(u"version"_ns, str);
1648 if (NS_SUCCEEDED(rv)) {
1649 SetPropertyAsAString(u"kernel_version"_ns, str);
1651 // When JNI is not available (eg. in xpcshell tests), sdk_version is 0.
1652 if (aInfo.sdk_version() != 0) {
1653 if (!aInfo.hardware().IsEmpty()) {
1654 SetPropertyAsAString(u"hardware"_ns, aInfo.hardware());
1656 SetPropertyAsInt32(u"version"_ns, aInfo.sdk_version());
1659 #endif // MOZ_WIDGET_ANDROID
1661 void nsSystemInfo::SetInt32Property(const nsAString& aPropertyName,
1662 const int32_t aValue) {
1663 NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
1664 if (aValue > 0) {
1665 #ifdef DEBUG
1666 nsresult rv =
1667 #endif
1668 SetPropertyAsInt32(aPropertyName, aValue);
1669 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
1673 void nsSystemInfo::SetUint32Property(const nsAString& aPropertyName,
1674 const uint32_t aValue) {
1675 // Only one property is currently set via this function.
1676 // It may legitimately be zero.
1677 #ifdef DEBUG
1678 nsresult rv =
1679 #endif
1680 SetPropertyAsUint32(aPropertyName, aValue);
1681 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
1684 void nsSystemInfo::SetUint64Property(const nsAString& aPropertyName,
1685 const uint64_t aValue) {
1686 NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
1687 if (aValue > 0) {
1688 #ifdef DEBUG
1689 nsresult rv =
1690 #endif
1691 SetPropertyAsUint64(aPropertyName, aValue);
1692 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
1696 #ifdef XP_WIN
1698 static bool GetJSObjForDiskInfo(JSContext* aCx, JS::Handle<JSObject*> aParent,
1699 const FolderDiskInfo& info,
1700 const char* propName) {
1701 JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
1702 if (!jsInfo) {
1703 return false;
1706 JSString* strModel =
1707 JS_NewStringCopyN(aCx, info.model.get(), info.model.Length());
1708 if (!strModel) {
1709 return false;
1711 JS::Rooted<JS::Value> valModel(aCx, JS::StringValue(strModel));
1712 if (!JS_SetProperty(aCx, jsInfo, "model", valModel)) {
1713 return false;
1716 JSString* strRevision =
1717 JS_NewStringCopyN(aCx, info.revision.get(), info.revision.Length());
1718 if (!strRevision) {
1719 return false;
1721 JS::Rooted<JS::Value> valRevision(aCx, JS::StringValue(strRevision));
1722 if (!JS_SetProperty(aCx, jsInfo, "revision", valRevision)) {
1723 return false;
1726 JSString* strSSD = JS_NewStringCopyZ(aCx, info.isSSD ? "SSD" : "HDD");
1727 if (!strSSD) {
1728 return false;
1730 JS::Rooted<JS::Value> valSSD(aCx, JS::StringValue(strSSD));
1731 if (!JS_SetProperty(aCx, jsInfo, "type", valSSD)) {
1732 return false;
1735 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*jsInfo));
1736 return JS_SetProperty(aCx, aParent, propName, val);
1739 JSObject* GetJSObjForOSInfo(JSContext* aCx, const OSInfo& info) {
1740 JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
1742 JS::Rooted<JS::Value> valInstallYear(aCx, JS::Int32Value(info.installYear));
1743 JS_SetProperty(aCx, jsInfo, "installYear", valInstallYear);
1745 JS::Rooted<JS::Value> valHasSuperfetch(aCx,
1746 JS::BooleanValue(info.hasSuperfetch));
1747 JS_SetProperty(aCx, jsInfo, "hasSuperfetch", valHasSuperfetch);
1749 JS::Rooted<JS::Value> valHasPrefetch(aCx, JS::BooleanValue(info.hasPrefetch));
1750 JS_SetProperty(aCx, jsInfo, "hasPrefetch", valHasPrefetch);
1752 return jsInfo;
1755 #endif
1757 JSObject* GetJSObjForProcessInfo(JSContext* aCx, const ProcessInfo& info) {
1758 JS::Rooted<JSObject*> jsInfo(aCx, JS_NewPlainObject(aCx));
1760 #if defined(XP_WIN)
1761 JS::Rooted<JS::Value> valisWow64(aCx, JS::BooleanValue(info.isWow64));
1762 JS_SetProperty(aCx, jsInfo, "isWow64", valisWow64);
1764 JS::Rooted<JS::Value> valisWowARM64(aCx, JS::BooleanValue(info.isWowARM64));
1765 JS_SetProperty(aCx, jsInfo, "isWowARM64", valisWowARM64);
1767 JS::Rooted<JS::Value> valisWindowsSMode(
1768 aCx, JS::BooleanValue(info.isWindowsSMode));
1769 JS_SetProperty(aCx, jsInfo, "isWindowsSMode", valisWindowsSMode);
1770 #endif
1772 JS::Rooted<JS::Value> valCountInfo(aCx, JS::Int32Value(info.cpuCount));
1773 JS_SetProperty(aCx, jsInfo, "count", valCountInfo);
1775 JS::Rooted<JS::Value> valCoreInfo(
1776 aCx, info.cpuCores ? JS::Int32Value(info.cpuCores) : JS::NullValue());
1777 JS_SetProperty(aCx, jsInfo, "cores", valCoreInfo);
1779 JS::Rooted<JS::Value> valPCountInfo(
1780 aCx, info.cpuCores ? JS::Int32Value(info.cpuPCount) : JS::NullValue());
1781 JS_SetProperty(aCx, jsInfo, "pcount", valPCountInfo);
1783 JS::Rooted<JS::Value> valMCountInfo(
1784 aCx, info.cpuCores ? JS::Int32Value(info.cpuMCount) : JS::NullValue());
1785 JS_SetProperty(aCx, jsInfo, "mcount", valMCountInfo);
1787 JS::Rooted<JS::Value> valECountInfo(
1788 aCx, info.cpuCores ? JS::Int32Value(info.cpuECount) : JS::NullValue());
1789 JS_SetProperty(aCx, jsInfo, "ecount", valECountInfo);
1791 JSString* strVendor =
1792 JS_NewStringCopyN(aCx, info.cpuVendor.get(), info.cpuVendor.Length());
1793 JS::Rooted<JS::Value> valVendor(aCx, JS::StringValue(strVendor));
1794 JS_SetProperty(aCx, jsInfo, "vendor", valVendor);
1796 JSString* strName =
1797 JS_NewStringCopyN(aCx, info.cpuName.get(), info.cpuName.Length());
1798 JS::Rooted<JS::Value> valName(aCx, JS::StringValue(strName));
1799 JS_SetProperty(aCx, jsInfo, "name", valName);
1801 JS::Rooted<JS::Value> valFamilyInfo(aCx, JS::Int32Value(info.cpuFamily));
1802 JS_SetProperty(aCx, jsInfo, "family", valFamilyInfo);
1804 JS::Rooted<JS::Value> valModelInfo(aCx, JS::Int32Value(info.cpuModel));
1805 JS_SetProperty(aCx, jsInfo, "model", valModelInfo);
1807 JS::Rooted<JS::Value> valSteppingInfo(aCx, JS::Int32Value(info.cpuStepping));
1808 JS_SetProperty(aCx, jsInfo, "stepping", valSteppingInfo);
1810 JS::Rooted<JS::Value> valL2CacheInfo(aCx, JS::Int32Value(info.l2cacheKB));
1811 JS_SetProperty(aCx, jsInfo, "l2cacheKB", valL2CacheInfo);
1813 JS::Rooted<JS::Value> valL3CacheInfo(aCx, JS::Int32Value(info.l3cacheKB));
1814 JS_SetProperty(aCx, jsInfo, "l3cacheKB", valL3CacheInfo);
1816 JS::Rooted<JS::Value> valSpeedInfo(aCx, JS::Int32Value(info.cpuSpeed));
1817 JS_SetProperty(aCx, jsInfo, "speedMHz", valSpeedInfo);
1819 return jsInfo;
1822 RefPtr<nsISerialEventTarget> nsSystemInfo::GetBackgroundTarget() {
1823 if (!mBackgroundET) {
1824 MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
1825 "SystemInfoThread", getter_AddRefs(mBackgroundET)));
1827 return mBackgroundET;
1830 NS_IMETHODIMP
1831 nsSystemInfo::GetOsInfo(JSContext* aCx, Promise** aResult) {
1832 NS_ENSURE_ARG_POINTER(aResult);
1833 *aResult = nullptr;
1834 if (!XRE_IsParentProcess()) {
1835 return NS_ERROR_FAILURE;
1837 #if defined(XP_WIN)
1838 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
1839 if (NS_WARN_IF(!global)) {
1840 return NS_ERROR_FAILURE;
1843 ErrorResult erv;
1844 RefPtr<Promise> promise = Promise::Create(global, erv);
1845 if (NS_WARN_IF(erv.Failed())) {
1846 return erv.StealNSResult();
1849 if (!mOSInfoPromise) {
1850 RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
1852 mOSInfoPromise = InvokeAsync(backgroundET, __func__, []() {
1853 OSInfo info;
1854 nsresult rv = CollectOSInfo(info);
1855 if (NS_SUCCEEDED(rv)) {
1856 return OSInfoPromise::CreateAndResolve(info, __func__);
1858 return OSInfoPromise::CreateAndReject(rv, __func__);
1862 // Chain the new promise to the extant mozpromise
1863 RefPtr<Promise> capturedPromise = promise;
1864 mOSInfoPromise->Then(
1865 GetMainThreadSerialEventTarget(), __func__,
1866 [capturedPromise](const OSInfo& info) {
1867 AutoJSAPI jsapi;
1868 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1869 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1870 return;
1872 JSContext* cx = jsapi.cx();
1873 JS::Rooted<JS::Value> val(
1874 cx, JS::ObjectValue(*GetJSObjForOSInfo(cx, info)));
1875 capturedPromise->MaybeResolve(val);
1877 [capturedPromise](const nsresult rv) {
1878 // Resolve with null when installYear is not available from the system
1879 capturedPromise->MaybeResolve(JS::NullHandleValue);
1882 promise.forget(aResult);
1883 #endif
1884 return NS_OK;
1887 NS_IMETHODIMP
1888 nsSystemInfo::GetDiskInfo(JSContext* aCx, Promise** aResult) {
1889 NS_ENSURE_ARG_POINTER(aResult);
1890 *aResult = nullptr;
1891 if (!XRE_IsParentProcess()) {
1892 return NS_ERROR_FAILURE;
1894 #ifdef XP_WIN
1895 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
1896 if (NS_WARN_IF(!global)) {
1897 return NS_ERROR_FAILURE;
1899 ErrorResult erv;
1900 RefPtr<Promise> promise = Promise::Create(global, erv);
1901 if (NS_WARN_IF(erv.Failed())) {
1902 return erv.StealNSResult();
1905 if (!mDiskInfoPromise) {
1906 RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
1907 nsCOMPtr<nsIFile> greDir;
1908 nsCOMPtr<nsIFile> winDir;
1909 nsCOMPtr<nsIFile> profDir;
1910 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir));
1911 if (NS_FAILED(rv)) {
1912 return rv;
1914 rv = NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR, getter_AddRefs(winDir));
1915 if (NS_FAILED(rv)) {
1916 return rv;
1918 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1919 getter_AddRefs(profDir));
1920 if (NS_FAILED(rv)) {
1921 return rv;
1924 mDiskInfoPromise =
1925 InvokeAsync(backgroundET, __func__, [greDir, winDir, profDir]() {
1926 DiskInfo info;
1927 nsresult rv = CollectDiskInfo(greDir, winDir, profDir, info);
1928 if (NS_SUCCEEDED(rv)) {
1929 return DiskInfoPromise::CreateAndResolve(info, __func__);
1931 return DiskInfoPromise::CreateAndReject(rv, __func__);
1935 // Chain the new promise to the extant mozpromise.
1936 RefPtr<Promise> capturedPromise = promise;
1937 mDiskInfoPromise->Then(
1938 GetMainThreadSerialEventTarget(), __func__,
1939 [capturedPromise](const DiskInfo& info) {
1940 AutoJSAPI jsapi;
1941 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
1942 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1943 return;
1945 JSContext* cx = jsapi.cx();
1946 JS::Rooted<JSObject*> jsInfo(cx, JS_NewPlainObject(cx));
1947 // Store data in the rv:
1948 bool succeededSettingAllObjects =
1949 jsInfo && GetJSObjForDiskInfo(cx, jsInfo, info.binary, "binary") &&
1950 GetJSObjForDiskInfo(cx, jsInfo, info.profile, "profile") &&
1951 GetJSObjForDiskInfo(cx, jsInfo, info.system, "system");
1952 // The above can fail due to OOM
1953 if (!succeededSettingAllObjects) {
1954 JS_ClearPendingException(cx);
1955 capturedPromise->MaybeReject(NS_ERROR_FAILURE);
1956 return;
1959 JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*jsInfo));
1960 capturedPromise->MaybeResolve(val);
1962 [capturedPromise](const nsresult rv) {
1963 capturedPromise->MaybeReject(rv);
1966 promise.forget(aResult);
1967 #endif
1968 return NS_OK;
1971 NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo, nsHashPropertyBag, nsISystemInfo)
1973 NS_IMETHODIMP
1974 nsSystemInfo::GetCountryCode(JSContext* aCx, Promise** aResult) {
1975 NS_ENSURE_ARG_POINTER(aResult);
1976 *aResult = nullptr;
1978 if (!XRE_IsParentProcess()) {
1979 return NS_ERROR_FAILURE;
1981 #if defined(XP_MACOSX) || defined(XP_WIN)
1982 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
1983 if (NS_WARN_IF(!global)) {
1984 return NS_ERROR_FAILURE;
1987 ErrorResult erv;
1988 RefPtr<Promise> promise = Promise::Create(global, erv);
1989 if (NS_WARN_IF(erv.Failed())) {
1990 return erv.StealNSResult();
1993 if (!mCountryCodePromise) {
1994 RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
1996 mCountryCodePromise = InvokeAsync(backgroundET, __func__, []() {
1997 nsAutoString countryCode;
1998 # ifdef XP_MACOSX
1999 nsresult rv = GetSelectedCityInfo(countryCode);
2000 # endif
2001 # ifdef XP_WIN
2002 nsresult rv = CollectCountryCode(countryCode);
2003 # endif
2005 if (NS_SUCCEEDED(rv)) {
2006 return CountryCodePromise::CreateAndResolve(countryCode, __func__);
2008 return CountryCodePromise::CreateAndReject(rv, __func__);
2012 RefPtr<Promise> capturedPromise = promise;
2013 mCountryCodePromise->Then(
2014 GetMainThreadSerialEventTarget(), __func__,
2015 [capturedPromise](const nsString& countryCode) {
2016 AutoJSAPI jsapi;
2017 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
2018 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
2019 return;
2021 JSContext* cx = jsapi.cx();
2022 JS::Rooted<JSString*> jsCountryCode(
2023 cx, JS_NewUCStringCopyZ(cx, countryCode.get()));
2025 JS::Rooted<JS::Value> val(cx, JS::StringValue(jsCountryCode));
2026 capturedPromise->MaybeResolve(val);
2028 [capturedPromise](const nsresult rv) {
2029 // Resolve with null when countryCode is not available from the system
2030 capturedPromise->MaybeResolve(JS::NullHandleValue);
2033 promise.forget(aResult);
2034 #endif
2035 return NS_OK;
2038 NS_IMETHODIMP
2039 nsSystemInfo::GetProcessInfo(JSContext* aCx, Promise** aResult) {
2040 NS_ENSURE_ARG_POINTER(aResult);
2041 *aResult = nullptr;
2043 if (!XRE_IsParentProcess()) {
2044 return NS_ERROR_FAILURE;
2047 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
2048 if (NS_WARN_IF(!global)) {
2049 return NS_ERROR_FAILURE;
2052 ErrorResult erv;
2053 RefPtr<Promise> promise = Promise::Create(global, erv);
2054 if (NS_WARN_IF(erv.Failed())) {
2055 return erv.StealNSResult();
2058 if (!mProcessInfoPromise) {
2059 RefPtr<nsISerialEventTarget> backgroundET = GetBackgroundTarget();
2061 mProcessInfoPromise = InvokeAsync(backgroundET, __func__, []() {
2062 ProcessInfo info;
2063 nsresult rv = CollectProcessInfo(info);
2064 if (NS_SUCCEEDED(rv)) {
2065 return ProcessInfoPromise::CreateAndResolve(info, __func__);
2067 return ProcessInfoPromise::CreateAndReject(rv, __func__);
2071 // Chain the new promise to the extant mozpromise
2072 RefPtr<Promise> capturedPromise = promise;
2073 mProcessInfoPromise->Then(
2074 GetMainThreadSerialEventTarget(), __func__,
2075 [capturedPromise](const ProcessInfo& info) {
2076 AutoJSAPI jsapi;
2077 if (NS_WARN_IF(!jsapi.Init(capturedPromise->GetGlobalObject()))) {
2078 capturedPromise->MaybeReject(NS_ERROR_UNEXPECTED);
2079 return;
2081 JSContext* cx = jsapi.cx();
2082 JS::Rooted<JS::Value> val(
2083 cx, JS::ObjectValue(*GetJSObjForProcessInfo(cx, info)));
2084 capturedPromise->MaybeResolve(val);
2086 [capturedPromise](const nsresult rv) {
2087 // Resolve with null when installYear is not available from the system
2088 capturedPromise->MaybeResolve(JS::NullHandleValue);
2091 promise.forget(aResult);
2093 return NS_OK;