1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
3 #if defined(_WIN32)
4 #  define NOMINMAX // use our min,max
5 #  if !defined(_WIN32_WINNT) && defined(_MSC_VER) && _MSC_VER >= 1800
6 #    define _WIN32_WINNT 0x0600 // vista
7 #  endif
8 #  if !defined(_WIN32_WINNT) && !(defined(_MSC_VER) && _MSC_VER < 1300)
9 #    define _WIN32_WINNT 0x0501
10 #  endif
11 #  include <winsock.h> // WSADATA, include before sys/types.h
12 #endif
13 
14 #if (defined(__GNUC__) || defined(__PGI)) && !defined(_GNU_SOURCE)
15 #  define _GNU_SOURCE
16 #endif
17 
18 // TODO:
19 // We need an alternative implementation for many functions in this file
20 // when USE_ASM_INSTRUCTIONS gets defined as 0.
21 //
22 // Consider using these on Win32/Win64 for some of them:
23 //
24 // IsProcessorFeaturePresent
25 // http://msdn.microsoft.com/en-us/library/ms724482(VS.85).aspx
26 //
27 // GetProcessMemoryInfo
28 // http://msdn.microsoft.com/en-us/library/ms683219(VS.85).aspx
29 
30 #include "kwsysPrivate.h"
31 #include KWSYS_HEADER(SystemInformation.hxx)
32 #include KWSYS_HEADER(Process.h)
33 
34 // Work-around CMake dependency scanning limitation.  This must
35 // duplicate the above list of headers.
36 #if 0
37 #  include "Process.h.in"
38 #  include "SystemInformation.hxx.in"
39 #endif
40 
41 #include <algorithm>
42 #include <bitset>
43 #include <cassert>
44 #include <fstream>
45 #include <iostream>
46 #include <limits>
47 #include <set>
48 #include <sstream>
49 #include <string>
50 #include <vector>
51 
52 #if defined(_WIN32)
53 #  include <windows.h>
54 #  if defined(_MSC_VER) && _MSC_VER >= 1800
55 #    define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
56 #  endif
57 #  include <errno.h>
58 #  if defined(KWSYS_SYS_HAS_PSAPI)
59 #    include <psapi.h>
60 #  endif
61 #  if !defined(siginfo_t)
62 typedef int siginfo_t;
63 #  endif
64 #else
65 #  include <sys/types.h>
66 
67 #  include <cerrno> // extern int errno;
68 #  include <csignal>
69 #  include <fcntl.h>
70 #  include <sys/resource.h> // getrlimit
71 #  include <sys/time.h>
72 #  include <sys/utsname.h> // int uname(struct utsname *buf);
73 #  include <unistd.h>
74 #endif
75 
76 #if defined(__CYGWIN__) && !defined(_WIN32)
77 #  include <windows.h>
78 #  undef _WIN32
79 #endif
80 
81 #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) ||    \
82   defined(__DragonFly__)
83 #  include <netdb.h>
84 #  include <netinet/in.h>
85 #  include <sys/param.h>
86 #  include <sys/socket.h>
87 #  include <sys/sysctl.h>
88 #  if defined(KWSYS_SYS_HAS_IFADDRS_H)
89 #    include <ifaddrs.h>
90 #    include <net/if.h>
91 #    define KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN
92 #  endif
93 #endif
94 
95 #if defined(KWSYS_SYS_HAS_MACHINE_CPU_H)
96 #  include <machine/cpu.h>
97 #endif
98 
99 #ifdef __APPLE__
100 #  include <mach/host_info.h>
101 #  include <mach/mach.h>
102 #  include <mach/mach_types.h>
103 #  include <mach/vm_statistics.h>
104 #  include <netdb.h>
105 #  include <netinet/in.h>
106 #  include <sys/socket.h>
107 #  include <sys/sysctl.h>
108 #  if defined(KWSYS_SYS_HAS_IFADDRS_H)
109 #    include <ifaddrs.h>
110 #    include <net/if.h>
111 #    define KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN
112 #  endif
113 #  if !(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - 0 >= 1050)
114 #    undef KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE
115 #  endif
116 #endif
117 
118 #if defined(__linux) || defined(__sun) || defined(_SCO_DS) ||                 \
119   defined(__GLIBC__) || defined(__GNU__)
120 #  include <netdb.h>
121 #  include <netinet/in.h>
122 #  include <sys/socket.h>
123 #  if defined(KWSYS_SYS_HAS_IFADDRS_H)
124 #    include <ifaddrs.h>
125 #    include <net/if.h>
126 #    if defined(__LSB_VERSION__)
127 /* LSB has no getifaddrs */
128 #    elif defined(__ANDROID_API__) && __ANDROID_API__ < 24
129 /* Android has no getifaddrs prior to API 24.  */
130 #    else
131 #      define KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN
132 #    endif
133 #  endif
134 #  if defined(KWSYS_CXX_HAS_RLIMIT64)
135 using ResourceLimitType = struct rlimit64;
136 #    define GetResourceLimit getrlimit64
137 #  else
138 typedef struct rlimit ResourceLimitType;
139 #    define GetResourceLimit getrlimit
140 #  endif
141 #elif defined(__hpux)
142 #  include <sys/param.h>
143 #  include <sys/pstat.h>
144 #  if defined(KWSYS_SYS_HAS_MPCTL_H)
145 #    include <sys/mpctl.h>
146 #  endif
147 #endif
148 
149 #ifdef __HAIKU__
150 #  include <OS.h>
151 #endif
152 
153 #if defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE)
154 #  include <execinfo.h>
155 #  if defined(KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE)
156 #    include <cxxabi.h>
157 #  endif
158 #  if defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP)
159 #    include <dlfcn.h>
160 #  endif
161 #else
162 #  undef KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE
163 #  undef KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP
164 #endif
165 
166 #include <cctype> // int isdigit(int c);
167 #include <cstdio>
168 #include <cstdlib>
169 #include <cstring>
170 #include <memory.h>
171 
172 #if defined(_MSC_VER) && (_MSC_VER >= 1300) && !defined(_WIN64) &&            \
173   !defined(__clang__)
174 #  define USE_ASM_INSTRUCTIONS 1
175 #else
176 #  define USE_ASM_INSTRUCTIONS 0
177 #endif
178 
179 #if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__clang__) &&         \
180   !defined(_M_ARM64)
181 #  include <intrin.h>
182 #  define USE_CPUID_INTRINSICS 1
183 #else
184 #  define USE_CPUID_INTRINSICS 0
185 #endif
186 
187 #if USE_ASM_INSTRUCTIONS || USE_CPUID_INTRINSICS
188 #  define USE_CPUID 1
189 #else
190 #  define USE_CPUID 0
191 #endif
192 
193 #if USE_CPUID
194 
195 #  define CPUID_AWARE_COMPILER
196 
197 /**
198  * call CPUID instruction
199  *
200  * Will return false if the instruction failed.
201  */
call_cpuid(int select,int result[4])202 static bool call_cpuid(int select, int result[4])
203 {
204 #  if USE_CPUID_INTRINSICS
205   __cpuid(result, select);
206   return true;
207 #  else
208   int tmp[4];
209 #    if defined(_MSC_VER)
210   // Use SEH to determine CPUID presence
211   __try {
212     _asm {
213 #      ifdef CPUID_AWARE_COMPILER
214       ; we must push/pop the registers <<CPUID>> writes to, as the
215       ; optimiser does not know about <<CPUID>>, and so does not expect
216       ; these registers to change.
217       push eax
218       push ebx
219       push ecx
220       push edx
221 #      endif
222       ; <<CPUID>>
223       mov eax, select
224 #      ifdef CPUID_AWARE_COMPILER
225       cpuid
226 #      else
227       _asm _emit 0x0f
228       _asm _emit 0xa2
229 #      endif
230       mov tmp[0 * TYPE int], eax
231       mov tmp[1 * TYPE int], ebx
232       mov tmp[2 * TYPE int], ecx
233       mov tmp[3 * TYPE int], edx
234 
235 #      ifdef CPUID_AWARE_COMPILER
236       pop edx
237       pop ecx
238       pop ebx
239       pop eax
240 #      endif
241     }
242   } __except (1) {
243     return false;
244   }
245 
246   memcpy(result, tmp, sizeof(tmp));
247 #    endif
248 
249   // The cpuid instruction succeeded.
250   return true;
251 #  endif
252 }
253 #endif
254 
255 namespace KWSYS_NAMESPACE {
256 template <typename T>
min(T a,T b)257 T min(T a, T b)
258 {
259   return a < b ? a : b;
260 }
261 
262 extern "C" {
263 using SigAction = void (*)(int, siginfo_t*, void*);
264 }
265 
266 //  Define SystemInformationImplementation class
267 using DELAY_FUNC = void (*)(unsigned int);
268 
269 class SystemInformationImplementation
270 {
271 public:
272   SystemInformationImplementation();
273   ~SystemInformationImplementation() = default;
274 
275   const char* GetVendorString() const;
276   const char* GetVendorID();
277   std::string GetTypeID() const;
278   std::string GetFamilyID() const;
279   std::string GetModelID() const;
280   std::string GetModelName() const;
281   std::string GetSteppingCode() const;
282   const char* GetExtendedProcessorName() const;
283   const char* GetProcessorSerialNumber() const;
284   int GetProcessorCacheSize() const;
285   unsigned int GetLogicalProcessorsPerPhysical() const;
286   float GetProcessorClockFrequency() const;
287   int GetProcessorAPICID() const;
288   int GetProcessorCacheXSize(long int) const;
289   bool DoesCPUSupportFeature(long int) const;
290 
291   const char* GetOSName();
292   const char* GetHostname();
293   int GetFullyQualifiedDomainName(std::string& fqdn);
294   const char* GetOSRelease();
295   const char* GetOSVersion();
296   const char* GetOSPlatform();
297 
298   bool Is64Bits() const;
299 
300   unsigned int GetNumberOfLogicalCPU() const; // per physical cpu
301   unsigned int GetNumberOfPhysicalCPU() const;
302 
303   bool DoesCPUSupportCPUID();
304 
305   // Retrieve memory information in MiB.
306   size_t GetTotalVirtualMemory() const;
307   size_t GetAvailableVirtualMemory() const;
308   size_t GetTotalPhysicalMemory() const;
309   size_t GetAvailablePhysicalMemory() const;
310 
311   long long GetProcessId();
312 
313   // Retrieve memory information in KiB.
314   long long GetHostMemoryTotal();
315   long long GetHostMemoryAvailable(const char* hostLimitEnvVarName);
316   long long GetHostMemoryUsed();
317 
318   long long GetProcMemoryAvailable(const char* hostLimitEnvVarName,
319                                    const char* procLimitEnvVarName);
320   long long GetProcMemoryUsed();
321 
322   double GetLoadAverage();
323 
324   // enable/disable stack trace signal handler.
325   static void SetStackTraceOnError(int enable);
326 
327   // get current stack
328   static std::string GetProgramStack(int firstFrame, int wholePath);
329 
330   /** Run the different checks */
331   void RunCPUCheck();
332   void RunOSCheck();
333   void RunMemoryCheck();
334 
335 public:
336   using ID = struct tagID
337 
338   {
339 
340     int Type;
341 
342     int Family;
343 
344     int Model;
345 
346     int Revision;
347 
348     int ExtendedFamily;
349 
350     int ExtendedModel;
351 
352     std::string ProcessorName;
353 
354     std::string Vendor;
355 
356     std::string SerialNumber;
357 
358     std::string ModelName;
359   };
360 
361   using CPUPowerManagement = struct tagCPUPowerManagement
362 
363   {
364 
365     bool HasVoltageID;
366 
367     bool HasFrequencyID;
368 
369     bool HasTempSenseDiode;
370   };
371 
372   using CPUExtendedFeatures = struct tagCPUExtendedFeatures
373 
374   {
375 
376     bool Has3DNow;
377 
378     bool Has3DNowPlus;
379 
380     bool SupportsMP;
381 
382     bool HasMMXPlus;
383 
384     bool HasSSEMMX;
385 
386     unsigned int LogicalProcessorsPerPhysical;
387 
388     int APIC_ID;
389 
390     CPUPowerManagement PowerManagement;
391   };
392 
393   using CPUFeatures = struct CPUtagFeatures
394 
395   {
396 
397     bool HasFPU;
398 
399     bool HasTSC;
400 
401     bool HasMMX;
402 
403     bool HasSSE;
404 
405     bool HasSSEFP;
406 
407     bool HasSSE2;
408 
409     bool HasIA64;
410 
411     bool HasAPIC;
412 
413     bool HasCMOV;
414 
415     bool HasMTRR;
416 
417     bool HasACPI;
418 
419     bool HasSerial;
420 
421     bool HasThermal;
422 
423     int CPUSpeed;
424 
425     int L1CacheSize;
426 
427     int L2CacheSize;
428 
429     int L3CacheSize;
430 
431     CPUExtendedFeatures ExtendedFeatures;
432   };
433 
434   enum Manufacturer
435   {
436     AMD,
437     Intel,
438     NSC,
439     UMC,
440     Cyrix,
441     NexGen,
442     IDT,
443     Rise,
444     Transmeta,
445     Sun,
446     IBM,
447     Motorola,
448     HP,
449     Hygon,
450     Zhaoxin,
451     Apple,
452     UnknownManufacturer
453   };
454 
455 protected:
456   // For windows
457   bool RetrieveCPUFeatures();
458   bool RetrieveCPUIdentity();
459   bool RetrieveCPUCacheDetails();
460   bool RetrieveClassicalCPUCacheDetails();
461   bool RetrieveCPUClockSpeed();
462   bool RetrieveClassicalCPUClockSpeed();
463   bool RetrieveCPUExtendedLevelSupport(int);
464   bool RetrieveExtendedCPUFeatures();
465   bool RetrieveProcessorSerialNumber();
466   bool RetrieveCPUPowerManagement();
467   bool RetrieveClassicalCPUIdentity();
468   bool RetrieveExtendedCPUIdentity();
469 
470   // Processor information
471   Manufacturer ChipManufacturer;
472   CPUFeatures Features;
473   ID ChipID;
474   float CPUSpeedInMHz;
475   unsigned int NumberOfLogicalCPU;
476   unsigned int NumberOfPhysicalCPU;
477 
478   void CPUCountWindows();    // For windows
479   unsigned char GetAPICId(); // For windows
480   bool IsSMTSupported() const;
481   static long long GetCyclesDifference(DELAY_FUNC,
482                                        unsigned int); // For windows
483 
484   // For Linux and Cygwin, /proc/cpuinfo formats are slightly different
485   bool RetreiveInformationFromCpuInfoFile();
486   std::string ExtractValueFromCpuInfoFile(std::string buffer, const char* word,
487                                           size_t init = 0);
488 
489   bool QueryLinuxMemory();
490   bool QueryCygwinMemory();
491 
492   static void Delay(unsigned int);
493   static void DelayOverhead(unsigned int);
494 
495   void FindManufacturer(const std::string& family = "");
496 
497   // For Mac
498   bool ParseSysCtl();
499   int CallSwVers(const char* arg, std::string& ver);
500   void TrimNewline(std::string&);
501   std::string ExtractValueFromSysCtl(const char* word);
502   std::string SysCtlBuffer;
503 
504   // For Solaris
505   bool QuerySolarisMemory();
506   bool QuerySolarisProcessor();
507   std::string ParseValueFromKStat(const char* arguments);
508   std::string RunProcess(std::vector<const char*> args);
509 
510   // For Haiku OS
511   bool QueryHaikuInfo();
512 
513   // For QNX
514   bool QueryQNXMemory();
515   bool QueryQNXProcessor();
516 
517   // For OpenBSD, FreeBSD, NetBSD, DragonFly
518   bool QueryBSDMemory();
519   bool QueryBSDProcessor();
520 
521   // For HP-UX
522   bool QueryHPUXMemory();
523   bool QueryHPUXProcessor();
524 
525   // For Microsoft Windows
526   bool QueryWindowsMemory();
527 
528   // For AIX
529   bool QueryAIXMemory();
530 
531   bool QueryProcessorBySysconf();
532   bool QueryProcessor();
533 
534   // Evaluate the memory information.
535   bool QueryMemoryBySysconf();
536   bool QueryMemory();
537   size_t TotalVirtualMemory;
538   size_t AvailableVirtualMemory;
539   size_t TotalPhysicalMemory;
540   size_t AvailablePhysicalMemory;
541 
542   size_t CurrentPositionInFile;
543 
544   // Operating System information
545   bool QueryOSInformation();
546   std::string OSName;
547   std::string Hostname;
548   std::string OSRelease;
549   std::string OSVersion;
550   std::string OSPlatform;
551   bool OSIs64Bit;
552 };
553 
SystemInformation()554 SystemInformation::SystemInformation()
555 {
556   this->Implementation = new SystemInformationImplementation;
557 }
558 
~SystemInformation()559 SystemInformation::~SystemInformation()
560 {
561   delete this->Implementation;
562 }
563 
GetVendorString()564 const char* SystemInformation::GetVendorString()
565 {
566   return this->Implementation->GetVendorString();
567 }
568 
GetVendorID()569 const char* SystemInformation::GetVendorID()
570 {
571   return this->Implementation->GetVendorID();
572 }
573 
GetTypeID()574 std::string SystemInformation::GetTypeID()
575 {
576   return this->Implementation->GetTypeID();
577 }
578 
GetFamilyID()579 std::string SystemInformation::GetFamilyID()
580 {
581   return this->Implementation->GetFamilyID();
582 }
583 
GetModelID()584 std::string SystemInformation::GetModelID()
585 {
586   return this->Implementation->GetModelID();
587 }
588 
GetModelName()589 std::string SystemInformation::GetModelName()
590 {
591   return this->Implementation->GetModelName();
592 }
593 
GetSteppingCode()594 std::string SystemInformation::GetSteppingCode()
595 {
596   return this->Implementation->GetSteppingCode();
597 }
598 
GetExtendedProcessorName()599 const char* SystemInformation::GetExtendedProcessorName()
600 {
601   return this->Implementation->GetExtendedProcessorName();
602 }
603 
GetProcessorSerialNumber()604 const char* SystemInformation::GetProcessorSerialNumber()
605 {
606   return this->Implementation->GetProcessorSerialNumber();
607 }
608 
GetProcessorCacheSize()609 int SystemInformation::GetProcessorCacheSize()
610 {
611   return this->Implementation->GetProcessorCacheSize();
612 }
613 
GetLogicalProcessorsPerPhysical()614 unsigned int SystemInformation::GetLogicalProcessorsPerPhysical()
615 {
616   return this->Implementation->GetLogicalProcessorsPerPhysical();
617 }
618 
GetProcessorClockFrequency()619 float SystemInformation::GetProcessorClockFrequency()
620 {
621   return this->Implementation->GetProcessorClockFrequency();
622 }
623 
GetProcessorAPICID()624 int SystemInformation::GetProcessorAPICID()
625 {
626   return this->Implementation->GetProcessorAPICID();
627 }
628 
GetProcessorCacheXSize(long int l)629 int SystemInformation::GetProcessorCacheXSize(long int l)
630 {
631   return this->Implementation->GetProcessorCacheXSize(l);
632 }
633 
DoesCPUSupportFeature(long int i)634 bool SystemInformation::DoesCPUSupportFeature(long int i)
635 {
636   return this->Implementation->DoesCPUSupportFeature(i);
637 }
638 
GetCPUDescription()639 std::string SystemInformation::GetCPUDescription()
640 {
641   std::ostringstream oss;
642   oss << this->GetNumberOfPhysicalCPU() << " core ";
643   if (this->GetModelName().empty()) {
644     oss << this->GetProcessorClockFrequency() << " MHz "
645         << this->GetVendorString() << " " << this->GetExtendedProcessorName();
646   } else {
647     oss << this->GetModelName();
648   }
649 
650   // remove extra spaces
651   std::string tmp = oss.str();
652   size_t pos;
653   while ((pos = tmp.find("  ")) != std::string::npos) {
654     tmp.replace(pos, 2, " ");
655   }
656 
657   return tmp;
658 }
659 
GetOSName()660 const char* SystemInformation::GetOSName()
661 {
662   return this->Implementation->GetOSName();
663 }
664 
GetHostname()665 const char* SystemInformation::GetHostname()
666 {
667   return this->Implementation->GetHostname();
668 }
669 
GetFullyQualifiedDomainName()670 std::string SystemInformation::GetFullyQualifiedDomainName()
671 {
672   std::string fqdn;
673   this->Implementation->GetFullyQualifiedDomainName(fqdn);
674   return fqdn;
675 }
676 
GetOSRelease()677 const char* SystemInformation::GetOSRelease()
678 {
679   return this->Implementation->GetOSRelease();
680 }
681 
GetOSVersion()682 const char* SystemInformation::GetOSVersion()
683 {
684   return this->Implementation->GetOSVersion();
685 }
686 
GetOSPlatform()687 const char* SystemInformation::GetOSPlatform()
688 {
689   return this->Implementation->GetOSPlatform();
690 }
691 
GetOSIsWindows()692 int SystemInformation::GetOSIsWindows()
693 {
694 #if defined(_WIN32)
695   return 1;
696 #else
697   return 0;
698 #endif
699 }
700 
GetOSIsLinux()701 int SystemInformation::GetOSIsLinux()
702 {
703 #if defined(__linux)
704   return 1;
705 #else
706   return 0;
707 #endif
708 }
709 
GetOSIsApple()710 int SystemInformation::GetOSIsApple()
711 {
712 #if defined(__APPLE__)
713   return 1;
714 #else
715   return 0;
716 #endif
717 }
718 
GetOSDescription()719 std::string SystemInformation::GetOSDescription()
720 {
721   std::ostringstream oss;
722   oss << this->GetOSName() << " " << this->GetOSRelease() << " "
723       << this->GetOSVersion();
724 
725   return oss.str();
726 }
727 
Is64Bits()728 bool SystemInformation::Is64Bits()
729 {
730   return this->Implementation->Is64Bits();
731 }
732 
GetNumberOfLogicalCPU()733 unsigned int SystemInformation::GetNumberOfLogicalCPU() // per physical cpu
734 {
735   return this->Implementation->GetNumberOfLogicalCPU();
736 }
737 
GetNumberOfPhysicalCPU()738 unsigned int SystemInformation::GetNumberOfPhysicalCPU()
739 {
740   return this->Implementation->GetNumberOfPhysicalCPU();
741 }
742 
DoesCPUSupportCPUID()743 bool SystemInformation::DoesCPUSupportCPUID()
744 {
745   return this->Implementation->DoesCPUSupportCPUID();
746 }
747 
748 // Retrieve memory information in MiB.
GetTotalVirtualMemory()749 size_t SystemInformation::GetTotalVirtualMemory()
750 {
751   return this->Implementation->GetTotalVirtualMemory();
752 }
753 
GetAvailableVirtualMemory()754 size_t SystemInformation::GetAvailableVirtualMemory()
755 {
756   return this->Implementation->GetAvailableVirtualMemory();
757 }
758 
GetTotalPhysicalMemory()759 size_t SystemInformation::GetTotalPhysicalMemory()
760 {
761   return this->Implementation->GetTotalPhysicalMemory();
762 }
763 
GetAvailablePhysicalMemory()764 size_t SystemInformation::GetAvailablePhysicalMemory()
765 {
766   return this->Implementation->GetAvailablePhysicalMemory();
767 }
768 
GetMemoryDescription(const char * hostLimitEnvVarName,const char * procLimitEnvVarName)769 std::string SystemInformation::GetMemoryDescription(
770   const char* hostLimitEnvVarName, const char* procLimitEnvVarName)
771 {
772   std::ostringstream oss;
773   oss << "Host Total: " << this->GetHostMemoryTotal()
774       << " KiB, Host Available: "
775       << this->GetHostMemoryAvailable(hostLimitEnvVarName)
776       << " KiB, Process Available: "
777       << this->GetProcMemoryAvailable(hostLimitEnvVarName, procLimitEnvVarName)
778       << " KiB";
779   return oss.str();
780 }
781 
782 // host memory info in units of KiB.
GetHostMemoryTotal()783 long long SystemInformation::GetHostMemoryTotal()
784 {
785   return this->Implementation->GetHostMemoryTotal();
786 }
787 
GetHostMemoryAvailable(const char * hostLimitEnvVarName)788 long long SystemInformation::GetHostMemoryAvailable(
789   const char* hostLimitEnvVarName)
790 {
791   return this->Implementation->GetHostMemoryAvailable(hostLimitEnvVarName);
792 }
793 
GetHostMemoryUsed()794 long long SystemInformation::GetHostMemoryUsed()
795 {
796   return this->Implementation->GetHostMemoryUsed();
797 }
798 
799 // process memory info in units of KiB.
GetProcMemoryAvailable(const char * hostLimitEnvVarName,const char * procLimitEnvVarName)800 long long SystemInformation::GetProcMemoryAvailable(
801   const char* hostLimitEnvVarName, const char* procLimitEnvVarName)
802 {
803   return this->Implementation->GetProcMemoryAvailable(hostLimitEnvVarName,
804                                                       procLimitEnvVarName);
805 }
806 
GetProcMemoryUsed()807 long long SystemInformation::GetProcMemoryUsed()
808 {
809   return this->Implementation->GetProcMemoryUsed();
810 }
811 
GetLoadAverage()812 double SystemInformation::GetLoadAverage()
813 {
814   return this->Implementation->GetLoadAverage();
815 }
816 
GetProcessId()817 long long SystemInformation::GetProcessId()
818 {
819   return this->Implementation->GetProcessId();
820 }
821 
SetStackTraceOnError(int enable)822 void SystemInformation::SetStackTraceOnError(int enable)
823 {
824   SystemInformationImplementation::SetStackTraceOnError(enable);
825 }
826 
GetProgramStack(int firstFrame,int wholePath)827 std::string SystemInformation::GetProgramStack(int firstFrame, int wholePath)
828 {
829   return SystemInformationImplementation::GetProgramStack(firstFrame,
830                                                           wholePath);
831 }
832 
833 /** Run the different checks */
RunCPUCheck()834 void SystemInformation::RunCPUCheck()
835 {
836   this->Implementation->RunCPUCheck();
837 }
838 
RunOSCheck()839 void SystemInformation::RunOSCheck()
840 {
841   this->Implementation->RunOSCheck();
842 }
843 
RunMemoryCheck()844 void SystemInformation::RunMemoryCheck()
845 {
846   this->Implementation->RunMemoryCheck();
847 }
848 
849 // SystemInformationImplementation starts here
850 
851 #if USE_CPUID
852 #  define STORE_TLBCACHE_INFO(x, y) x = (x < (y)) ? (y) : x
853 #  define TLBCACHE_INFO_UNITS (15)
854 #endif
855 
856 #if USE_ASM_INSTRUCTIONS
857 #  define CLASSICAL_CPU_FREQ_LOOP 10000000
858 #  define RDTSC_INSTRUCTION _asm _emit 0x0f _asm _emit 0x31
859 #endif
860 
861 #define INITIAL_APIC_ID_BITS 0xFF000000
862 // initial APIC ID for the processor this code is running on.
863 // Default value = 0xff if HT is not supported
864 
865 // Hide implementation details in an anonymous namespace.
866 namespace {
867 // *****************************************************************************
868 #if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__)
LoadLines(FILE * file,std::vector<std::string> & lines)869 int LoadLines(FILE* file, std::vector<std::string>& lines)
870 {
871   // Load each line in the given file into a the vector.
872   int nRead = 0;
873   const int bufSize = 1024;
874   char buf[bufSize] = { '\0' };
875   while (!feof(file) && !ferror(file)) {
876     errno = 0;
877     if (fgets(buf, bufSize, file) == nullptr) {
878       if (ferror(file) && (errno == EINTR)) {
879         clearerr(file);
880       }
881       continue;
882     }
883     char* pBuf = buf;
884     while (*pBuf) {
885       if (*pBuf == '\n')
886         *pBuf = '\0';
887       pBuf += 1;
888     }
889     lines.emplace_back(buf);
890     ++nRead;
891   }
892   if (ferror(file)) {
893     return 0;
894   }
895   return nRead;
896 }
897 
898 #  if defined(__linux) || defined(__CYGWIN__)
899 // *****************************************************************************
LoadLines(const char * fileName,std::vector<std::string> & lines)900 int LoadLines(const char* fileName, std::vector<std::string>& lines)
901 {
902   FILE* file = fopen(fileName, "r");
903   if (file == nullptr) {
904     return 0;
905   }
906   int nRead = LoadLines(file, lines);
907   fclose(file);
908   return nRead;
909 }
910 #  endif
911 
912 // ****************************************************************************
913 template <typename T>
NameValue(std::vector<std::string> const & lines,std::string const & name,T & value)914 int NameValue(std::vector<std::string> const& lines, std::string const& name,
915               T& value)
916 {
917   size_t nLines = lines.size();
918   for (size_t i = 0; i < nLines; ++i) {
919     size_t at = lines[i].find(name);
920     if (at == std::string::npos) {
921       continue;
922     }
923     std::istringstream is(lines[i].substr(at + name.size()));
924     is >> value;
925     return 0;
926   }
927   return -1;
928 }
929 #endif
930 
931 #if defined(__linux) || defined(__CYGWIN__)
932 // ****************************************************************************
933 template <typename T>
GetFieldsFromFile(const char * fileName,const char ** fieldNames,T * values)934 int GetFieldsFromFile(const char* fileName, const char** fieldNames, T* values)
935 {
936   std::vector<std::string> fields;
937   if (!LoadLines(fileName, fields)) {
938     return -1;
939   }
940   int i = 0;
941   while (fieldNames[i] != nullptr) {
942     int ierr = NameValue(fields, fieldNames[i], values[i]);
943     if (ierr) {
944       return -(i + 2);
945     }
946     i += 1;
947   }
948   return 0;
949 }
950 
951 // ****************************************************************************
952 template <typename T>
GetFieldFromFile(const char * fileName,const char * fieldName,T & value)953 int GetFieldFromFile(const char* fileName, const char* fieldName, T& value)
954 {
955   const char* fieldNames[2] = { fieldName, nullptr };
956   T values[1] = { T(0) };
957   int ierr = GetFieldsFromFile(fileName, fieldNames, values);
958   if (ierr) {
959     return ierr;
960   }
961   value = values[0];
962   return 0;
963 }
964 #endif
965 
966 // ****************************************************************************
967 #if defined(__APPLE__)
968 template <typename T>
GetFieldsFromCommand(const char * command,const char ** fieldNames,T * values)969 int GetFieldsFromCommand(const char* command, const char** fieldNames,
970                          T* values)
971 {
972   FILE* file = popen(command, "r");
973   if (file == nullptr) {
974     return -1;
975   }
976   std::vector<std::string> fields;
977   int nl = LoadLines(file, fields);
978   pclose(file);
979   if (nl == 0) {
980     return -1;
981   }
982   int i = 0;
983   while (fieldNames[i] != nullptr) {
984     int ierr = NameValue(fields, fieldNames[i], values[i]);
985     if (ierr) {
986       return -(i + 2);
987     }
988     i += 1;
989   }
990   return 0;
991 }
992 #endif
993 
994 // ****************************************************************************
995 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
StacktraceSignalHandler(int sigNo,siginfo_t * sigInfo,void *)996 void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo,
997                              void* /*sigContext*/)
998 {
999 #  if defined(__linux) || defined(__APPLE__)
1000   std::ostringstream oss;
1001   oss << std::endl
1002       << "========================================================="
1003       << std::endl
1004       << "Process id " << getpid() << " ";
1005   switch (sigNo) {
1006     case SIGINT:
1007       oss << "Caught SIGINT";
1008       break;
1009 
1010     case SIGTERM:
1011       oss << "Caught SIGTERM";
1012       break;
1013 
1014     case SIGABRT:
1015       oss << "Caught SIGABRT";
1016       break;
1017 
1018     case SIGFPE:
1019       oss << "Caught SIGFPE at " << (sigInfo->si_addr == nullptr ? "0x" : "")
1020           << sigInfo->si_addr << " ";
1021       switch (sigInfo->si_code) {
1022 #    if defined(FPE_INTDIV)
1023         case FPE_INTDIV:
1024           oss << "integer division by zero";
1025           break;
1026 #    endif
1027 
1028 #    if defined(FPE_INTOVF)
1029         case FPE_INTOVF:
1030           oss << "integer overflow";
1031           break;
1032 #    endif
1033 
1034         case FPE_FLTDIV:
1035           oss << "floating point divide by zero";
1036           break;
1037 
1038         case FPE_FLTOVF:
1039           oss << "floating point overflow";
1040           break;
1041 
1042         case FPE_FLTUND:
1043           oss << "floating point underflow";
1044           break;
1045 
1046         case FPE_FLTRES:
1047           oss << "floating point inexact result";
1048           break;
1049 
1050         case FPE_FLTINV:
1051           oss << "floating point invalid operation";
1052           break;
1053 
1054 #    if defined(FPE_FLTSUB)
1055         case FPE_FLTSUB:
1056           oss << "floating point subscript out of range";
1057           break;
1058 #    endif
1059 
1060         default:
1061           oss << "code " << sigInfo->si_code;
1062           break;
1063       }
1064       break;
1065 
1066     case SIGSEGV:
1067       oss << "Caught SIGSEGV at " << (sigInfo->si_addr == nullptr ? "0x" : "")
1068           << sigInfo->si_addr << " ";
1069       switch (sigInfo->si_code) {
1070         case SEGV_MAPERR:
1071           oss << "address not mapped to object";
1072           break;
1073 
1074         case SEGV_ACCERR:
1075           oss << "invalid permission for mapped object";
1076           break;
1077 
1078         default:
1079           oss << "code " << sigInfo->si_code;
1080           break;
1081       }
1082       break;
1083 
1084     case SIGBUS:
1085       oss << "Caught SIGBUS at " << (sigInfo->si_addr == nullptr ? "0x" : "")
1086           << sigInfo->si_addr << " ";
1087       switch (sigInfo->si_code) {
1088         case BUS_ADRALN:
1089           oss << "invalid address alignment";
1090           break;
1091 
1092 #    if defined(BUS_ADRERR)
1093         case BUS_ADRERR:
1094           oss << "nonexistent physical address";
1095           break;
1096 #    endif
1097 
1098 #    if defined(BUS_OBJERR)
1099         case BUS_OBJERR:
1100           oss << "object-specific hardware error";
1101           break;
1102 #    endif
1103 
1104 #    if defined(BUS_MCEERR_AR)
1105         case BUS_MCEERR_AR:
1106           oss << "Hardware memory error consumed on a machine check; action "
1107                  "required.";
1108           break;
1109 #    endif
1110 
1111 #    if defined(BUS_MCEERR_AO)
1112         case BUS_MCEERR_AO:
1113           oss << "Hardware memory error detected in process but not consumed; "
1114                  "action optional.";
1115           break;
1116 #    endif
1117 
1118         default:
1119           oss << "code " << sigInfo->si_code;
1120           break;
1121       }
1122       break;
1123 
1124     case SIGILL:
1125       oss << "Caught SIGILL at " << (sigInfo->si_addr == nullptr ? "0x" : "")
1126           << sigInfo->si_addr << " ";
1127       switch (sigInfo->si_code) {
1128         case ILL_ILLOPC:
1129           oss << "illegal opcode";
1130           break;
1131 
1132 #    if defined(ILL_ILLOPN)
1133         case ILL_ILLOPN:
1134           oss << "illegal operand";
1135           break;
1136 #    endif
1137 
1138 #    if defined(ILL_ILLADR)
1139         case ILL_ILLADR:
1140           oss << "illegal addressing mode.";
1141           break;
1142 #    endif
1143 
1144         case ILL_ILLTRP:
1145           oss << "illegal trap";
1146           break;
1147 
1148         case ILL_PRVOPC:
1149           oss << "privileged opcode";
1150           break;
1151 
1152 #    if defined(ILL_PRVREG)
1153         case ILL_PRVREG:
1154           oss << "privileged register";
1155           break;
1156 #    endif
1157 
1158 #    if defined(ILL_COPROC)
1159         case ILL_COPROC:
1160           oss << "co-processor error";
1161           break;
1162 #    endif
1163 
1164 #    if defined(ILL_BADSTK)
1165         case ILL_BADSTK:
1166           oss << "internal stack error";
1167           break;
1168 #    endif
1169 
1170         default:
1171           oss << "code " << sigInfo->si_code;
1172           break;
1173       }
1174       break;
1175 
1176     default:
1177       oss << "Caught " << sigNo << " code " << sigInfo->si_code;
1178       break;
1179   }
1180   oss << std::endl
1181       << "Program Stack:" << std::endl
1182       << SystemInformationImplementation::GetProgramStack(2, 0)
1183       << "========================================================="
1184       << std::endl;
1185   std::cerr << oss.str() << std::endl;
1186 
1187   // restore the previously registered handlers
1188   // and abort
1189   SystemInformationImplementation::SetStackTraceOnError(0);
1190   abort();
1191 #  else
1192   // avoid warning C4100
1193   (void)sigNo;
1194   (void)sigInfo;
1195 #  endif
1196 }
1197 #endif
1198 
1199 #if defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE)
1200 #  define safes(_arg) ((_arg) ? (_arg) : "???")
1201 
1202 // Description:
1203 // A container for symbol properties. Each instance
1204 // must be Initialized.
1205 class SymbolProperties
1206 {
1207 public:
1208   SymbolProperties();
1209 
1210   // Description:
1211   // The SymbolProperties instance must be initialized by
1212   // passing a stack address.
1213   void Initialize(void* address);
1214 
1215   // Description:
1216   // Get the symbol's stack address.
GetAddress() const1217   void* GetAddress() const { return this->Address; }
1218 
1219   // Description:
1220   // If not set paths will be removed. eg, from a binary
1221   // or source file.
SetReportPath(int rp)1222   void SetReportPath(int rp) { this->ReportPath = rp; }
1223 
1224   // Description:
1225   // Set/Get the name of the binary file that the symbol
1226   // is found in.
SetBinary(const char * binary)1227   void SetBinary(const char* binary) { this->Binary = safes(binary); }
1228 
1229   std::string GetBinary() const;
1230 
1231   // Description:
1232   // Set the name of the function that the symbol is found in.
1233   // If c++ demangling is supported it will be demangled.
SetFunction(const char * function)1234   void SetFunction(const char* function)
1235   {
1236     this->Function = this->Demangle(function);
1237   }
1238 
GetFunction() const1239   std::string GetFunction() const { return this->Function; }
1240 
1241   // Description:
1242   // Set/Get the name of the source file where the symbol
1243   // is defined.
SetSourceFile(const char * sourcefile)1244   void SetSourceFile(const char* sourcefile)
1245   {
1246     this->SourceFile = safes(sourcefile);
1247   }
1248 
GetSourceFile() const1249   std::string GetSourceFile() const
1250   {
1251     return this->GetFileName(this->SourceFile);
1252   }
1253 
1254   // Description:
1255   // Set/Get the line number where the symbol is defined
SetLineNumber(long linenumber)1256   void SetLineNumber(long linenumber) { this->LineNumber = linenumber; }
GetLineNumber() const1257   long GetLineNumber() const { return this->LineNumber; }
1258 
1259   // Description:
1260   // Set the address where the binary image is mapped
1261   // into memory.
SetBinaryBaseAddress(void * address)1262   void SetBinaryBaseAddress(void* address)
1263   {
1264     this->BinaryBaseAddress = address;
1265   }
1266 
1267 private:
GetRealAddress() const1268   void* GetRealAddress() const
1269   {
1270     return (void*)((char*)this->Address - (char*)this->BinaryBaseAddress);
1271   }
1272 
1273   std::string GetFileName(const std::string& path) const;
1274   std::string Demangle(const char* symbol) const;
1275 
1276 private:
1277   std::string Binary;
1278   void* BinaryBaseAddress;
1279   void* Address;
1280   std::string SourceFile;
1281   std::string Function;
1282   long LineNumber;
1283   int ReportPath;
1284 };
1285 
operator <<(std::ostream & os,const SymbolProperties & sp)1286 std::ostream& operator<<(std::ostream& os, const SymbolProperties& sp)
1287 {
1288 #  if defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP)
1289   os << std::hex << sp.GetAddress() << " : " << sp.GetFunction() << " [("
1290      << sp.GetBinary() << ") " << sp.GetSourceFile() << ":" << std::dec
1291      << sp.GetLineNumber() << "]";
1292 #  elif defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE)
1293   void* addr = sp.GetAddress();
1294   char** syminfo = backtrace_symbols(&addr, 1);
1295   os << safes(syminfo[0]);
1296   free(syminfo);
1297 #  else
1298   (void)os;
1299   (void)sp;
1300 #  endif
1301   return os;
1302 }
1303 
SymbolProperties()1304 SymbolProperties::SymbolProperties()
1305 {
1306   // not using an initializer list
1307   // to avoid some PGI compiler warnings
1308   this->SetBinary("???");
1309   this->SetBinaryBaseAddress(nullptr);
1310   this->Address = nullptr;
1311   this->SetSourceFile("???");
1312   this->SetFunction("???");
1313   this->SetLineNumber(-1);
1314   this->SetReportPath(0);
1315   // avoid PGI compiler warnings
1316   this->GetRealAddress();
1317   this->GetFunction();
1318   this->GetSourceFile();
1319   this->GetLineNumber();
1320 }
1321 
GetFileName(const std::string & path) const1322 std::string SymbolProperties::GetFileName(const std::string& path) const
1323 {
1324   std::string file(path);
1325   if (!this->ReportPath) {
1326     size_t at = file.rfind('/');
1327     if (at != std::string::npos) {
1328       file.erase(0, at + 1);
1329     }
1330   }
1331   return file;
1332 }
1333 
GetBinary() const1334 std::string SymbolProperties::GetBinary() const
1335 {
1336 // only linux has proc fs
1337 #  if defined(__linux__)
1338   if (this->Binary == "/proc/self/exe") {
1339     std::string binary;
1340     char buf[1024] = { '\0' };
1341     ssize_t ll = 0;
1342     if ((ll = readlink("/proc/self/exe", buf, 1024)) > 0 && ll < 1024) {
1343       buf[ll] = '\0';
1344       binary = buf;
1345     } else {
1346       binary = "/proc/self/exe";
1347     }
1348     return this->GetFileName(binary);
1349   }
1350 #  endif
1351   return this->GetFileName(this->Binary);
1352 }
1353 
Demangle(const char * symbol) const1354 std::string SymbolProperties::Demangle(const char* symbol) const
1355 {
1356   std::string result = safes(symbol);
1357 #  if defined(KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE)
1358   int status = 0;
1359   char* demangledSymbol =
1360     abi::__cxa_demangle(symbol, nullptr, nullptr, &status);
1361   if (!status) {
1362     result = demangledSymbol;
1363   }
1364   free(demangledSymbol);
1365 #  else
1366   (void)symbol;
1367 #  endif
1368   return result;
1369 }
1370 
Initialize(void * address)1371 void SymbolProperties::Initialize(void* address)
1372 {
1373   this->Address = address;
1374 #  if defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP)
1375   // first fallback option can demangle c++ functions
1376   Dl_info info;
1377   int ierr = dladdr(this->Address, &info);
1378   if (ierr && info.dli_sname && info.dli_saddr) {
1379     this->SetBinary(info.dli_fname);
1380     this->SetFunction(info.dli_sname);
1381   }
1382 #  else
1383 // second fallback use builtin backtrace_symbols
1384 // to decode the backtrace.
1385 #  endif
1386 }
1387 #endif // don't define this class if we're not using it
1388 
1389 #if defined(_WIN32) || defined(__CYGWIN__)
1390 #  define KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes
1391 #endif
1392 #if defined(_MSC_VER) && _MSC_VER < 1310
1393 #  undef KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes
1394 #endif
1395 #if defined(KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes)
calculateCPULoad(unsigned __int64 idleTicks,unsigned __int64 totalTicks)1396 double calculateCPULoad(unsigned __int64 idleTicks,
1397                         unsigned __int64 totalTicks)
1398 {
1399   static double previousLoad = -0.0;
1400   static unsigned __int64 previousIdleTicks = 0;
1401   static unsigned __int64 previousTotalTicks = 0;
1402 
1403   unsigned __int64 const idleTicksSinceLastTime =
1404     idleTicks - previousIdleTicks;
1405   unsigned __int64 const totalTicksSinceLastTime =
1406     totalTicks - previousTotalTicks;
1407 
1408   double load;
1409   if (previousTotalTicks == 0 || totalTicksSinceLastTime == 0) {
1410     // No new information.  Use previous result.
1411     load = previousLoad;
1412   } else {
1413     // Calculate load since last time.
1414     load = 1.0 - double(idleTicksSinceLastTime) / totalTicksSinceLastTime;
1415 
1416     // Smooth if possible.
1417     if (previousLoad > 0) {
1418       load = 0.25 * load + 0.75 * previousLoad;
1419     }
1420   }
1421 
1422   previousLoad = load;
1423   previousIdleTicks = idleTicks;
1424   previousTotalTicks = totalTicks;
1425 
1426   return load;
1427 }
1428 
fileTimeToUInt64(FILETIME const & ft)1429 unsigned __int64 fileTimeToUInt64(FILETIME const& ft)
1430 {
1431   LARGE_INTEGER out;
1432   out.HighPart = ft.dwHighDateTime;
1433   out.LowPart = ft.dwLowDateTime;
1434   return out.QuadPart;
1435 }
1436 #endif
1437 
1438 } // anonymous namespace
1439 
SystemInformationImplementation()1440 SystemInformationImplementation::SystemInformationImplementation()
1441 {
1442   this->TotalVirtualMemory = 0;
1443   this->AvailableVirtualMemory = 0;
1444   this->TotalPhysicalMemory = 0;
1445   this->AvailablePhysicalMemory = 0;
1446   this->CurrentPositionInFile = 0;
1447   this->ChipManufacturer = UnknownManufacturer;
1448   memset(&this->Features, 0, sizeof(CPUFeatures));
1449   this->ChipID.Type = 0;
1450   this->ChipID.Family = 0;
1451   this->ChipID.Model = 0;
1452   this->ChipID.Revision = 0;
1453   this->ChipID.ExtendedFamily = 0;
1454   this->ChipID.ExtendedModel = 0;
1455   this->CPUSpeedInMHz = 0;
1456   this->NumberOfLogicalCPU = 0;
1457   this->NumberOfPhysicalCPU = 0;
1458   this->OSName = "";
1459   this->Hostname = "";
1460   this->OSRelease = "";
1461   this->OSVersion = "";
1462   this->OSPlatform = "";
1463   this->OSIs64Bit = (sizeof(void*) == 8);
1464 }
1465 
RunCPUCheck()1466 void SystemInformationImplementation::RunCPUCheck()
1467 {
1468 #ifdef _WIN32
1469   // Check to see if this processor supports CPUID.
1470   bool supportsCPUID = DoesCPUSupportCPUID();
1471 
1472   if (supportsCPUID) {
1473     // Retrieve the CPU details.
1474     RetrieveCPUIdentity();
1475     this->FindManufacturer();
1476     RetrieveCPUFeatures();
1477   }
1478 
1479   // These two may be called without support for the CPUID instruction.
1480   // (But if the instruction is there, they should be called *after*
1481   // the above call to RetrieveCPUIdentity... that's why the two if
1482   // blocks exist with the same "if (supportsCPUID)" logic...
1483   //
1484   if (!RetrieveCPUClockSpeed()) {
1485     RetrieveClassicalCPUClockSpeed();
1486   }
1487 
1488   if (supportsCPUID) {
1489     // Retrieve cache information.
1490     if (!RetrieveCPUCacheDetails()) {
1491       RetrieveClassicalCPUCacheDetails();
1492     }
1493 
1494     // Retrieve the extended CPU details.
1495     if (!RetrieveExtendedCPUIdentity()) {
1496       RetrieveClassicalCPUIdentity();
1497     }
1498 
1499     RetrieveExtendedCPUFeatures();
1500     RetrieveCPUPowerManagement();
1501 
1502     // Now attempt to retrieve the serial number (if possible).
1503     RetrieveProcessorSerialNumber();
1504   }
1505 
1506   this->CPUCountWindows();
1507 
1508 #elif defined(__APPLE__)
1509   this->ParseSysCtl();
1510 #elif defined(__SVR4) && defined(__sun)
1511   this->QuerySolarisProcessor();
1512 #elif defined(__HAIKU__)
1513   this->QueryHaikuInfo();
1514 #elif defined(__QNX__)
1515   this->QueryQNXProcessor();
1516 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) ||  \
1517   defined(__DragonFly__)
1518   this->QueryBSDProcessor();
1519 #elif defined(__hpux)
1520   this->QueryHPUXProcessor();
1521 #elif defined(__linux) || defined(__CYGWIN__)
1522   this->RetreiveInformationFromCpuInfoFile();
1523 #else
1524   this->QueryProcessor();
1525 #endif
1526 }
1527 
RunOSCheck()1528 void SystemInformationImplementation::RunOSCheck()
1529 {
1530   this->QueryOSInformation();
1531 }
1532 
RunMemoryCheck()1533 void SystemInformationImplementation::RunMemoryCheck()
1534 {
1535 #if defined(__APPLE__)
1536   this->ParseSysCtl();
1537 #elif defined(__SVR4) && defined(__sun)
1538   this->QuerySolarisMemory();
1539 #elif defined(__HAIKU__)
1540   this->QueryHaikuInfo();
1541 #elif defined(__QNX__)
1542   this->QueryQNXMemory();
1543 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) ||  \
1544   defined(__DragonFly__)
1545   this->QueryBSDMemory();
1546 #elif defined(__CYGWIN__)
1547   this->QueryCygwinMemory();
1548 #elif defined(_WIN32)
1549   this->QueryWindowsMemory();
1550 #elif defined(__hpux)
1551   this->QueryHPUXMemory();
1552 #elif defined(__linux)
1553   this->QueryLinuxMemory();
1554 #elif defined(_AIX)
1555   this->QueryAIXMemory();
1556 #else
1557   this->QueryMemory();
1558 #endif
1559 }
1560 
1561 /** Get the vendor string */
GetVendorString() const1562 const char* SystemInformationImplementation::GetVendorString() const
1563 {
1564   return this->ChipID.Vendor.c_str();
1565 }
1566 
1567 /** Get the OS Name */
GetOSName()1568 const char* SystemInformationImplementation::GetOSName()
1569 {
1570   return this->OSName.c_str();
1571 }
1572 
1573 /** Get the hostname */
GetHostname()1574 const char* SystemInformationImplementation::GetHostname()
1575 {
1576   if (this->Hostname.empty()) {
1577     this->Hostname = "localhost";
1578 #if defined(_WIN32)
1579     WORD wVersionRequested;
1580     WSADATA wsaData;
1581     char name[255];
1582     wVersionRequested = MAKEWORD(2, 0);
1583     if (WSAStartup(wVersionRequested, &wsaData) == 0) {
1584       gethostname(name, sizeof(name));
1585       WSACleanup();
1586     }
1587     this->Hostname = name;
1588 #else
1589     struct utsname unameInfo;
1590     int errorFlag = uname(&unameInfo);
1591     if (errorFlag == 0) {
1592       this->Hostname = unameInfo.nodename;
1593     }
1594 #endif
1595   }
1596   return this->Hostname.c_str();
1597 }
1598 
1599 /** Get the FQDN */
GetFullyQualifiedDomainName(std::string & fqdn)1600 int SystemInformationImplementation::GetFullyQualifiedDomainName(
1601   std::string& fqdn)
1602 {
1603   // in the event of absolute failure return localhost.
1604   fqdn = "localhost";
1605 
1606 #if defined(_WIN32)
1607   int ierr;
1608   // TODO - a more robust implementation for windows, see comments
1609   // in unix implementation.
1610   WSADATA wsaData;
1611   WORD ver = MAKEWORD(2, 0);
1612   ierr = WSAStartup(ver, &wsaData);
1613   if (ierr) {
1614     return -1;
1615   }
1616 
1617   char base[256] = { '\0' };
1618   ierr = gethostname(base, 256);
1619   if (ierr) {
1620     WSACleanup();
1621     return -2;
1622   }
1623   fqdn = base;
1624 
1625   HOSTENT* hent = gethostbyname(base);
1626   if (hent) {
1627     fqdn = hent->h_name;
1628   }
1629 
1630   WSACleanup();
1631   return 0;
1632 
1633 #elif defined(KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN)
1634   // gethostname typical returns an alias for loopback interface
1635   // we want the fully qualified domain name. Because there are
1636   // any number of interfaces on this system we look for the
1637   // first of these that contains the name returned by gethostname
1638   // and is longer. failing that we return gethostname and indicate
1639   // with a failure code. Return of a failure code is not necessarily
1640   // an indication of an error. for instance gethostname may return
1641   // the fully qualified domain name, or there may not be one if the
1642   // system lives on a private network such as in the case of a cluster
1643   // node.
1644 
1645   int ierr = 0;
1646   char base[NI_MAXHOST];
1647   ierr = gethostname(base, NI_MAXHOST);
1648   if (ierr) {
1649     return -1;
1650   }
1651   size_t baseSize = strlen(base);
1652   fqdn = base;
1653 
1654   struct ifaddrs* ifas;
1655   struct ifaddrs* ifa;
1656   ierr = getifaddrs(&ifas);
1657   if (ierr) {
1658     return -2;
1659   }
1660 
1661   for (ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next) {
1662     int fam = ifa->ifa_addr ? ifa->ifa_addr->sa_family : -1;
1663     // Skip Loopback interfaces
1664     if (((fam == AF_INET) || (fam == AF_INET6)) &&
1665         !(ifa->ifa_flags & IFF_LOOPBACK)) {
1666       char host[NI_MAXHOST] = { '\0' };
1667 
1668       const size_t addrlen = (fam == AF_INET ? sizeof(struct sockaddr_in)
1669                                              : sizeof(struct sockaddr_in6));
1670 
1671       ierr = getnameinfo(ifa->ifa_addr, static_cast<socklen_t>(addrlen), host,
1672                          NI_MAXHOST, nullptr, 0, NI_NAMEREQD);
1673       if (ierr) {
1674         // don't report the failure now since we may succeed on another
1675         // interface. If all attempts fail then return the failure code.
1676         ierr = -3;
1677         continue;
1678       }
1679 
1680       std::string candidate = host;
1681       if ((candidate.find(base) != std::string::npos) &&
1682           baseSize < candidate.size()) {
1683         // success, stop now.
1684         ierr = 0;
1685         fqdn = candidate;
1686         break;
1687       }
1688     }
1689   }
1690   freeifaddrs(ifas);
1691 
1692   return ierr;
1693 #else
1694   /* TODO: Implement on more platforms.  */
1695   fqdn = this->GetHostname();
1696   return -1;
1697 #endif
1698 }
1699 
1700 /** Get the OS release */
GetOSRelease()1701 const char* SystemInformationImplementation::GetOSRelease()
1702 {
1703   return this->OSRelease.c_str();
1704 }
1705 
1706 /** Get the OS version */
GetOSVersion()1707 const char* SystemInformationImplementation::GetOSVersion()
1708 {
1709   return this->OSVersion.c_str();
1710 }
1711 
1712 /** Get the OS platform */
GetOSPlatform()1713 const char* SystemInformationImplementation::GetOSPlatform()
1714 {
1715   return this->OSPlatform.c_str();
1716 }
1717 
1718 /** Get the vendor ID */
GetVendorID()1719 const char* SystemInformationImplementation::GetVendorID()
1720 {
1721   // Return the vendor ID.
1722   switch (this->ChipManufacturer) {
1723     case Intel:
1724       return "Intel Corporation";
1725     case AMD:
1726       return "Advanced Micro Devices";
1727     case NSC:
1728       return "National Semiconductor";
1729     case Cyrix:
1730       return "Cyrix Corp., VIA Inc.";
1731     case NexGen:
1732       return "NexGen Inc., Advanced Micro Devices";
1733     case IDT:
1734       return "IDT\\Centaur, Via Inc., Shanghai Zhaoxin Semiconductor Co., "
1735              "Ltd.";
1736     case UMC:
1737       return "United Microelectronics Corp.";
1738     case Rise:
1739       return "Rise";
1740     case Transmeta:
1741       return "Transmeta";
1742     case Sun:
1743       return "Sun Microelectronics";
1744     case IBM:
1745       return "IBM";
1746     case Motorola:
1747       return "Motorola";
1748     case HP:
1749       return "Hewlett-Packard";
1750     case Hygon:
1751       return "Chengdu Haiguang IC Design Co., Ltd.";
1752     case Zhaoxin:
1753       return "Shanghai Zhaoxin Semiconductor Co., Ltd.";
1754     case Apple:
1755       return "Apple";
1756     case UnknownManufacturer:
1757     default:
1758       return "Unknown Manufacturer";
1759   }
1760 }
1761 
1762 /** Return the type ID of the CPU */
GetTypeID() const1763 std::string SystemInformationImplementation::GetTypeID() const
1764 {
1765   std::ostringstream str;
1766   str << this->ChipID.Type;
1767   return str.str();
1768 }
1769 
1770 /** Return the family of the CPU present */
GetFamilyID() const1771 std::string SystemInformationImplementation::GetFamilyID() const
1772 {
1773   std::ostringstream str;
1774   str << this->ChipID.Family;
1775   return str.str();
1776 }
1777 
1778 // Return the model of CPU present */
GetModelID() const1779 std::string SystemInformationImplementation::GetModelID() const
1780 {
1781   std::ostringstream str;
1782   str << this->ChipID.Model;
1783   return str.str();
1784 }
1785 
1786 // Return the model name of CPU present */
GetModelName() const1787 std::string SystemInformationImplementation::GetModelName() const
1788 {
1789   return this->ChipID.ModelName;
1790 }
1791 
1792 /** Return the stepping code of the CPU present. */
GetSteppingCode() const1793 std::string SystemInformationImplementation::GetSteppingCode() const
1794 {
1795   std::ostringstream str;
1796   str << this->ChipID.Revision;
1797   return str.str();
1798 }
1799 
1800 /** Return the stepping code of the CPU present. */
GetExtendedProcessorName() const1801 const char* SystemInformationImplementation::GetExtendedProcessorName() const
1802 {
1803   return this->ChipID.ProcessorName.c_str();
1804 }
1805 
1806 /** Return the serial number of the processor
1807  *  in hexadecimal: xxxx-xxxx-xxxx-xxxx-xxxx-xxxx. */
GetProcessorSerialNumber() const1808 const char* SystemInformationImplementation::GetProcessorSerialNumber() const
1809 {
1810   return this->ChipID.SerialNumber.c_str();
1811 }
1812 
1813 /** Return the logical processors per physical */
GetLogicalProcessorsPerPhysical() const1814 unsigned int SystemInformationImplementation::GetLogicalProcessorsPerPhysical()
1815   const
1816 {
1817   return this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical;
1818 }
1819 
1820 /** Return the processor clock frequency. */
GetProcessorClockFrequency() const1821 float SystemInformationImplementation::GetProcessorClockFrequency() const
1822 {
1823   return this->CPUSpeedInMHz;
1824 }
1825 
1826 /**  Return the APIC ID. */
GetProcessorAPICID() const1827 int SystemInformationImplementation::GetProcessorAPICID() const
1828 {
1829   return this->Features.ExtendedFeatures.APIC_ID;
1830 }
1831 
1832 /** Return the L1 cache size. */
GetProcessorCacheSize() const1833 int SystemInformationImplementation::GetProcessorCacheSize() const
1834 {
1835   return this->Features.L1CacheSize;
1836 }
1837 
1838 /** Return the chosen cache size. */
GetProcessorCacheXSize(long int dwCacheID) const1839 int SystemInformationImplementation::GetProcessorCacheXSize(
1840   long int dwCacheID) const
1841 {
1842   switch (dwCacheID) {
1843     case SystemInformation::CPU_FEATURE_L1CACHE:
1844       return this->Features.L1CacheSize;
1845     case SystemInformation::CPU_FEATURE_L2CACHE:
1846       return this->Features.L2CacheSize;
1847     case SystemInformation::CPU_FEATURE_L3CACHE:
1848       return this->Features.L3CacheSize;
1849   }
1850   return -1;
1851 }
1852 
DoesCPUSupportFeature(long int dwFeature) const1853 bool SystemInformationImplementation::DoesCPUSupportFeature(
1854   long int dwFeature) const
1855 {
1856   bool bHasFeature = false;
1857 
1858   // Check for MMX instructions.
1859   if (((dwFeature & SystemInformation::CPU_FEATURE_MMX) != 0) &&
1860       this->Features.HasMMX)
1861     bHasFeature = true;
1862 
1863   // Check for MMX+ instructions.
1864   if (((dwFeature & SystemInformation::CPU_FEATURE_MMX_PLUS) != 0) &&
1865       this->Features.ExtendedFeatures.HasMMXPlus)
1866     bHasFeature = true;
1867 
1868   // Check for SSE FP instructions.
1869   if (((dwFeature & SystemInformation::CPU_FEATURE_SSE) != 0) &&
1870       this->Features.HasSSE)
1871     bHasFeature = true;
1872 
1873   // Check for SSE FP instructions.
1874   if (((dwFeature & SystemInformation::CPU_FEATURE_SSE_FP) != 0) &&
1875       this->Features.HasSSEFP)
1876     bHasFeature = true;
1877 
1878   // Check for SSE MMX instructions.
1879   if (((dwFeature & SystemInformation::CPU_FEATURE_SSE_MMX) != 0) &&
1880       this->Features.ExtendedFeatures.HasSSEMMX)
1881     bHasFeature = true;
1882 
1883   // Check for SSE2 instructions.
1884   if (((dwFeature & SystemInformation::CPU_FEATURE_SSE2) != 0) &&
1885       this->Features.HasSSE2)
1886     bHasFeature = true;
1887 
1888   // Check for 3DNow! instructions.
1889   if (((dwFeature & SystemInformation::CPU_FEATURE_AMD_3DNOW) != 0) &&
1890       this->Features.ExtendedFeatures.Has3DNow)
1891     bHasFeature = true;
1892 
1893   // Check for 3DNow+ instructions.
1894   if (((dwFeature & SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS) != 0) &&
1895       this->Features.ExtendedFeatures.Has3DNowPlus)
1896     bHasFeature = true;
1897 
1898   // Check for IA64 instructions.
1899   if (((dwFeature & SystemInformation::CPU_FEATURE_IA64) != 0) &&
1900       this->Features.HasIA64)
1901     bHasFeature = true;
1902 
1903   // Check for MP capable.
1904   if (((dwFeature & SystemInformation::CPU_FEATURE_MP_CAPABLE) != 0) &&
1905       this->Features.ExtendedFeatures.SupportsMP)
1906     bHasFeature = true;
1907 
1908   // Check for a serial number for the processor.
1909   if (((dwFeature & SystemInformation::CPU_FEATURE_SERIALNUMBER) != 0) &&
1910       this->Features.HasSerial)
1911     bHasFeature = true;
1912 
1913   // Check for a local APIC in the processor.
1914   if (((dwFeature & SystemInformation::CPU_FEATURE_APIC) != 0) &&
1915       this->Features.HasAPIC)
1916     bHasFeature = true;
1917 
1918   // Check for CMOV instructions.
1919   if (((dwFeature & SystemInformation::CPU_FEATURE_CMOV) != 0) &&
1920       this->Features.HasCMOV)
1921     bHasFeature = true;
1922 
1923   // Check for MTRR instructions.
1924   if (((dwFeature & SystemInformation::CPU_FEATURE_MTRR) != 0) &&
1925       this->Features.HasMTRR)
1926     bHasFeature = true;
1927 
1928   // Check for L1 cache size.
1929   if (((dwFeature & SystemInformation::CPU_FEATURE_L1CACHE) != 0) &&
1930       (this->Features.L1CacheSize != -1))
1931     bHasFeature = true;
1932 
1933   // Check for L2 cache size.
1934   if (((dwFeature & SystemInformation::CPU_FEATURE_L2CACHE) != 0) &&
1935       (this->Features.L2CacheSize != -1))
1936     bHasFeature = true;
1937 
1938   // Check for L3 cache size.
1939   if (((dwFeature & SystemInformation::CPU_FEATURE_L3CACHE) != 0) &&
1940       (this->Features.L3CacheSize != -1))
1941     bHasFeature = true;
1942 
1943   // Check for ACPI capability.
1944   if (((dwFeature & SystemInformation::CPU_FEATURE_ACPI) != 0) &&
1945       this->Features.HasACPI)
1946     bHasFeature = true;
1947 
1948   // Check for thermal monitor support.
1949   if (((dwFeature & SystemInformation::CPU_FEATURE_THERMALMONITOR) != 0) &&
1950       this->Features.HasThermal)
1951     bHasFeature = true;
1952 
1953   // Check for temperature sensing diode support.
1954   if (((dwFeature & SystemInformation::CPU_FEATURE_TEMPSENSEDIODE) != 0) &&
1955       this->Features.ExtendedFeatures.PowerManagement.HasTempSenseDiode)
1956     bHasFeature = true;
1957 
1958   // Check for frequency ID support.
1959   if (((dwFeature & SystemInformation::CPU_FEATURE_FREQUENCYID) != 0) &&
1960       this->Features.ExtendedFeatures.PowerManagement.HasFrequencyID)
1961     bHasFeature = true;
1962 
1963   // Check for voltage ID support.
1964   if (((dwFeature & SystemInformation::CPU_FEATURE_VOLTAGEID_FREQUENCY) !=
1965        0) &&
1966       this->Features.ExtendedFeatures.PowerManagement.HasVoltageID)
1967     bHasFeature = true;
1968 
1969   // Check for FPU support.
1970   if (((dwFeature & SystemInformation::CPU_FEATURE_FPU) != 0) &&
1971       this->Features.HasFPU)
1972     bHasFeature = true;
1973 
1974   return bHasFeature;
1975 }
1976 
Delay(unsigned int uiMS)1977 void SystemInformationImplementation::Delay(unsigned int uiMS)
1978 {
1979 #ifdef _WIN32
1980   LARGE_INTEGER Frequency, StartCounter, EndCounter;
1981   __int64 x;
1982 
1983   // Get the frequency of the high performance counter.
1984   if (!QueryPerformanceFrequency(&Frequency))
1985     return;
1986   x = Frequency.QuadPart / 1000 * uiMS;
1987 
1988   // Get the starting position of the counter.
1989   QueryPerformanceCounter(&StartCounter);
1990 
1991   do {
1992     // Get the ending position of the counter.
1993     QueryPerformanceCounter(&EndCounter);
1994   } while (EndCounter.QuadPart - StartCounter.QuadPart < x);
1995 #endif
1996   (void)uiMS;
1997 }
1998 
DoesCPUSupportCPUID()1999 bool SystemInformationImplementation::DoesCPUSupportCPUID()
2000 {
2001 #if USE_CPUID
2002   int dummy[4] = { 0, 0, 0, 0 };
2003 
2004 #  if USE_ASM_INSTRUCTIONS
2005   return call_cpuid(0, dummy);
2006 #  else
2007   call_cpuid(0, dummy);
2008   return dummy[0] || dummy[1] || dummy[2] || dummy[3];
2009 #  endif
2010 #else
2011   // Assume no cpuid instruction.
2012   return false;
2013 #endif
2014 }
2015 
RetrieveCPUFeatures()2016 bool SystemInformationImplementation::RetrieveCPUFeatures()
2017 {
2018 #if USE_CPUID
2019   int cpuinfo[4] = { 0, 0, 0, 0 };
2020 
2021   if (!call_cpuid(1, cpuinfo)) {
2022     return false;
2023   }
2024 
2025   // Retrieve the features of CPU present.
2026   this->Features.HasFPU =
2027     ((cpuinfo[3] & 0x00000001) != 0); // FPU Present --> Bit 0
2028   this->Features.HasTSC =
2029     ((cpuinfo[3] & 0x00000010) != 0); // TSC Present --> Bit 4
2030   this->Features.HasAPIC =
2031     ((cpuinfo[3] & 0x00000200) != 0); // APIC Present --> Bit 9
2032   this->Features.HasMTRR =
2033     ((cpuinfo[3] & 0x00001000) != 0); // MTRR Present --> Bit 12
2034   this->Features.HasCMOV =
2035     ((cpuinfo[3] & 0x00008000) != 0); // CMOV Present --> Bit 15
2036   this->Features.HasSerial =
2037     ((cpuinfo[3] & 0x00040000) != 0); // Serial Present --> Bit 18
2038   this->Features.HasACPI =
2039     ((cpuinfo[3] & 0x00400000) != 0); // ACPI Capable --> Bit 22
2040   this->Features.HasMMX =
2041     ((cpuinfo[3] & 0x00800000) != 0); // MMX Present --> Bit 23
2042   this->Features.HasSSE =
2043     ((cpuinfo[3] & 0x02000000) != 0); // SSE Present --> Bit 25
2044   this->Features.HasSSE2 =
2045     ((cpuinfo[3] & 0x04000000) != 0); // SSE2 Present --> Bit 26
2046   this->Features.HasThermal =
2047     ((cpuinfo[3] & 0x20000000) != 0); // Thermal Monitor Present --> Bit 29
2048   this->Features.HasIA64 =
2049     ((cpuinfo[3] & 0x40000000) != 0); // IA64 Present --> Bit 30
2050 
2051 #  if USE_ASM_INSTRUCTIONS
2052   // Retrieve extended SSE capabilities if SSE is available.
2053   if (this->Features.HasSSE) {
2054 
2055     // Attempt to __try some SSE FP instructions.
2056     __try {
2057       // Perform: orps xmm0, xmm0
2058       _asm
2059       {
2060         _emit 0x0f
2061         _emit 0x56
2062         _emit 0xc0
2063       }
2064 
2065       // SSE FP capable processor.
2066       this->Features.HasSSEFP = true;
2067     } __except (1) {
2068       // bad instruction - processor or OS cannot handle SSE FP.
2069       this->Features.HasSSEFP = false;
2070     }
2071   } else {
2072     // Set the advanced SSE capabilities to not available.
2073     this->Features.HasSSEFP = false;
2074   }
2075 #  else
2076   this->Features.HasSSEFP = false;
2077 #  endif
2078 
2079   // Retrieve Intel specific extended features.
2080   if (this->ChipManufacturer == Intel) {
2081     bool SupportsSMT =
2082       ((cpuinfo[3] & 0x10000000) != 0); // Intel specific: SMT --> Bit 28
2083 
2084     if ((SupportsSMT) && (this->Features.HasAPIC)) {
2085       // Retrieve APIC information if there is one present.
2086       this->Features.ExtendedFeatures.APIC_ID =
2087         ((cpuinfo[1] & 0xFF000000) >> 24);
2088     }
2089   }
2090 
2091   return true;
2092 
2093 #else
2094   return false;
2095 #endif
2096 }
2097 
2098 /** Find the manufacturer given the vendor id */
FindManufacturer(const std::string & family)2099 void SystemInformationImplementation::FindManufacturer(
2100   const std::string& family)
2101 {
2102   if (this->ChipID.Vendor == "GenuineIntel")
2103     this->ChipManufacturer = Intel; // Intel Corp.
2104   else if (this->ChipID.Vendor == "UMC UMC UMC ")
2105     this->ChipManufacturer = UMC; // United Microelectronics Corp.
2106   else if (this->ChipID.Vendor == "AuthenticAMD")
2107     this->ChipManufacturer = AMD; // Advanced Micro Devices
2108   else if (this->ChipID.Vendor == "AMD ISBETTER")
2109     this->ChipManufacturer = AMD; // Advanced Micro Devices (1994)
2110   else if (this->ChipID.Vendor == "HygonGenuine")
2111     this->ChipManufacturer = Hygon; // Chengdu Haiguang IC Design Co., Ltd.
2112   else if (this->ChipID.Vendor == "CyrixInstead")
2113     this->ChipManufacturer = Cyrix; // Cyrix Corp., VIA Inc.
2114   else if (this->ChipID.Vendor == "NexGenDriven")
2115     this->ChipManufacturer = NexGen; // NexGen Inc. (now AMD)
2116   else if (this->ChipID.Vendor == "CentaurHauls")
2117     this->ChipManufacturer = IDT; // original IDT/Centaur/VIA (now Zhaoxin)
2118   else if (this->ChipID.Vendor == "  Shanghai  ")
2119     this->ChipManufacturer =
2120       Zhaoxin; // Shanghai Zhaoxin Semiconductor Co., Ltd.
2121   else if (this->ChipID.Vendor == "RiseRiseRise")
2122     this->ChipManufacturer = Rise; // Rise
2123   else if (this->ChipID.Vendor == "GenuineTMx86")
2124     this->ChipManufacturer = Transmeta; // Transmeta
2125   else if (this->ChipID.Vendor == "TransmetaCPU")
2126     this->ChipManufacturer = Transmeta; // Transmeta
2127   else if (this->ChipID.Vendor == "Geode By NSC")
2128     this->ChipManufacturer = NSC; // National Semiconductor
2129   else if (this->ChipID.Vendor == "Sun")
2130     this->ChipManufacturer = Sun; // Sun Microelectronics
2131   else if (this->ChipID.Vendor == "IBM")
2132     this->ChipManufacturer = IBM; // IBM Microelectronics
2133   else if (this->ChipID.Vendor == "Hewlett-Packard")
2134     this->ChipManufacturer = HP; // Hewlett-Packard
2135   else if (this->ChipID.Vendor == "Motorola")
2136     this->ChipManufacturer = Motorola; // Motorola Microelectronics
2137   else if (family.compare(0, 7, "PA-RISC") == 0)
2138     this->ChipManufacturer = HP; // Hewlett-Packard
2139   else if (this->ChipID.Vendor == "Apple")
2140     this->ChipManufacturer = Apple; // Apple
2141   else
2142     this->ChipManufacturer = UnknownManufacturer; // Unknown manufacturer
2143 }
2144 
2145 /** */
RetrieveCPUIdentity()2146 bool SystemInformationImplementation::RetrieveCPUIdentity()
2147 {
2148 #if USE_CPUID
2149   int localCPUVendor[4];
2150   int localCPUSignature[4];
2151 
2152   if (!call_cpuid(0, localCPUVendor)) {
2153     return false;
2154   }
2155   if (!call_cpuid(1, localCPUSignature)) {
2156     return false;
2157   }
2158 
2159   // Process the returned information.
2160   //    ; eax = 0 --> eax: maximum value of CPUID instruction.
2161   //    ;        ebx: part 1 of 3; CPU signature.
2162   //    ;        edx: part 2 of 3; CPU signature.
2163   //    ;        ecx: part 3 of 3; CPU signature.
2164   char vbuf[13];
2165   memcpy(&(vbuf[0]), &(localCPUVendor[1]), sizeof(int));
2166   memcpy(&(vbuf[4]), &(localCPUVendor[3]), sizeof(int));
2167   memcpy(&(vbuf[8]), &(localCPUVendor[2]), sizeof(int));
2168   vbuf[12] = '\0';
2169   this->ChipID.Vendor = vbuf;
2170 
2171   // Retrieve the family of CPU present.
2172   //    ; eax = 1 --> eax: CPU ID - bits 31..16 - unused, bits 15..12 - type,
2173   //    bits 11..8 - family, bits 7..4 - model, bits 3..0 - mask revision
2174   //    ;        ebx: 31..24 - default APIC ID, 23..16 - logical processor ID,
2175   //    15..8 - CFLUSH chunk size , 7..0 - brand ID
2176   //    ;        edx: CPU feature flags
2177   this->ChipID.ExtendedFamily =
2178     ((localCPUSignature[0] & 0x0FF00000) >> 20); // Bits 27..20 Used
2179   this->ChipID.ExtendedModel =
2180     ((localCPUSignature[0] & 0x000F0000) >> 16); // Bits 19..16 Used
2181   this->ChipID.Type =
2182     ((localCPUSignature[0] & 0x0000F000) >> 12); // Bits 15..12 Used
2183   this->ChipID.Family =
2184     ((localCPUSignature[0] & 0x00000F00) >> 8); // Bits 11..8 Used
2185   this->ChipID.Model =
2186     ((localCPUSignature[0] & 0x000000F0) >> 4); // Bits 7..4 Used
2187   this->ChipID.Revision =
2188     ((localCPUSignature[0] & 0x0000000F) >> 0); // Bits 3..0 Used
2189 
2190   return true;
2191 
2192 #else
2193   return false;
2194 #endif
2195 }
2196 
2197 /** */
RetrieveCPUCacheDetails()2198 bool SystemInformationImplementation::RetrieveCPUCacheDetails()
2199 {
2200 #if USE_CPUID
2201   int L1Cache[4] = { 0, 0, 0, 0 };
2202   int L2Cache[4] = { 0, 0, 0, 0 };
2203 
2204   // Check to see if what we are about to do is supported...
2205   if (RetrieveCPUExtendedLevelSupport(0x80000005)) {
2206     if (!call_cpuid(0x80000005, L1Cache)) {
2207       return false;
2208     }
2209     // Save the L1 data cache size (in KB) from ecx: bits 31..24 as well as
2210     // data cache size from edx: bits 31..24.
2211     this->Features.L1CacheSize = ((L1Cache[2] & 0xFF000000) >> 24);
2212     this->Features.L1CacheSize += ((L1Cache[3] & 0xFF000000) >> 24);
2213   } else {
2214     // Store -1 to indicate the cache could not be queried.
2215     this->Features.L1CacheSize = -1;
2216   }
2217 
2218   // Check to see if what we are about to do is supported...
2219   if (RetrieveCPUExtendedLevelSupport(0x80000006)) {
2220     if (!call_cpuid(0x80000006, L2Cache)) {
2221       return false;
2222     }
2223     // Save the L2 unified cache size (in KB) from ecx: bits 31..16.
2224     this->Features.L2CacheSize = ((L2Cache[2] & 0xFFFF0000) >> 16);
2225   } else {
2226     // Store -1 to indicate the cache could not be queried.
2227     this->Features.L2CacheSize = -1;
2228   }
2229 
2230   // Define L3 as being not present as we cannot test for it.
2231   this->Features.L3CacheSize = -1;
2232 
2233 #endif
2234 
2235   // Return failure if we cannot detect either cache with this method.
2236   return ((this->Features.L1CacheSize == -1) &&
2237           (this->Features.L2CacheSize == -1))
2238     ? false
2239     : true;
2240 }
2241 
2242 /** */
RetrieveClassicalCPUCacheDetails()2243 bool SystemInformationImplementation::RetrieveClassicalCPUCacheDetails()
2244 {
2245 #if USE_CPUID
2246   int TLBCode = -1, TLBData = -1, L1Code = -1, L1Data = -1, L1Trace = -1,
2247       L2Unified = -1, L3Unified = -1;
2248   int TLBCacheData[4] = { 0, 0, 0, 0 };
2249   int TLBPassCounter = 0;
2250   int TLBCacheUnit = 0;
2251 
2252   do {
2253     if (!call_cpuid(2, TLBCacheData)) {
2254       return false;
2255     }
2256 
2257     int bob = ((TLBCacheData[0] & 0x00FF0000) >> 16);
2258     (void)bob;
2259     // Process the returned TLB and cache information.
2260     for (int nCounter = 0; nCounter < TLBCACHE_INFO_UNITS; nCounter++) {
2261       // First of all - decide which unit we are dealing with.
2262       switch (nCounter) {
2263         // eax: bits 8..15 : bits 16..23 : bits 24..31
2264         case 0:
2265           TLBCacheUnit = ((TLBCacheData[0] & 0x0000FF00) >> 8);
2266           break;
2267         case 1:
2268           TLBCacheUnit = ((TLBCacheData[0] & 0x00FF0000) >> 16);
2269           break;
2270         case 2:
2271           TLBCacheUnit = ((TLBCacheData[0] & 0xFF000000) >> 24);
2272           break;
2273 
2274         // ebx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31
2275         case 3:
2276           TLBCacheUnit = ((TLBCacheData[1] & 0x000000FF) >> 0);
2277           break;
2278         case 4:
2279           TLBCacheUnit = ((TLBCacheData[1] & 0x0000FF00) >> 8);
2280           break;
2281         case 5:
2282           TLBCacheUnit = ((TLBCacheData[1] & 0x00FF0000) >> 16);
2283           break;
2284         case 6:
2285           TLBCacheUnit = ((TLBCacheData[1] & 0xFF000000) >> 24);
2286           break;
2287 
2288         // ecx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31
2289         case 7:
2290           TLBCacheUnit = ((TLBCacheData[2] & 0x000000FF) >> 0);
2291           break;
2292         case 8:
2293           TLBCacheUnit = ((TLBCacheData[2] & 0x0000FF00) >> 8);
2294           break;
2295         case 9:
2296           TLBCacheUnit = ((TLBCacheData[2] & 0x00FF0000) >> 16);
2297           break;
2298         case 10:
2299           TLBCacheUnit = ((TLBCacheData[2] & 0xFF000000) >> 24);
2300           break;
2301 
2302         // edx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31
2303         case 11:
2304           TLBCacheUnit = ((TLBCacheData[3] & 0x000000FF) >> 0);
2305           break;
2306         case 12:
2307           TLBCacheUnit = ((TLBCacheData[3] & 0x0000FF00) >> 8);
2308           break;
2309         case 13:
2310           TLBCacheUnit = ((TLBCacheData[3] & 0x00FF0000) >> 16);
2311           break;
2312         case 14:
2313           TLBCacheUnit = ((TLBCacheData[3] & 0xFF000000) >> 24);
2314           break;
2315 
2316         // Default case - an error has occurred.
2317         default:
2318           return false;
2319       }
2320 
2321       // Now process the resulting unit to see what it means....
2322       switch (TLBCacheUnit) {
2323         case 0x00:
2324           break;
2325         case 0x01:
2326           STORE_TLBCACHE_INFO(TLBCode, 4);
2327           break;
2328         case 0x02:
2329           STORE_TLBCACHE_INFO(TLBCode, 4096);
2330           break;
2331         case 0x03:
2332           STORE_TLBCACHE_INFO(TLBData, 4);
2333           break;
2334         case 0x04:
2335           STORE_TLBCACHE_INFO(TLBData, 4096);
2336           break;
2337         case 0x06:
2338           STORE_TLBCACHE_INFO(L1Code, 8);
2339           break;
2340         case 0x08:
2341           STORE_TLBCACHE_INFO(L1Code, 16);
2342           break;
2343         case 0x0a:
2344           STORE_TLBCACHE_INFO(L1Data, 8);
2345           break;
2346         case 0x0c:
2347           STORE_TLBCACHE_INFO(L1Data, 16);
2348           break;
2349         case 0x10:
2350           STORE_TLBCACHE_INFO(L1Data, 16);
2351           break; // <-- FIXME: IA-64 Only
2352         case 0x15:
2353           STORE_TLBCACHE_INFO(L1Code, 16);
2354           break; // <-- FIXME: IA-64 Only
2355         case 0x1a:
2356           STORE_TLBCACHE_INFO(L2Unified, 96);
2357           break; // <-- FIXME: IA-64 Only
2358         case 0x22:
2359           STORE_TLBCACHE_INFO(L3Unified, 512);
2360           break;
2361         case 0x23:
2362           STORE_TLBCACHE_INFO(L3Unified, 1024);
2363           break;
2364         case 0x25:
2365           STORE_TLBCACHE_INFO(L3Unified, 2048);
2366           break;
2367         case 0x29:
2368           STORE_TLBCACHE_INFO(L3Unified, 4096);
2369           break;
2370         case 0x39:
2371           STORE_TLBCACHE_INFO(L2Unified, 128);
2372           break;
2373         case 0x3c:
2374           STORE_TLBCACHE_INFO(L2Unified, 256);
2375           break;
2376         case 0x40:
2377           STORE_TLBCACHE_INFO(L2Unified, 0);
2378           break; // <-- FIXME: No integrated L2 cache (P6 core) or L3 cache (P4
2379                  // core).
2380         case 0x41:
2381           STORE_TLBCACHE_INFO(L2Unified, 128);
2382           break;
2383         case 0x42:
2384           STORE_TLBCACHE_INFO(L2Unified, 256);
2385           break;
2386         case 0x43:
2387           STORE_TLBCACHE_INFO(L2Unified, 512);
2388           break;
2389         case 0x44:
2390           STORE_TLBCACHE_INFO(L2Unified, 1024);
2391           break;
2392         case 0x45:
2393           STORE_TLBCACHE_INFO(L2Unified, 2048);
2394           break;
2395         case 0x50:
2396           STORE_TLBCACHE_INFO(TLBCode, 4096);
2397           break;
2398         case 0x51:
2399           STORE_TLBCACHE_INFO(TLBCode, 4096);
2400           break;
2401         case 0x52:
2402           STORE_TLBCACHE_INFO(TLBCode, 4096);
2403           break;
2404         case 0x5b:
2405           STORE_TLBCACHE_INFO(TLBData, 4096);
2406           break;
2407         case 0x5c:
2408           STORE_TLBCACHE_INFO(TLBData, 4096);
2409           break;
2410         case 0x5d:
2411           STORE_TLBCACHE_INFO(TLBData, 4096);
2412           break;
2413         case 0x66:
2414           STORE_TLBCACHE_INFO(L1Data, 8);
2415           break;
2416         case 0x67:
2417           STORE_TLBCACHE_INFO(L1Data, 16);
2418           break;
2419         case 0x68:
2420           STORE_TLBCACHE_INFO(L1Data, 32);
2421           break;
2422         case 0x70:
2423           STORE_TLBCACHE_INFO(L1Trace, 12);
2424           break;
2425         case 0x71:
2426           STORE_TLBCACHE_INFO(L1Trace, 16);
2427           break;
2428         case 0x72:
2429           STORE_TLBCACHE_INFO(L1Trace, 32);
2430           break;
2431         case 0x77:
2432           STORE_TLBCACHE_INFO(L1Code, 16);
2433           break; // <-- FIXME: IA-64 Only
2434         case 0x79:
2435           STORE_TLBCACHE_INFO(L2Unified, 128);
2436           break;
2437         case 0x7a:
2438           STORE_TLBCACHE_INFO(L2Unified, 256);
2439           break;
2440         case 0x7b:
2441           STORE_TLBCACHE_INFO(L2Unified, 512);
2442           break;
2443         case 0x7c:
2444           STORE_TLBCACHE_INFO(L2Unified, 1024);
2445           break;
2446         case 0x7e:
2447           STORE_TLBCACHE_INFO(L2Unified, 256);
2448           break;
2449         case 0x81:
2450           STORE_TLBCACHE_INFO(L2Unified, 128);
2451           break;
2452         case 0x82:
2453           STORE_TLBCACHE_INFO(L2Unified, 256);
2454           break;
2455         case 0x83:
2456           STORE_TLBCACHE_INFO(L2Unified, 512);
2457           break;
2458         case 0x84:
2459           STORE_TLBCACHE_INFO(L2Unified, 1024);
2460           break;
2461         case 0x85:
2462           STORE_TLBCACHE_INFO(L2Unified, 2048);
2463           break;
2464         case 0x88:
2465           STORE_TLBCACHE_INFO(L3Unified, 2048);
2466           break; // <-- FIXME: IA-64 Only
2467         case 0x89:
2468           STORE_TLBCACHE_INFO(L3Unified, 4096);
2469           break; // <-- FIXME: IA-64 Only
2470         case 0x8a:
2471           STORE_TLBCACHE_INFO(L3Unified, 8192);
2472           break; // <-- FIXME: IA-64 Only
2473         case 0x8d:
2474           STORE_TLBCACHE_INFO(L3Unified, 3096);
2475           break; // <-- FIXME: IA-64 Only
2476         case 0x90:
2477           STORE_TLBCACHE_INFO(TLBCode, 262144);
2478           break; // <-- FIXME: IA-64 Only
2479         case 0x96:
2480           STORE_TLBCACHE_INFO(TLBCode, 262144);
2481           break; // <-- FIXME: IA-64 Only
2482         case 0x9b:
2483           STORE_TLBCACHE_INFO(TLBCode, 262144);
2484           break; // <-- FIXME: IA-64 Only
2485 
2486         // Default case - an error has occurred.
2487         default:
2488           return false;
2489       }
2490     }
2491 
2492     // Increment the TLB pass counter.
2493     TLBPassCounter++;
2494   } while ((TLBCacheData[0] & 0x000000FF) > TLBPassCounter);
2495 
2496   // Ok - we now have the maximum TLB, L1, L2, and L3 sizes...
2497   if ((L1Code == -1) && (L1Data == -1) && (L1Trace == -1)) {
2498     this->Features.L1CacheSize = -1;
2499   } else if ((L1Code == -1) && (L1Data == -1) && (L1Trace != -1)) {
2500     this->Features.L1CacheSize = L1Trace;
2501   } else if ((L1Code != -1) && (L1Data == -1)) {
2502     this->Features.L1CacheSize = L1Code;
2503   } else if ((L1Code == -1) && (L1Data != -1)) {
2504     this->Features.L1CacheSize = L1Data;
2505   } else if ((L1Code != -1) && (L1Data != -1)) {
2506     this->Features.L1CacheSize = L1Code + L1Data;
2507   } else {
2508     this->Features.L1CacheSize = -1;
2509   }
2510 
2511   // Ok - we now have the maximum TLB, L1, L2, and L3 sizes...
2512   if (L2Unified == -1) {
2513     this->Features.L2CacheSize = -1;
2514   } else {
2515     this->Features.L2CacheSize = L2Unified;
2516   }
2517 
2518   // Ok - we now have the maximum TLB, L1, L2, and L3 sizes...
2519   if (L3Unified == -1) {
2520     this->Features.L3CacheSize = -1;
2521   } else {
2522     this->Features.L3CacheSize = L3Unified;
2523   }
2524 
2525   return true;
2526 
2527 #else
2528   return false;
2529 #endif
2530 }
2531 
2532 /** */
RetrieveCPUClockSpeed()2533 bool SystemInformationImplementation::RetrieveCPUClockSpeed()
2534 {
2535   bool retrieved = false;
2536 
2537 #if defined(_WIN32)
2538   unsigned int uiRepetitions = 1;
2539   unsigned int uiMSecPerRepetition = 50;
2540   __int64 i64Total = 0;
2541   __int64 i64Overhead = 0;
2542 
2543   // Check if the TSC implementation works at all
2544   if (this->Features.HasTSC &&
2545       GetCyclesDifference(SystemInformationImplementation::Delay,
2546                           uiMSecPerRepetition) > 0) {
2547     for (unsigned int nCounter = 0; nCounter < uiRepetitions; nCounter++) {
2548       i64Total += GetCyclesDifference(SystemInformationImplementation::Delay,
2549                                       uiMSecPerRepetition);
2550       i64Overhead += GetCyclesDifference(
2551         SystemInformationImplementation::DelayOverhead, uiMSecPerRepetition);
2552     }
2553 
2554     // Calculate the MHz speed.
2555     i64Total -= i64Overhead;
2556     i64Total /= uiRepetitions;
2557     i64Total /= uiMSecPerRepetition;
2558     i64Total /= 1000;
2559 
2560     // Save the CPU speed.
2561     this->CPUSpeedInMHz = (float)i64Total;
2562 
2563     retrieved = true;
2564   }
2565 
2566   // If RDTSC is not supported, we fallback to trying to read this value
2567   // from the registry:
2568   if (!retrieved) {
2569     HKEY hKey = nullptr;
2570     LONG err =
2571       RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2572                     L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
2573                     KEY_READ, &hKey);
2574 
2575     if (ERROR_SUCCESS == err) {
2576       DWORD dwType = 0;
2577       DWORD data = 0;
2578       DWORD dwSize = sizeof(DWORD);
2579 
2580       err =
2581         RegQueryValueExW(hKey, L"~MHz", 0, &dwType, (LPBYTE)&data, &dwSize);
2582 
2583       if (ERROR_SUCCESS == err) {
2584         this->CPUSpeedInMHz = (float)data;
2585         retrieved = true;
2586       }
2587 
2588       RegCloseKey(hKey);
2589       hKey = nullptr;
2590     }
2591   }
2592 #endif
2593 
2594   return retrieved;
2595 }
2596 
2597 /** */
RetrieveClassicalCPUClockSpeed()2598 bool SystemInformationImplementation::RetrieveClassicalCPUClockSpeed()
2599 {
2600 #if USE_ASM_INSTRUCTIONS
2601   LARGE_INTEGER liStart, liEnd, liCountsPerSecond;
2602   double dFrequency, dDifference;
2603 
2604   // Attempt to get a starting tick count.
2605   QueryPerformanceCounter(&liStart);
2606 
2607   __try {
2608     _asm {
2609       mov eax, 0x80000000
2610       mov ebx, CLASSICAL_CPU_FREQ_LOOP
2611       Timer_Loop:
2612       bsf ecx,eax
2613       dec ebx
2614       jnz Timer_Loop
2615     }
2616   } __except (1) {
2617     return false;
2618   }
2619 
2620   // Attempt to get a starting tick count.
2621   QueryPerformanceCounter(&liEnd);
2622 
2623   // Get the difference...  NB: This is in seconds....
2624   QueryPerformanceFrequency(&liCountsPerSecond);
2625   dDifference = (((double)liEnd.QuadPart - (double)liStart.QuadPart) /
2626                  (double)liCountsPerSecond.QuadPart);
2627 
2628   // Calculate the clock speed.
2629   if (this->ChipID.Family == 3) {
2630     // 80386 processors....  Loop time is 115 cycles!
2631     dFrequency = (((CLASSICAL_CPU_FREQ_LOOP * 115) / dDifference) / 1000000);
2632   } else if (this->ChipID.Family == 4) {
2633     // 80486 processors....  Loop time is 47 cycles!
2634     dFrequency = (((CLASSICAL_CPU_FREQ_LOOP * 47) / dDifference) / 1000000);
2635   } else if (this->ChipID.Family == 5) {
2636     // Pentium processors....  Loop time is 43 cycles!
2637     dFrequency = (((CLASSICAL_CPU_FREQ_LOOP * 43) / dDifference) / 1000000);
2638   }
2639 
2640   // Save the clock speed.
2641   this->Features.CPUSpeed = (int)dFrequency;
2642 
2643   return true;
2644 
2645 #else
2646   return false;
2647 #endif
2648 }
2649 
2650 /** */
RetrieveCPUExtendedLevelSupport(int CPULevelToCheck)2651 bool SystemInformationImplementation::RetrieveCPUExtendedLevelSupport(
2652   int CPULevelToCheck)
2653 {
2654   int cpuinfo[4] = { 0, 0, 0, 0 };
2655 
2656   // The extended CPUID is supported by various vendors starting with the
2657   // following CPU models:
2658   //
2659   //    Manufacturer & Chip Name      |    Family     Model    Revision
2660   //
2661   //    AMD K6, K6-2                  |       5       6      x
2662   //    Cyrix GXm, Cyrix III "Joshua" |       5       4      x
2663   //    IDT C6-2                      |       5       8      x
2664   //    VIA Cyrix III                 |       6       5      x
2665   //    Transmeta Crusoe              |       5       x      x
2666   //    Intel Pentium 4               |       f       x      x
2667   //
2668 
2669   // We check to see if a supported processor is present...
2670   if (this->ChipManufacturer == AMD) {
2671     if (this->ChipID.Family < 5)
2672       return false;
2673     if ((this->ChipID.Family == 5) && (this->ChipID.Model < 6))
2674       return false;
2675   } else if (this->ChipManufacturer == Cyrix) {
2676     if (this->ChipID.Family < 5)
2677       return false;
2678     if ((this->ChipID.Family == 5) && (this->ChipID.Model < 4))
2679       return false;
2680     if ((this->ChipID.Family == 6) && (this->ChipID.Model < 5))
2681       return false;
2682   } else if (this->ChipManufacturer == IDT) {
2683     if (this->ChipID.Family < 5)
2684       return false;
2685     if ((this->ChipID.Family == 5) && (this->ChipID.Model < 8))
2686       return false;
2687   } else if (this->ChipManufacturer == Transmeta) {
2688     if (this->ChipID.Family < 5)
2689       return false;
2690   } else if (this->ChipManufacturer == Intel) {
2691     if (this->ChipID.Family < 0xf) {
2692       return false;
2693     }
2694   }
2695 
2696 #if USE_CPUID
2697   if (!call_cpuid(0x80000000, cpuinfo)) {
2698     return false;
2699   }
2700 #endif
2701 
2702   // Now we have to check the level wanted vs level returned...
2703   int nLevelWanted = (CPULevelToCheck & 0x7FFFFFFF);
2704   int nLevelReturn = (cpuinfo[0] & 0x7FFFFFFF);
2705 
2706   // Check to see if the level provided is supported...
2707   if (nLevelWanted > nLevelReturn) {
2708     return false;
2709   }
2710 
2711   return true;
2712 }
2713 
2714 /** */
RetrieveExtendedCPUFeatures()2715 bool SystemInformationImplementation::RetrieveExtendedCPUFeatures()
2716 {
2717 
2718   // Check that we are not using an Intel processor as it does not support
2719   // this.
2720   if (this->ChipManufacturer == Intel) {
2721     return false;
2722   }
2723 
2724   // Check to see if what we are about to do is supported...
2725   if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000001))) {
2726     return false;
2727   }
2728 
2729 #if USE_CPUID
2730   int localCPUExtendedFeatures[4] = { 0, 0, 0, 0 };
2731 
2732   if (!call_cpuid(0x80000001, localCPUExtendedFeatures)) {
2733     return false;
2734   }
2735 
2736   // Retrieve the extended features of CPU present.
2737   this->Features.ExtendedFeatures.Has3DNow =
2738     ((localCPUExtendedFeatures[3] & 0x80000000) !=
2739      0); // 3DNow Present --> Bit 31.
2740   this->Features.ExtendedFeatures.Has3DNowPlus =
2741     ((localCPUExtendedFeatures[3] & 0x40000000) !=
2742      0); // 3DNow+ Present -- > Bit 30.
2743   this->Features.ExtendedFeatures.HasSSEMMX =
2744     ((localCPUExtendedFeatures[3] & 0x00400000) !=
2745      0); // SSE MMX Present --> Bit 22.
2746   this->Features.ExtendedFeatures.SupportsMP =
2747     ((localCPUExtendedFeatures[3] & 0x00080000) !=
2748      0); // MP Capable -- > Bit 19.
2749 
2750   // Retrieve AMD specific extended features.
2751   if (this->ChipManufacturer == AMD || this->ChipManufacturer == Hygon) {
2752     this->Features.ExtendedFeatures.HasMMXPlus =
2753       ((localCPUExtendedFeatures[3] & 0x00400000) !=
2754        0); // AMD specific: MMX-SSE --> Bit 22
2755   }
2756 
2757   // Retrieve Cyrix specific extended features.
2758   if (this->ChipManufacturer == Cyrix) {
2759     this->Features.ExtendedFeatures.HasMMXPlus =
2760       ((localCPUExtendedFeatures[3] & 0x01000000) !=
2761        0); // Cyrix specific: Extended MMX --> Bit 24
2762   }
2763 
2764   return true;
2765 
2766 #else
2767   return false;
2768 #endif
2769 }
2770 
2771 /** */
RetrieveProcessorSerialNumber()2772 bool SystemInformationImplementation::RetrieveProcessorSerialNumber()
2773 {
2774   // Check to see if the processor supports the processor serial number.
2775   if (!this->Features.HasSerial) {
2776     return false;
2777   }
2778 
2779 #if USE_CPUID
2780   int SerialNumber[4];
2781 
2782   if (!call_cpuid(3, SerialNumber)) {
2783     return false;
2784   }
2785 
2786   // Process the returned information.
2787   //    ; eax = 3 --> ebx: top 32 bits are the processor signature bits --> NB:
2788   //    Transmeta only ?!?
2789   //    ;        ecx: middle 32 bits are the processor signature bits
2790   //    ;        edx: bottom 32 bits are the processor signature bits
2791   char sn[128];
2792   sprintf(sn, "%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x",
2793           ((SerialNumber[1] & 0xff000000) >> 24),
2794           ((SerialNumber[1] & 0x00ff0000) >> 16),
2795           ((SerialNumber[1] & 0x0000ff00) >> 8),
2796           ((SerialNumber[1] & 0x000000ff) >> 0),
2797           ((SerialNumber[2] & 0xff000000) >> 24),
2798           ((SerialNumber[2] & 0x00ff0000) >> 16),
2799           ((SerialNumber[2] & 0x0000ff00) >> 8),
2800           ((SerialNumber[2] & 0x000000ff) >> 0),
2801           ((SerialNumber[3] & 0xff000000) >> 24),
2802           ((SerialNumber[3] & 0x00ff0000) >> 16),
2803           ((SerialNumber[3] & 0x0000ff00) >> 8),
2804           ((SerialNumber[3] & 0x000000ff) >> 0));
2805   this->ChipID.SerialNumber = sn;
2806   return true;
2807 
2808 #else
2809   return false;
2810 #endif
2811 }
2812 
2813 /** */
RetrieveCPUPowerManagement()2814 bool SystemInformationImplementation::RetrieveCPUPowerManagement()
2815 {
2816   // Check to see if what we are about to do is supported...
2817   if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000007))) {
2818     this->Features.ExtendedFeatures.PowerManagement.HasFrequencyID = false;
2819     this->Features.ExtendedFeatures.PowerManagement.HasVoltageID = false;
2820     this->Features.ExtendedFeatures.PowerManagement.HasTempSenseDiode = false;
2821     return false;
2822   }
2823 
2824 #if USE_CPUID
2825   int localCPUPowerManagement[4] = { 0, 0, 0, 0 };
2826 
2827   if (!call_cpuid(0x80000007, localCPUPowerManagement)) {
2828     return false;
2829   }
2830 
2831   // Check for the power management capabilities of the CPU.
2832   this->Features.ExtendedFeatures.PowerManagement.HasTempSenseDiode =
2833     ((localCPUPowerManagement[3] & 0x00000001) != 0);
2834   this->Features.ExtendedFeatures.PowerManagement.HasFrequencyID =
2835     ((localCPUPowerManagement[3] & 0x00000002) != 0);
2836   this->Features.ExtendedFeatures.PowerManagement.HasVoltageID =
2837     ((localCPUPowerManagement[3] & 0x00000004) != 0);
2838 
2839   return true;
2840 
2841 #else
2842   return false;
2843 #endif
2844 }
2845 
2846 #if USE_CPUID
2847 // Used only in USE_CPUID implementation below.
SystemInformationStripLeadingSpace(std::string & str)2848 static void SystemInformationStripLeadingSpace(std::string& str)
2849 {
2850   // Because some manufacturers have leading white space - we have to
2851   // post-process the name.
2852   std::string::size_type pos = str.find_first_not_of(" ");
2853   if (pos != std::string::npos) {
2854     str.erase(0, pos);
2855   }
2856 }
2857 #endif
2858 
2859 /** */
RetrieveExtendedCPUIdentity()2860 bool SystemInformationImplementation::RetrieveExtendedCPUIdentity()
2861 {
2862   // Check to see if what we are about to do is supported...
2863   if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000002)))
2864     return false;
2865   if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000003)))
2866     return false;
2867   if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000004)))
2868     return false;
2869 
2870 #if USE_CPUID
2871   int CPUExtendedIdentity[12];
2872 
2873   if (!call_cpuid(0x80000002, CPUExtendedIdentity)) {
2874     return false;
2875   }
2876   if (!call_cpuid(0x80000003, CPUExtendedIdentity + 4)) {
2877     return false;
2878   }
2879   if (!call_cpuid(0x80000004, CPUExtendedIdentity + 8)) {
2880     return false;
2881   }
2882 
2883   // Process the returned information.
2884   char nbuf[49];
2885   memcpy(&(nbuf[0]), &(CPUExtendedIdentity[0]), sizeof(int));
2886   memcpy(&(nbuf[4]), &(CPUExtendedIdentity[1]), sizeof(int));
2887   memcpy(&(nbuf[8]), &(CPUExtendedIdentity[2]), sizeof(int));
2888   memcpy(&(nbuf[12]), &(CPUExtendedIdentity[3]), sizeof(int));
2889   memcpy(&(nbuf[16]), &(CPUExtendedIdentity[4]), sizeof(int));
2890   memcpy(&(nbuf[20]), &(CPUExtendedIdentity[5]), sizeof(int));
2891   memcpy(&(nbuf[24]), &(CPUExtendedIdentity[6]), sizeof(int));
2892   memcpy(&(nbuf[28]), &(CPUExtendedIdentity[7]), sizeof(int));
2893   memcpy(&(nbuf[32]), &(CPUExtendedIdentity[8]), sizeof(int));
2894   memcpy(&(nbuf[36]), &(CPUExtendedIdentity[9]), sizeof(int));
2895   memcpy(&(nbuf[40]), &(CPUExtendedIdentity[10]), sizeof(int));
2896   memcpy(&(nbuf[44]), &(CPUExtendedIdentity[11]), sizeof(int));
2897   nbuf[48] = '\0';
2898   this->ChipID.ProcessorName = nbuf;
2899   this->ChipID.ModelName = nbuf;
2900 
2901   // Because some manufacturers have leading white space - we have to
2902   // post-process the name.
2903   SystemInformationStripLeadingSpace(this->ChipID.ProcessorName);
2904   return true;
2905 #else
2906   return false;
2907 #endif
2908 }
2909 
2910 /** */
RetrieveClassicalCPUIdentity()2911 bool SystemInformationImplementation::RetrieveClassicalCPUIdentity()
2912 {
2913   // Start by decided which manufacturer we are using....
2914   switch (this->ChipManufacturer) {
2915     case Intel:
2916       // Check the family / model / revision to determine the CPU ID.
2917       switch (this->ChipID.Family) {
2918         case 3:
2919           this->ChipID.ProcessorName = "Newer i80386 family";
2920           break;
2921         case 4:
2922           switch (this->ChipID.Model) {
2923             case 0:
2924               this->ChipID.ProcessorName = "i80486DX-25/33";
2925               break;
2926             case 1:
2927               this->ChipID.ProcessorName = "i80486DX-50";
2928               break;
2929             case 2:
2930               this->ChipID.ProcessorName = "i80486SX";
2931               break;
2932             case 3:
2933               this->ChipID.ProcessorName = "i80486DX2";
2934               break;
2935             case 4:
2936               this->ChipID.ProcessorName = "i80486SL";
2937               break;
2938             case 5:
2939               this->ChipID.ProcessorName = "i80486SX2";
2940               break;
2941             case 7:
2942               this->ChipID.ProcessorName = "i80486DX2 WriteBack";
2943               break;
2944             case 8:
2945               this->ChipID.ProcessorName = "i80486DX4";
2946               break;
2947             case 9:
2948               this->ChipID.ProcessorName = "i80486DX4 WriteBack";
2949               break;
2950             default:
2951               this->ChipID.ProcessorName = "Unknown 80486 family";
2952               return false;
2953           }
2954           break;
2955         case 5:
2956           switch (this->ChipID.Model) {
2957             case 0:
2958               this->ChipID.ProcessorName = "P5 A-Step";
2959               break;
2960             case 1:
2961               this->ChipID.ProcessorName = "P5";
2962               break;
2963             case 2:
2964               this->ChipID.ProcessorName = "P54C";
2965               break;
2966             case 3:
2967               this->ChipID.ProcessorName = "P24T OverDrive";
2968               break;
2969             case 4:
2970               this->ChipID.ProcessorName = "P55C";
2971               break;
2972             case 7:
2973               this->ChipID.ProcessorName = "P54C";
2974               break;
2975             case 8:
2976               this->ChipID.ProcessorName = "P55C (0.25micron)";
2977               break;
2978             default:
2979               this->ChipID.ProcessorName = "Unknown Pentium family";
2980               return false;
2981           }
2982           break;
2983         case 6:
2984           switch (this->ChipID.Model) {
2985             case 0:
2986               this->ChipID.ProcessorName = "P6 A-Step";
2987               break;
2988             case 1:
2989               this->ChipID.ProcessorName = "P6";
2990               break;
2991             case 3:
2992               this->ChipID.ProcessorName = "Pentium II (0.28 micron)";
2993               break;
2994             case 5:
2995               this->ChipID.ProcessorName = "Pentium II (0.25 micron)";
2996               break;
2997             case 6:
2998               this->ChipID.ProcessorName = "Pentium II With On-Die L2 Cache";
2999               break;
3000             case 7:
3001               this->ChipID.ProcessorName = "Pentium III (0.25 micron)";
3002               break;
3003             case 8:
3004               this->ChipID.ProcessorName =
3005                 "Pentium III (0.18 micron) With 256 KB On-Die L2 Cache ";
3006               break;
3007             case 0xa:
3008               this->ChipID.ProcessorName =
3009                 "Pentium III (0.18 micron) With 1 Or 2 MB On-Die L2 Cache ";
3010               break;
3011             case 0xb:
3012               this->ChipID.ProcessorName = "Pentium III (0.13 micron) With "
3013                                            "256 Or 512 KB On-Die L2 Cache ";
3014               break;
3015             case 23:
3016               this->ChipID.ProcessorName =
3017                 "Intel(R) Core(TM)2 Duo CPU     T9500  @ 2.60GHz";
3018               break;
3019             default:
3020               this->ChipID.ProcessorName = "Unknown P6 family";
3021               return false;
3022           }
3023           break;
3024         case 7:
3025           this->ChipID.ProcessorName = "Intel Merced (IA-64)";
3026           break;
3027         case 0xf:
3028           // Check the extended family bits...
3029           switch (this->ChipID.ExtendedFamily) {
3030             case 0:
3031               switch (this->ChipID.Model) {
3032                 case 0:
3033                   this->ChipID.ProcessorName = "Pentium IV (0.18 micron)";
3034                   break;
3035                 case 1:
3036                   this->ChipID.ProcessorName = "Pentium IV (0.18 micron)";
3037                   break;
3038                 case 2:
3039                   this->ChipID.ProcessorName = "Pentium IV (0.13 micron)";
3040                   break;
3041                 default:
3042                   this->ChipID.ProcessorName = "Unknown Pentium 4 family";
3043                   return false;
3044               }
3045               break;
3046             case 1:
3047               this->ChipID.ProcessorName = "Intel McKinley (IA-64)";
3048               break;
3049             default:
3050               this->ChipID.ProcessorName = "Pentium";
3051           }
3052           break;
3053         default:
3054           this->ChipID.ProcessorName = "Unknown Intel family";
3055           return false;
3056       }
3057       break;
3058 
3059     case AMD:
3060       // Check the family / model / revision to determine the CPU ID.
3061       switch (this->ChipID.Family) {
3062         case 4:
3063           switch (this->ChipID.Model) {
3064             case 3:
3065               this->ChipID.ProcessorName = "80486DX2";
3066               break;
3067             case 7:
3068               this->ChipID.ProcessorName = "80486DX2 WriteBack";
3069               break;
3070             case 8:
3071               this->ChipID.ProcessorName = "80486DX4";
3072               break;
3073             case 9:
3074               this->ChipID.ProcessorName = "80486DX4 WriteBack";
3075               break;
3076             case 0xe:
3077               this->ChipID.ProcessorName = "5x86";
3078               break;
3079             case 0xf:
3080               this->ChipID.ProcessorName = "5x86WB";
3081               break;
3082             default:
3083               this->ChipID.ProcessorName = "Unknown 80486 family";
3084               return false;
3085           }
3086           break;
3087         case 5:
3088           switch (this->ChipID.Model) {
3089             case 0:
3090               this->ChipID.ProcessorName = "SSA5 (PR75, PR90 =  PR100)";
3091               break;
3092             case 1:
3093               this->ChipID.ProcessorName = "5k86 (PR120 =  PR133)";
3094               break;
3095             case 2:
3096               this->ChipID.ProcessorName = "5k86 (PR166)";
3097               break;
3098             case 3:
3099               this->ChipID.ProcessorName = "5k86 (PR200)";
3100               break;
3101             case 6:
3102               this->ChipID.ProcessorName = "K6 (0.30 micron)";
3103               break;
3104             case 7:
3105               this->ChipID.ProcessorName = "K6 (0.25 micron)";
3106               break;
3107             case 8:
3108               this->ChipID.ProcessorName = "K6-2";
3109               break;
3110             case 9:
3111               this->ChipID.ProcessorName = "K6-III";
3112               break;
3113             case 0xd:
3114               this->ChipID.ProcessorName = "K6-2+ or K6-III+ (0.18 micron)";
3115               break;
3116             default:
3117               this->ChipID.ProcessorName = "Unknown 80586 family";
3118               return false;
3119           }
3120           break;
3121         case 6:
3122           switch (this->ChipID.Model) {
3123             case 1:
3124               this->ChipID.ProcessorName = "Athlon- (0.25 micron)";
3125               break;
3126             case 2:
3127               this->ChipID.ProcessorName = "Athlon- (0.18 micron)";
3128               break;
3129             case 3:
3130               this->ChipID.ProcessorName = "Duron- (SF core)";
3131               break;
3132             case 4:
3133               this->ChipID.ProcessorName = "Athlon- (Thunderbird core)";
3134               break;
3135             case 6:
3136               this->ChipID.ProcessorName = "Athlon- (Palomino core)";
3137               break;
3138             case 7:
3139               this->ChipID.ProcessorName = "Duron- (Morgan core)";
3140               break;
3141             case 8:
3142               if (this->Features.ExtendedFeatures.SupportsMP)
3143                 this->ChipID.ProcessorName = "Athlon - MP (Thoroughbred core)";
3144               else
3145                 this->ChipID.ProcessorName = "Athlon - XP (Thoroughbred core)";
3146               break;
3147             default:
3148               this->ChipID.ProcessorName = "Unknown K7 family";
3149               return false;
3150           }
3151           break;
3152         default:
3153           this->ChipID.ProcessorName = "Unknown AMD family";
3154           return false;
3155       }
3156       break;
3157 
3158     case Hygon:
3159       this->ChipID.ProcessorName = "Unknown Hygon family";
3160       return false;
3161 
3162     case Transmeta:
3163       switch (this->ChipID.Family) {
3164         case 5:
3165           switch (this->ChipID.Model) {
3166             case 4:
3167               this->ChipID.ProcessorName = "Crusoe TM3x00 and TM5x00";
3168               break;
3169             default:
3170               this->ChipID.ProcessorName = "Unknown Crusoe family";
3171               return false;
3172           }
3173           break;
3174         default:
3175           this->ChipID.ProcessorName = "Unknown Transmeta family";
3176           return false;
3177       }
3178       break;
3179 
3180     case Rise:
3181       switch (this->ChipID.Family) {
3182         case 5:
3183           switch (this->ChipID.Model) {
3184             case 0:
3185               this->ChipID.ProcessorName = "mP6 (0.25 micron)";
3186               break;
3187             case 2:
3188               this->ChipID.ProcessorName = "mP6 (0.18 micron)";
3189               break;
3190             default:
3191               this->ChipID.ProcessorName = "Unknown Rise family";
3192               return false;
3193           }
3194           break;
3195         default:
3196           this->ChipID.ProcessorName = "Unknown Rise family";
3197           return false;
3198       }
3199       break;
3200 
3201     case UMC:
3202       switch (this->ChipID.Family) {
3203         case 4:
3204           switch (this->ChipID.Model) {
3205             case 1:
3206               this->ChipID.ProcessorName = "U5D";
3207               break;
3208             case 2:
3209               this->ChipID.ProcessorName = "U5S";
3210               break;
3211             default:
3212               this->ChipID.ProcessorName = "Unknown UMC family";
3213               return false;
3214           }
3215           break;
3216         default:
3217           this->ChipID.ProcessorName = "Unknown UMC family";
3218           return false;
3219       }
3220       break;
3221 
3222     case IDT:
3223       switch (this->ChipID.Family) {
3224         case 5:
3225           switch (this->ChipID.Model) {
3226             case 4:
3227               this->ChipID.ProcessorName = "C6";
3228               break;
3229             case 8:
3230               this->ChipID.ProcessorName = "C2";
3231               break;
3232             case 9:
3233               this->ChipID.ProcessorName = "C3";
3234               break;
3235             default:
3236               this->ChipID.ProcessorName =
3237                 "Unknown IDT\\Centaur\\VIA\\Zhaoxin family";
3238               return false;
3239           }
3240           break;
3241         case 6:
3242           switch (this->ChipID.Model) {
3243             case 6:
3244               this->ChipID.ProcessorName = "VIA Cyrix III - Samuel";
3245               break;
3246             case 0xf:
3247               this->ChipID.ProcessorName = "Zhaoxin zxc";
3248               break;
3249             default:
3250               this->ChipID.ProcessorName =
3251                 "Unknown IDT\\Centaur\\VIA\\Zhaoxin family";
3252               return false;
3253           }
3254           break;
3255         case 7:
3256           switch (this->ChipID.Model) {
3257             case 0x1b:
3258               this->ChipID.ProcessorName = "Zhaoxin kx5000";
3259               break;
3260             case 0x3b:
3261               this->ChipID.ProcessorName = "Zhaoxin kx6000";
3262               break;
3263             default:
3264               this->ChipID.ProcessorName =
3265                 "Unknown IDT\\Centaur\\VIA\\Zhaoxin family";
3266               return false;
3267           }
3268           break;
3269         default:
3270           this->ChipID.ProcessorName =
3271             "Unknown IDT\\Centaur\\VIA\\Zhaoxin family";
3272           return false;
3273       }
3274       break;
3275 
3276     case Zhaoxin:
3277       switch (this->ChipID.Family) {
3278         case 6:
3279           switch (this->ChipID.Model) {
3280             case 0x19:
3281               this->ChipID.ProcessorName = "Zhaoxin zxc";
3282               break;
3283             default:
3284               this->ChipID.ProcessorName = "Unknown Zhaoxin family";
3285               return false;
3286           }
3287           break;
3288         case 7:
3289           switch (this->ChipID.Model) {
3290             case 0x1b:
3291               this->ChipID.ProcessorName = "Zhaoxin kx5000";
3292               break;
3293             case 0x3b:
3294               this->ChipID.ProcessorName = "Zhaoxin kx6000";
3295               break;
3296             default:
3297               this->ChipID.ProcessorName = "Unknown Zhaoxin family";
3298               return false;
3299           }
3300           break;
3301         default:
3302           this->ChipID.ProcessorName = "Unknown Zhaoxin family";
3303           return false;
3304       }
3305       break;
3306 
3307     case Cyrix:
3308       switch (this->ChipID.Family) {
3309         case 4:
3310           switch (this->ChipID.Model) {
3311             case 4:
3312               this->ChipID.ProcessorName = "MediaGX GX =  GXm";
3313               break;
3314             case 9:
3315               this->ChipID.ProcessorName = "5x86";
3316               break;
3317             default:
3318               this->ChipID.ProcessorName = "Unknown Cx5x86 family";
3319               return false;
3320           }
3321           break;
3322         case 5:
3323           switch (this->ChipID.Model) {
3324             case 2:
3325               this->ChipID.ProcessorName = "Cx6x86";
3326               break;
3327             case 4:
3328               this->ChipID.ProcessorName = "MediaGX GXm";
3329               break;
3330             default:
3331               this->ChipID.ProcessorName = "Unknown Cx6x86 family";
3332               return false;
3333           }
3334           break;
3335         case 6:
3336           switch (this->ChipID.Model) {
3337             case 0:
3338               this->ChipID.ProcessorName = "6x86MX";
3339               break;
3340             case 5:
3341               this->ChipID.ProcessorName = "Cyrix M2 Core";
3342               break;
3343             case 6:
3344               this->ChipID.ProcessorName = "WinChip C5A Core";
3345               break;
3346             case 7:
3347               this->ChipID.ProcessorName = "WinChip C5B\\C5C Core";
3348               break;
3349             case 8:
3350               this->ChipID.ProcessorName = "WinChip C5C-T Core";
3351               break;
3352             default:
3353               this->ChipID.ProcessorName = "Unknown 6x86MX\\Cyrix III family";
3354               return false;
3355           }
3356           break;
3357         default:
3358           this->ChipID.ProcessorName = "Unknown Cyrix family";
3359           return false;
3360       }
3361       break;
3362 
3363     case NexGen:
3364       switch (this->ChipID.Family) {
3365         case 5:
3366           switch (this->ChipID.Model) {
3367             case 0:
3368               this->ChipID.ProcessorName = "Nx586 or Nx586FPU";
3369               break;
3370             default:
3371               this->ChipID.ProcessorName = "Unknown NexGen family";
3372               return false;
3373           }
3374           break;
3375         default:
3376           this->ChipID.ProcessorName = "Unknown NexGen family";
3377           return false;
3378       }
3379       break;
3380 
3381     case NSC:
3382       this->ChipID.ProcessorName = "Cx486SLC \\ DLC \\ Cx486S A-Step";
3383       break;
3384 
3385     case Sun:
3386     case IBM:
3387     case Motorola:
3388     case HP:
3389     case UnknownManufacturer:
3390     default:
3391       this->ChipID.ProcessorName =
3392         "Unknown family"; // We cannot identify the processor.
3393       return false;
3394   }
3395 
3396   return true;
3397 }
3398 
3399 /** Extract a value from the CPUInfo file */
ExtractValueFromCpuInfoFile(std::string buffer,const char * word,size_t init)3400 std::string SystemInformationImplementation::ExtractValueFromCpuInfoFile(
3401   std::string buffer, const char* word, size_t init)
3402 {
3403   size_t pos = buffer.find(word, init);
3404   if (pos != std::string::npos) {
3405     this->CurrentPositionInFile = pos;
3406     pos = buffer.find(':', pos);
3407     size_t pos2 = buffer.find('\n', pos);
3408     if (pos != std::string::npos && pos2 != std::string::npos) {
3409       // It may happen that the beginning matches, but this is still not the
3410       // requested key.
3411       // An example is looking for "cpu" when "cpu family" comes first. So we
3412       // check that
3413       // we have only spaces from here to pos, otherwise we search again.
3414       for (size_t i = this->CurrentPositionInFile + strlen(word); i < pos;
3415            ++i) {
3416         if (buffer[i] != ' ' && buffer[i] != '\t') {
3417           return this->ExtractValueFromCpuInfoFile(buffer, word, pos2);
3418         }
3419       }
3420       buffer.erase(0, pos + 2);
3421       buffer.resize(pos2 - pos - 2);
3422       return buffer;
3423     }
3424   }
3425   this->CurrentPositionInFile = std::string::npos;
3426   return "";
3427 }
3428 
3429 /** Query for the cpu status */
RetreiveInformationFromCpuInfoFile()3430 bool SystemInformationImplementation::RetreiveInformationFromCpuInfoFile()
3431 {
3432   this->NumberOfLogicalCPU = 0;
3433   this->NumberOfPhysicalCPU = 0;
3434   std::string buffer;
3435 
3436   FILE* fd = fopen("/proc/cpuinfo", "r");
3437   if (!fd) {
3438     std::cout << "Problem opening /proc/cpuinfo" << std::endl;
3439     return false;
3440   }
3441 
3442   size_t fileSize = 0;
3443   while (!feof(fd)) {
3444     buffer += static_cast<char>(fgetc(fd));
3445     fileSize++;
3446   }
3447   fclose(fd);
3448   buffer.resize(fileSize - 2);
3449   // Number of logical CPUs (combination of multiple processors, multi-core
3450   // and SMT)
3451   size_t pos = buffer.find("processor\t");
3452   while (pos != std::string::npos) {
3453     this->NumberOfLogicalCPU++;
3454     pos = buffer.find("processor\t", pos + 1);
3455   }
3456 
3457 #if defined(__linux) || defined(__CYGWIN__)
3458   // Count sockets.
3459   std::set<int> PhysicalIDs;
3460   std::string idc = this->ExtractValueFromCpuInfoFile(buffer, "physical id");
3461   while (this->CurrentPositionInFile != std::string::npos) {
3462     int id = atoi(idc.c_str());
3463     PhysicalIDs.insert(id);
3464     idc = this->ExtractValueFromCpuInfoFile(buffer, "physical id",
3465                                             this->CurrentPositionInFile + 1);
3466   }
3467   uint64_t NumberOfSockets = PhysicalIDs.size();
3468   NumberOfSockets = std::max(NumberOfSockets, (uint64_t)1);
3469   // Physical ids returned by Linux don't distinguish cores.
3470   // We want to record the total number of cores in this->NumberOfPhysicalCPU
3471   // (checking only the first proc)
3472   std::string Cores = this->ExtractValueFromCpuInfoFile(buffer, "cpu cores");
3473   if (Cores.empty()) {
3474     // Linux Sparc is different
3475     Cores = this->ExtractValueFromCpuInfoFile(buffer, "ncpus probed");
3476   }
3477   auto NumberOfCoresPerSocket = (unsigned int)atoi(Cores.c_str());
3478   NumberOfCoresPerSocket = std::max(NumberOfCoresPerSocket, 1u);
3479   this->NumberOfPhysicalCPU =
3480     NumberOfCoresPerSocket * (unsigned int)NumberOfSockets;
3481 
3482 #else
3483   // For systems which do not have "physical id" entries, neither "cpu cores"
3484   // this has to be fixed for hyper-threading.
3485   std::string cpucount =
3486     this->ExtractValueFromCpuInfoFile(buffer, "cpu count");
3487   this->NumberOfPhysicalCPU = this->NumberOfLogicalCPU =
3488     atoi(cpucount.c_str());
3489 #endif
3490   // gotta have one, and if this is 0 then we get a / by 0n
3491   // better to have a bad answer than a crash
3492   if (this->NumberOfPhysicalCPU <= 0) {
3493     this->NumberOfPhysicalCPU = 1;
3494   }
3495   if (this->NumberOfLogicalCPU == 0) {
3496     this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU;
3497   }
3498   // LogicalProcessorsPerPhysical>1 => SMT.
3499   this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical =
3500     this->NumberOfLogicalCPU / this->NumberOfPhysicalCPU;
3501 
3502   // CPU speed (checking only the first processor)
3503   std::string CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "cpu MHz");
3504   if (!CPUSpeed.empty()) {
3505     this->CPUSpeedInMHz = static_cast<float>(atof(CPUSpeed.c_str()));
3506   }
3507 #ifdef __linux
3508   else {
3509     // Linux Sparc: CPU speed is in Hz and encoded in hexadecimal
3510     CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "Cpu0ClkTck");
3511     if (!CPUSpeed.empty()) {
3512       this->CPUSpeedInMHz =
3513         static_cast<float>(strtoull(CPUSpeed.c_str(), nullptr, 16)) /
3514         1000000.0f;
3515     } else {
3516       // if the kernel is build as Sparc32 it's in decimal, note the different
3517       // case
3518       CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "CPU0ClkTck");
3519       this->CPUSpeedInMHz =
3520         static_cast<float>(strtoull(CPUSpeed.c_str(), nullptr, 10)) /
3521         1000000.0f;
3522     }
3523   }
3524 #endif
3525 
3526   // Chip family
3527   std::string familyStr =
3528     this->ExtractValueFromCpuInfoFile(buffer, "cpu family");
3529   if (familyStr.empty()) {
3530     familyStr = this->ExtractValueFromCpuInfoFile(buffer, "CPU architecture");
3531   }
3532   this->ChipID.Family = atoi(familyStr.c_str());
3533 
3534   // Chip Vendor
3535   this->ChipID.Vendor = this->ExtractValueFromCpuInfoFile(buffer, "vendor_id");
3536   this->FindManufacturer(familyStr);
3537 
3538   // second try for setting family
3539   if (this->ChipID.Family == 0 && this->ChipManufacturer == HP) {
3540     if (familyStr == "PA-RISC 1.1a")
3541       this->ChipID.Family = 0x11a;
3542     else if (familyStr == "PA-RISC 2.0")
3543       this->ChipID.Family = 0x200;
3544     // If you really get CMake to work on a machine not belonging to
3545     // any of those families I owe you a dinner if you get it to
3546     // contribute nightly builds regularly.
3547   }
3548 
3549   // Chip Model
3550   this->ChipID.Model =
3551     atoi(this->ExtractValueFromCpuInfoFile(buffer, "model").c_str());
3552   if (!this->RetrieveClassicalCPUIdentity()) {
3553     // Some platforms (e.g. PA-RISC) tell us their CPU name here.
3554     // Note: x86 does not.
3555     std::string cpuname = this->ExtractValueFromCpuInfoFile(buffer, "cpu");
3556     if (!cpuname.empty()) {
3557       this->ChipID.ProcessorName = cpuname;
3558     }
3559   }
3560 
3561   // Chip revision
3562   std::string cpurev = this->ExtractValueFromCpuInfoFile(buffer, "stepping");
3563   if (cpurev.empty()) {
3564     cpurev = this->ExtractValueFromCpuInfoFile(buffer, "CPU revision");
3565   }
3566   this->ChipID.Revision = atoi(cpurev.c_str());
3567 
3568   // Chip Model Name
3569   this->ChipID.ModelName =
3570     this->ExtractValueFromCpuInfoFile(buffer, "model name");
3571 
3572   // L1 Cache size
3573   // Different architectures may show different names for the caches.
3574   // Sum up everything we find.
3575   std::vector<const char*> cachename;
3576   cachename.clear();
3577 
3578   cachename.push_back("cache size"); // e.g. x86
3579   cachename.push_back("I-cache");    // e.g. PA-RISC
3580   cachename.push_back("D-cache");    // e.g. PA-RISC
3581 
3582   this->Features.L1CacheSize = 0;
3583   for (auto& index : cachename) {
3584     std::string cacheSize = this->ExtractValueFromCpuInfoFile(buffer, index);
3585     if (!cacheSize.empty()) {
3586       pos = cacheSize.find(" KB");
3587       if (pos != std::string::npos) {
3588         cacheSize.resize(pos);
3589       }
3590       this->Features.L1CacheSize += atoi(cacheSize.c_str());
3591     }
3592   }
3593 
3594   // processor feature flags (probably x86 specific)
3595   std::string cpuflags = this->ExtractValueFromCpuInfoFile(buffer, "flags");
3596   if (!cpurev.empty()) {
3597     // now we can match every flags as space + flag + space
3598     cpuflags = " " + cpuflags + " ";
3599     if ((cpuflags.find(" fpu ") != std::string::npos)) {
3600       this->Features.HasFPU = true;
3601     }
3602     if ((cpuflags.find(" tsc ") != std::string::npos)) {
3603       this->Features.HasTSC = true;
3604     }
3605     if ((cpuflags.find(" mmx ") != std::string::npos)) {
3606       this->Features.HasMMX = true;
3607     }
3608     if ((cpuflags.find(" sse ") != std::string::npos)) {
3609       this->Features.HasSSE = true;
3610     }
3611     if ((cpuflags.find(" sse2 ") != std::string::npos)) {
3612       this->Features.HasSSE2 = true;
3613     }
3614     if ((cpuflags.find(" apic ") != std::string::npos)) {
3615       this->Features.HasAPIC = true;
3616     }
3617     if ((cpuflags.find(" cmov ") != std::string::npos)) {
3618       this->Features.HasCMOV = true;
3619     }
3620     if ((cpuflags.find(" mtrr ") != std::string::npos)) {
3621       this->Features.HasMTRR = true;
3622     }
3623     if ((cpuflags.find(" acpi ") != std::string::npos)) {
3624       this->Features.HasACPI = true;
3625     }
3626     if ((cpuflags.find(" 3dnow ") != std::string::npos)) {
3627       this->Features.ExtendedFeatures.Has3DNow = true;
3628     }
3629   }
3630 
3631   return true;
3632 }
3633 
QueryProcessorBySysconf()3634 bool SystemInformationImplementation::QueryProcessorBySysconf()
3635 {
3636 #if defined(_SC_NPROC_ONLN) && !defined(_SC_NPROCESSORS_ONLN)
3637 // IRIX names this slightly different
3638 #  define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
3639 #endif
3640 
3641 #ifdef _SC_NPROCESSORS_ONLN
3642   long c = sysconf(_SC_NPROCESSORS_ONLN);
3643   if (c <= 0) {
3644     return false;
3645   }
3646 
3647   this->NumberOfPhysicalCPU = static_cast<unsigned int>(c);
3648   this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU;
3649 
3650   return true;
3651 #else
3652   return false;
3653 #endif
3654 }
3655 
QueryProcessor()3656 bool SystemInformationImplementation::QueryProcessor()
3657 {
3658   return this->QueryProcessorBySysconf();
3659 }
3660 
3661 /**
3662 Get total system RAM in units of KiB.
3663 */
GetHostMemoryTotal()3664 long long SystemInformationImplementation::GetHostMemoryTotal()
3665 {
3666 #if defined(_WIN32)
3667 #  if defined(_MSC_VER) && _MSC_VER < 1300
3668   MEMORYSTATUS stat;
3669   stat.dwLength = sizeof(stat);
3670   GlobalMemoryStatus(&stat);
3671   return stat.dwTotalPhys / 1024;
3672 #  else
3673   MEMORYSTATUSEX statex;
3674   statex.dwLength = sizeof(statex);
3675   GlobalMemoryStatusEx(&statex);
3676   return statex.ullTotalPhys / 1024;
3677 #  endif
3678 #elif defined(__linux) || defined(__CYGWIN__)
3679   long long memTotal = 0;
3680   int ierr = GetFieldFromFile("/proc/meminfo", "MemTotal:", memTotal);
3681   if (ierr) {
3682     return -1;
3683   }
3684   return memTotal;
3685 #elif defined(__APPLE__)
3686   uint64_t mem;
3687   size_t len = sizeof(mem);
3688   int ierr = sysctlbyname("hw.memsize", &mem, &len, nullptr, 0);
3689   if (ierr) {
3690     return -1;
3691   }
3692   return mem / 1024;
3693 #else
3694   return 0;
3695 #endif
3696 }
3697 
3698 /**
3699 Get total system RAM in units of KiB. This may differ from the
3700 host total if a host-wide resource limit is applied.
3701 */
GetHostMemoryAvailable(const char * hostLimitEnvVarName)3702 long long SystemInformationImplementation::GetHostMemoryAvailable(
3703   const char* hostLimitEnvVarName)
3704 {
3705   long long memTotal = this->GetHostMemoryTotal();
3706 
3707   // the following mechanism is provided for systems that
3708   // apply resource limits across groups of processes.
3709   // this is of use on certain SMP systems (eg. SGI UV)
3710   // where the host has a large amount of ram but a given user's
3711   // access to it is severely restricted. The system will
3712   // apply a limit across a set of processes. Units are in KiB.
3713   if (hostLimitEnvVarName) {
3714     const char* hostLimitEnvVarValue = getenv(hostLimitEnvVarName);
3715     if (hostLimitEnvVarValue) {
3716       long long hostLimit = std::atoll(hostLimitEnvVarValue);
3717       if (hostLimit > 0) {
3718         memTotal = min(hostLimit, memTotal);
3719       }
3720     }
3721   }
3722 
3723   return memTotal;
3724 }
3725 
3726 /**
3727 Get total system RAM in units of KiB. This may differ from the
3728 host total if a per-process resource limit is applied.
3729 */
GetProcMemoryAvailable(const char * hostLimitEnvVarName,const char * procLimitEnvVarName)3730 long long SystemInformationImplementation::GetProcMemoryAvailable(
3731   const char* hostLimitEnvVarName, const char* procLimitEnvVarName)
3732 {
3733   long long memAvail = this->GetHostMemoryAvailable(hostLimitEnvVarName);
3734 
3735   // the following mechanism is provide for systems where rlimits
3736   // are not employed. Units are in KiB.
3737   if (procLimitEnvVarName) {
3738     const char* procLimitEnvVarValue = getenv(procLimitEnvVarName);
3739     if (procLimitEnvVarValue) {
3740       long long procLimit = std::atoll(procLimitEnvVarValue);
3741       if (procLimit > 0) {
3742         memAvail = min(procLimit, memAvail);
3743       }
3744     }
3745   }
3746 
3747 #if defined(__linux)
3748   int ierr;
3749   ResourceLimitType rlim;
3750   ierr = GetResourceLimit(RLIMIT_DATA, &rlim);
3751   if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) {
3752     memAvail = min((long long)rlim.rlim_cur / 1024, memAvail);
3753   }
3754 
3755   ierr = GetResourceLimit(RLIMIT_AS, &rlim);
3756   if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) {
3757     memAvail = min((long long)rlim.rlim_cur / 1024, memAvail);
3758   }
3759 #elif defined(__APPLE__)
3760   struct rlimit rlim;
3761   int ierr;
3762   ierr = getrlimit(RLIMIT_DATA, &rlim);
3763   if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) {
3764     memAvail = min((long long)rlim.rlim_cur / 1024, memAvail);
3765   }
3766 
3767   ierr = getrlimit(RLIMIT_RSS, &rlim);
3768   if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) {
3769     memAvail = min((long long)rlim.rlim_cur / 1024, memAvail);
3770   }
3771 #endif
3772 
3773   return memAvail;
3774 }
3775 
3776 /**
3777 Get RAM used by all processes in the host, in units of KiB.
3778 */
GetHostMemoryUsed()3779 long long SystemInformationImplementation::GetHostMemoryUsed()
3780 {
3781 #if defined(_WIN32)
3782 #  if defined(_MSC_VER) && _MSC_VER < 1300
3783   MEMORYSTATUS stat;
3784   stat.dwLength = sizeof(stat);
3785   GlobalMemoryStatus(&stat);
3786   return (stat.dwTotalPhys - stat.dwAvailPhys) / 1024;
3787 #  else
3788   MEMORYSTATUSEX statex;
3789   statex.dwLength = sizeof(statex);
3790   GlobalMemoryStatusEx(&statex);
3791   return (statex.ullTotalPhys - statex.ullAvailPhys) / 1024;
3792 #  endif
3793 #elif defined(__CYGWIN__)
3794   const char* names[3] = { "MemTotal:", "MemFree:", nullptr };
3795   long long values[2] = { 0 };
3796   int ierr = GetFieldsFromFile("/proc/meminfo", names, values);
3797   if (ierr) {
3798     return ierr;
3799   }
3800   long long& memTotal = values[0];
3801   long long& memFree = values[1];
3802   return memTotal - memFree;
3803 #elif defined(__linux)
3804   // First try to use MemAvailable, but it only works on newer kernels
3805   const char* names2[3] = { "MemTotal:", "MemAvailable:", nullptr };
3806   long long values2[2] = { 0 };
3807   int ierr = GetFieldsFromFile("/proc/meminfo", names2, values2);
3808   if (ierr) {
3809     const char* names4[5] = { "MemTotal:", "MemFree:", "Buffers:", "Cached:",
3810                               nullptr };
3811     long long values4[4] = { 0 };
3812     ierr = GetFieldsFromFile("/proc/meminfo", names4, values4);
3813     if (ierr) {
3814       return ierr;
3815     }
3816     long long& memTotal = values4[0];
3817     long long& memFree = values4[1];
3818     long long& memBuffers = values4[2];
3819     long long& memCached = values4[3];
3820     return memTotal - memFree - memBuffers - memCached;
3821   }
3822   long long& memTotal = values2[0];
3823   long long& memAvail = values2[1];
3824   return memTotal - memAvail;
3825 #elif defined(__APPLE__)
3826   long long psz = getpagesize();
3827   if (psz < 1) {
3828     return -1;
3829   }
3830   const char* names[3] = { "Pages wired down:", "Pages active:", nullptr };
3831   long long values[2] = { 0 };
3832   int ierr = GetFieldsFromCommand("vm_stat", names, values);
3833   if (ierr) {
3834     return -1;
3835   }
3836   long long& vmWired = values[0];
3837   long long& vmActive = values[1];
3838   return ((vmActive + vmWired) * psz) / 1024;
3839 #else
3840   return 0;
3841 #endif
3842 }
3843 
3844 /**
3845 Get system RAM used by the process associated with the given
3846 process id in units of KiB.
3847 */
GetProcMemoryUsed()3848 long long SystemInformationImplementation::GetProcMemoryUsed()
3849 {
3850 #if defined(_WIN32) && defined(KWSYS_SYS_HAS_PSAPI)
3851   long pid = GetCurrentProcessId();
3852   HANDLE hProc;
3853   hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
3854   if (hProc == 0) {
3855     return -1;
3856   }
3857   PROCESS_MEMORY_COUNTERS pmc;
3858   int ok = GetProcessMemoryInfo(hProc, &pmc, sizeof(pmc));
3859   CloseHandle(hProc);
3860   if (!ok) {
3861     return -2;
3862   }
3863   return pmc.WorkingSetSize / 1024;
3864 #elif defined(__linux) || defined(__CYGWIN__)
3865   long long memUsed = 0;
3866   int ierr = GetFieldFromFile("/proc/self/status", "VmRSS:", memUsed);
3867   if (ierr) {
3868     return -1;
3869   }
3870   return memUsed;
3871 #elif defined(__APPLE__)
3872   long long memUsed = 0;
3873   pid_t pid = getpid();
3874   std::ostringstream oss;
3875   oss << "ps -o rss= -p " << pid;
3876   FILE* file = popen(oss.str().c_str(), "r");
3877   if (file == nullptr) {
3878     return -1;
3879   }
3880   oss.str("");
3881   while (!feof(file) && !ferror(file)) {
3882     char buf[256] = { '\0' };
3883     errno = 0;
3884     size_t nRead = fread(buf, 1, 256, file);
3885     if (ferror(file) && (errno == EINTR)) {
3886       clearerr(file);
3887     }
3888     if (nRead)
3889       oss << buf;
3890   }
3891   int ierr = ferror(file);
3892   pclose(file);
3893   if (ierr) {
3894     return -2;
3895   }
3896   std::istringstream iss(oss.str());
3897   iss >> memUsed;
3898   return memUsed;
3899 #else
3900   return 0;
3901 #endif
3902 }
3903 
GetLoadAverage()3904 double SystemInformationImplementation::GetLoadAverage()
3905 {
3906 #if defined(KWSYS_CXX_HAS_GETLOADAVG)
3907   double loadavg[3] = { 0.0, 0.0, 0.0 };
3908   if (getloadavg(loadavg, 3) > 0) {
3909     return loadavg[0];
3910   }
3911   return -0.0;
3912 #elif defined(KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes)
3913   // Old windows.h headers do not provide GetSystemTimes.
3914   typedef BOOL(WINAPI * GetSystemTimesType)(LPFILETIME, LPFILETIME,
3915                                             LPFILETIME);
3916   static GetSystemTimesType pGetSystemTimes =
3917     (GetSystemTimesType)GetProcAddress(GetModuleHandleW(L"kernel32"),
3918                                        "GetSystemTimes");
3919   FILETIME idleTime, kernelTime, userTime;
3920   if (pGetSystemTimes && pGetSystemTimes(&idleTime, &kernelTime, &userTime)) {
3921     unsigned __int64 const idleTicks = fileTimeToUInt64(idleTime);
3922     unsigned __int64 const totalTicks =
3923       fileTimeToUInt64(kernelTime) + fileTimeToUInt64(userTime);
3924     return calculateCPULoad(idleTicks, totalTicks) * GetNumberOfPhysicalCPU();
3925   }
3926   return -0.0;
3927 #else
3928   // Not implemented on this platform.
3929   return -0.0;
3930 #endif
3931 }
3932 
3933 /**
3934 Get the process id of the running process.
3935 */
GetProcessId()3936 long long SystemInformationImplementation::GetProcessId()
3937 {
3938 #if defined(_WIN32)
3939   return GetCurrentProcessId();
3940 #elif defined(__linux) || defined(__APPLE__) || defined(__OpenBSD__) ||       \
3941   defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) ||    \
3942   defined(__CYGWIN__)
3943   return getpid();
3944 #else
3945   return -1;
3946 #endif
3947 }
3948 
3949 /**
3950  * Used in GetProgramStack(...) below
3951  */
3952 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 && defined(_MSC_VER) &&   \
3953   _MSC_VER >= 1800
3954 #  define KWSYS_SYSTEMINFORMATION_HAS_DBGHELP
3955 #  define TRACE_MAX_STACK_FRAMES 1024
3956 #  define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
3957 #  pragma warning(push)
3958 #  pragma warning(disable : 4091) /* 'typedef ': ignored on left of '' */
3959 #  include "dbghelp.h"
3960 #  pragma warning(pop)
3961 #endif
3962 
3963 /**
3964 return current program stack in a string
3965 demangle cxx symbols if possible.
3966 */
GetProgramStack(int firstFrame,int wholePath)3967 std::string SystemInformationImplementation::GetProgramStack(int firstFrame,
3968                                                              int wholePath)
3969 {
3970   std::ostringstream oss;
3971   std::string programStack;
3972 
3973 #ifdef KWSYS_SYSTEMINFORMATION_HAS_DBGHELP
3974   (void)wholePath;
3975 
3976   void* stack[TRACE_MAX_STACK_FRAMES];
3977   HANDLE process = GetCurrentProcess();
3978   SymInitialize(process, nullptr, TRUE);
3979   WORD numberOfFrames =
3980     CaptureStackBackTrace(firstFrame, TRACE_MAX_STACK_FRAMES, stack, nullptr);
3981   SYMBOL_INFO* symbol = static_cast<SYMBOL_INFO*>(
3982     malloc(sizeof(SYMBOL_INFO) +
3983            (TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)));
3984   symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
3985   symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
3986   DWORD displacement;
3987   IMAGEHLP_LINE64 line;
3988   line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
3989   for (int i = 0; i < numberOfFrames; i++) {
3990     DWORD64 address = reinterpret_cast<DWORD64>(stack[i]);
3991     SymFromAddr(process, address, nullptr, symbol);
3992     if (SymGetLineFromAddr64(process, address, &displacement, &line)) {
3993       oss << " at " << symbol->Name << " in " << line.FileName << " line "
3994           << line.LineNumber << std::endl;
3995     } else {
3996       oss << " at " << symbol->Name << std::endl;
3997     }
3998   }
3999   free(symbol);
4000 
4001 #else
4002   programStack += ""
4003 #  if !defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE)
4004                   "WARNING: The stack could not be examined "
4005                   "because backtrace is not supported.\n"
4006 #  elif !defined(KWSYS_SYSTEMINFORMATION_HAS_DEBUG_BUILD)
4007                   "WARNING: The stack trace will not use advanced "
4008                   "capabilities because this is a release build.\n"
4009 #  else
4010 #    if !defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP)
4011                   "WARNING: Function names will not be demangled "
4012                   "because dladdr is not available.\n"
4013 #    endif
4014 #    if !defined(KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE)
4015                   "WARNING: Function names will not be demangled "
4016                   "because cxxabi is not available.\n"
4017 #    endif
4018 #  endif
4019     ;
4020 
4021 #  if defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE)
4022   void* stackSymbols[256];
4023   int nFrames = backtrace(stackSymbols, 256);
4024   for (int i = firstFrame; i < nFrames; ++i) {
4025     SymbolProperties symProps;
4026     symProps.SetReportPath(wholePath);
4027     symProps.Initialize(stackSymbols[i]);
4028     oss << symProps << std::endl;
4029   }
4030 #  else
4031   (void)firstFrame;
4032   (void)wholePath;
4033 #  endif
4034 #endif
4035 
4036   programStack += oss.str();
4037 
4038   return programStack;
4039 }
4040 
4041 /**
4042 when set print stack trace in response to common signals.
4043 */
SetStackTraceOnError(int enable)4044 void SystemInformationImplementation::SetStackTraceOnError(int enable)
4045 {
4046 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
4047   static int saOrigValid = 0;
4048   static struct sigaction saABRTOrig;
4049   static struct sigaction saSEGVOrig;
4050   static struct sigaction saTERMOrig;
4051   static struct sigaction saINTOrig;
4052   static struct sigaction saILLOrig;
4053   static struct sigaction saBUSOrig;
4054   static struct sigaction saFPEOrig;
4055 
4056   if (enable && !saOrigValid) {
4057     // save the current actions
4058     sigaction(SIGABRT, nullptr, &saABRTOrig);
4059     sigaction(SIGSEGV, nullptr, &saSEGVOrig);
4060     sigaction(SIGTERM, nullptr, &saTERMOrig);
4061     sigaction(SIGINT, nullptr, &saINTOrig);
4062     sigaction(SIGILL, nullptr, &saILLOrig);
4063     sigaction(SIGBUS, nullptr, &saBUSOrig);
4064     sigaction(SIGFPE, nullptr, &saFPEOrig);
4065 
4066     // enable read, disable write
4067     saOrigValid = 1;
4068 
4069     // install ours
4070     struct sigaction sa;
4071     sa.sa_sigaction = (SigAction)StacktraceSignalHandler;
4072     sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
4073 #  ifdef SA_RESTART
4074     sa.sa_flags |= SA_RESTART;
4075 #  endif
4076     sigemptyset(&sa.sa_mask);
4077 
4078     sigaction(SIGABRT, &sa, nullptr);
4079     sigaction(SIGSEGV, &sa, nullptr);
4080     sigaction(SIGTERM, &sa, nullptr);
4081     sigaction(SIGINT, &sa, nullptr);
4082     sigaction(SIGILL, &sa, nullptr);
4083     sigaction(SIGBUS, &sa, nullptr);
4084     sigaction(SIGFPE, &sa, nullptr);
4085   } else if (!enable && saOrigValid) {
4086     // restore previous actions
4087     sigaction(SIGABRT, &saABRTOrig, nullptr);
4088     sigaction(SIGSEGV, &saSEGVOrig, nullptr);
4089     sigaction(SIGTERM, &saTERMOrig, nullptr);
4090     sigaction(SIGINT, &saINTOrig, nullptr);
4091     sigaction(SIGILL, &saILLOrig, nullptr);
4092     sigaction(SIGBUS, &saBUSOrig, nullptr);
4093     sigaction(SIGFPE, &saFPEOrig, nullptr);
4094 
4095     // enable write, disable read
4096     saOrigValid = 0;
4097   }
4098 #else
4099   // avoid warning C4100
4100   (void)enable;
4101 #endif
4102 }
4103 
QueryWindowsMemory()4104 bool SystemInformationImplementation::QueryWindowsMemory()
4105 {
4106 #if defined(_WIN32)
4107 #  if defined(_MSC_VER) && _MSC_VER < 1300
4108   MEMORYSTATUS ms;
4109   unsigned long tv, tp, av, ap;
4110   ms.dwLength = sizeof(ms);
4111   GlobalMemoryStatus(&ms);
4112 #    define MEM_VAL(value) dw##value
4113 #  else
4114   MEMORYSTATUSEX ms;
4115   DWORDLONG tv, tp, av, ap;
4116   ms.dwLength = sizeof(ms);
4117   if (0 == GlobalMemoryStatusEx(&ms)) {
4118     return 0;
4119   }
4120 #    define MEM_VAL(value) ull##value
4121 #  endif
4122   tv = ms.MEM_VAL(TotalPageFile);
4123   tp = ms.MEM_VAL(TotalPhys);
4124   av = ms.MEM_VAL(AvailPageFile);
4125   ap = ms.MEM_VAL(AvailPhys);
4126   this->TotalVirtualMemory = tv >> 10 >> 10;
4127   this->TotalPhysicalMemory = tp >> 10 >> 10;
4128   this->AvailableVirtualMemory = av >> 10 >> 10;
4129   this->AvailablePhysicalMemory = ap >> 10 >> 10;
4130   return true;
4131 #else
4132   return false;
4133 #endif
4134 }
4135 
QueryLinuxMemory()4136 bool SystemInformationImplementation::QueryLinuxMemory()
4137 {
4138 #if defined(__linux)
4139   unsigned long tv = 0;
4140   unsigned long tp = 0;
4141   unsigned long av = 0;
4142   unsigned long ap = 0;
4143 
4144   char buffer[1024]; // for reading lines
4145 
4146   int linuxMajor = 0;
4147   int linuxMinor = 0;
4148 
4149   // Find the Linux kernel version first
4150   struct utsname unameInfo;
4151   int errorFlag = uname(&unameInfo);
4152   if (errorFlag != 0) {
4153     std::cout << "Problem calling uname(): " << strerror(errno) << std::endl;
4154     return false;
4155   }
4156 
4157   if (strlen(unameInfo.release) >= 3) {
4158     // release looks like "2.6.3-15mdk-i686-up-4GB"
4159     char majorChar = unameInfo.release[0];
4160     char minorChar = unameInfo.release[2];
4161 
4162     if (isdigit(majorChar)) {
4163       linuxMajor = majorChar - '0';
4164     }
4165 
4166     if (isdigit(minorChar)) {
4167       linuxMinor = minorChar - '0';
4168     }
4169   }
4170 
4171   FILE* fd = fopen("/proc/meminfo", "r");
4172   if (!fd) {
4173     std::cout << "Problem opening /proc/meminfo" << std::endl;
4174     return false;
4175   }
4176 
4177   if (linuxMajor >= 3 || ((linuxMajor >= 2) && (linuxMinor >= 6))) {
4178     // new /proc/meminfo format since kernel 2.6.x
4179     // Rigorously, this test should check from the developing version 2.5.x
4180     // that introduced the new format...
4181 
4182     enum
4183     {
4184       mMemTotal,
4185       mMemFree,
4186       mBuffers,
4187       mCached,
4188       mSwapTotal,
4189       mSwapFree
4190     };
4191     const char* format[6] = { "MemTotal:%lu kB",  "MemFree:%lu kB",
4192                               "Buffers:%lu kB",   "Cached:%lu kB",
4193                               "SwapTotal:%lu kB", "SwapFree:%lu kB" };
4194     bool have[6] = { false, false, false, false, false, false };
4195     unsigned long value[6];
4196     int count = 0;
4197     while (fgets(buffer, static_cast<int>(sizeof(buffer)), fd)) {
4198       for (int i = 0; i < 6; ++i) {
4199         if (!have[i] && sscanf(buffer, format[i], &value[i]) == 1) {
4200           have[i] = true;
4201           ++count;
4202         }
4203       }
4204     }
4205     if (count == 6) {
4206       this->TotalPhysicalMemory = value[mMemTotal] / 1024;
4207       this->AvailablePhysicalMemory =
4208         (value[mMemFree] + value[mBuffers] + value[mCached]) / 1024;
4209       this->TotalVirtualMemory = value[mSwapTotal] / 1024;
4210       this->AvailableVirtualMemory = value[mSwapFree] / 1024;
4211     } else {
4212       std::cout << "Problem parsing /proc/meminfo" << std::endl;
4213       fclose(fd);
4214       return false;
4215     }
4216   } else {
4217     // /proc/meminfo format for kernel older than 2.6.x
4218 
4219     unsigned long temp;
4220     unsigned long cachedMem;
4221     unsigned long buffersMem;
4222     // Skip "total: used:..."
4223     char* r = fgets(buffer, static_cast<int>(sizeof(buffer)), fd);
4224     int status = 0;
4225     if (r == buffer) {
4226       status += fscanf(fd, "Mem: %lu %lu %lu %lu %lu %lu\n", &tp, &temp, &ap,
4227                        &temp, &buffersMem, &cachedMem);
4228     }
4229     if (status == 6) {
4230       status += fscanf(fd, "Swap: %lu %lu %lu\n", &tv, &temp, &av);
4231     }
4232     if (status == 9) {
4233       this->TotalVirtualMemory = tv >> 10 >> 10;
4234       this->TotalPhysicalMemory = tp >> 10 >> 10;
4235       this->AvailableVirtualMemory = av >> 10 >> 10;
4236       this->AvailablePhysicalMemory =
4237         (ap + buffersMem + cachedMem) >> 10 >> 10;
4238     } else {
4239       std::cout << "Problem parsing /proc/meminfo" << std::endl;
4240       fclose(fd);
4241       return false;
4242     }
4243   }
4244   fclose(fd);
4245 
4246   return true;
4247 #else
4248   return false;
4249 #endif
4250 }
4251 
QueryCygwinMemory()4252 bool SystemInformationImplementation::QueryCygwinMemory()
4253 {
4254 #ifdef __CYGWIN__
4255   // _SC_PAGE_SIZE does return the mmap() granularity on Cygwin,
4256   // see http://cygwin.com/ml/cygwin/2006-06/msg00350.html
4257   // Therefore just use 4096 as the page size of Windows.
4258   long m = sysconf(_SC_PHYS_PAGES);
4259   if (m < 0) {
4260     return false;
4261   }
4262   this->TotalPhysicalMemory = m >> 8;
4263   return true;
4264 #else
4265   return false;
4266 #endif
4267 }
4268 
QueryAIXMemory()4269 bool SystemInformationImplementation::QueryAIXMemory()
4270 {
4271 #if defined(_AIX) && defined(_SC_AIX_REALMEM)
4272   long c = sysconf(_SC_AIX_REALMEM);
4273   if (c <= 0) {
4274     return false;
4275   }
4276 
4277   this->TotalPhysicalMemory = c / 1024;
4278 
4279   return true;
4280 #else
4281   return false;
4282 #endif
4283 }
4284 
QueryMemoryBySysconf()4285 bool SystemInformationImplementation::QueryMemoryBySysconf()
4286 {
4287 #if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
4288   // Assume the mmap() granularity as returned by _SC_PAGESIZE is also
4289   // the system page size. The only known system where this isn't true
4290   // is Cygwin.
4291   long p = sysconf(_SC_PHYS_PAGES);
4292   long m = sysconf(_SC_PAGESIZE);
4293 
4294   if (p < 0 || m < 0) {
4295     return false;
4296   }
4297 
4298   // assume pagesize is a power of 2 and smaller 1 MiB
4299   size_t pagediv = (1024 * 1024 / m);
4300 
4301   this->TotalPhysicalMemory = p;
4302   this->TotalPhysicalMemory /= pagediv;
4303 
4304 #  if defined(_SC_AVPHYS_PAGES)
4305   p = sysconf(_SC_AVPHYS_PAGES);
4306   if (p < 0) {
4307     return false;
4308   }
4309 
4310   this->AvailablePhysicalMemory = p;
4311   this->AvailablePhysicalMemory /= pagediv;
4312 #  endif
4313 
4314   return true;
4315 #else
4316   return false;
4317 #endif
4318 }
4319 
4320 /** Query for the memory status */
QueryMemory()4321 bool SystemInformationImplementation::QueryMemory()
4322 {
4323   return this->QueryMemoryBySysconf();
4324 }
4325 
4326 /** */
GetTotalVirtualMemory() const4327 size_t SystemInformationImplementation::GetTotalVirtualMemory() const
4328 {
4329   return this->TotalVirtualMemory;
4330 }
4331 
4332 /** */
GetAvailableVirtualMemory() const4333 size_t SystemInformationImplementation::GetAvailableVirtualMemory() const
4334 {
4335   return this->AvailableVirtualMemory;
4336 }
4337 
GetTotalPhysicalMemory() const4338 size_t SystemInformationImplementation::GetTotalPhysicalMemory() const
4339 {
4340   return this->TotalPhysicalMemory;
4341 }
4342 
4343 /** */
GetAvailablePhysicalMemory() const4344 size_t SystemInformationImplementation::GetAvailablePhysicalMemory() const
4345 {
4346   return this->AvailablePhysicalMemory;
4347 }
4348 
4349 /** Get Cycle differences */
GetCyclesDifference(DELAY_FUNC DelayFunction,unsigned int uiParameter)4350 long long SystemInformationImplementation::GetCyclesDifference(
4351   DELAY_FUNC DelayFunction, unsigned int uiParameter)
4352 {
4353 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
4354   unsigned __int64 stamp1, stamp2;
4355 
4356 #  ifdef _M_ARM64
4357   stamp1 = _ReadStatusReg(ARM64_PMCCNTR_EL0);
4358   DelayFunction(uiParameter);
4359   stamp2 = _ReadStatusReg(ARM64_PMCCNTR_EL0);
4360 #  else
4361   stamp1 = __rdtsc();
4362   DelayFunction(uiParameter);
4363   stamp2 = __rdtsc();
4364 #  endif
4365 
4366   return stamp2 - stamp1;
4367 #elif USE_ASM_INSTRUCTIONS
4368 
4369   unsigned int edx1, eax1;
4370   unsigned int edx2, eax2;
4371 
4372   // Calculate the frequency of the CPU instructions.
4373   __try {
4374     _asm {
4375       push uiParameter ; push parameter param
4376       mov ebx, DelayFunction ; store func in ebx
4377 
4378       RDTSC_INSTRUCTION
4379 
4380       mov esi, eax ; esi = eax
4381       mov edi, edx ; edi = edx
4382 
4383       call ebx ; call the delay functions
4384 
4385       RDTSC_INSTRUCTION
4386 
4387       pop ebx
4388 
4389       mov edx2, edx      ; edx2 = edx
4390       mov eax2, eax      ; eax2 = eax
4391 
4392       mov edx1, edi      ; edx2 = edi
4393       mov eax1, esi      ; eax2 = esi
4394     }
4395   } __except (1) {
4396     return -1;
4397   }
4398 
4399   return ((((__int64)edx2 << 32) + eax2) - (((__int64)edx1 << 32) + eax1));
4400 
4401 #else
4402   (void)DelayFunction;
4403   (void)uiParameter;
4404   return -1;
4405 #endif
4406 }
4407 
4408 /** Compute the delay overhead */
DelayOverhead(unsigned int uiMS)4409 void SystemInformationImplementation::DelayOverhead(unsigned int uiMS)
4410 {
4411 #if defined(_WIN32)
4412   LARGE_INTEGER Frequency, StartCounter, EndCounter;
4413   __int64 x;
4414 
4415   // Get the frequency of the high performance counter.
4416   if (!QueryPerformanceFrequency(&Frequency)) {
4417     return;
4418   }
4419   x = Frequency.QuadPart / 1000 * uiMS;
4420 
4421   // Get the starting position of the counter.
4422   QueryPerformanceCounter(&StartCounter);
4423 
4424   do {
4425     // Get the ending position of the counter.
4426     QueryPerformanceCounter(&EndCounter);
4427   } while (EndCounter.QuadPart - StartCounter.QuadPart == x);
4428 #endif
4429   (void)uiMS;
4430 }
4431 
4432 /** Works only for windows */
IsSMTSupported() const4433 bool SystemInformationImplementation::IsSMTSupported() const
4434 {
4435   return this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical > 1;
4436 }
4437 
4438 /** Return the APIC Id. Works only for windows. */
GetAPICId()4439 unsigned char SystemInformationImplementation::GetAPICId()
4440 {
4441   int Regs[4] = { 0, 0, 0, 0 };
4442 
4443 #if USE_CPUID
4444   if (!this->IsSMTSupported()) {
4445     return static_cast<unsigned char>(-1); // HT not supported
4446   }                                        // Logical processor = 1
4447   call_cpuid(1, Regs);
4448 #endif
4449 
4450   return static_cast<unsigned char>((Regs[1] & INITIAL_APIC_ID_BITS) >> 24);
4451 }
4452 
4453 /** Count the number of CPUs. Works only on windows. */
CPUCountWindows()4454 void SystemInformationImplementation::CPUCountWindows()
4455 {
4456 #if defined(_WIN32)
4457   this->NumberOfPhysicalCPU = 0;
4458   this->NumberOfLogicalCPU = 0;
4459 
4460   typedef BOOL(WINAPI * GetLogicalProcessorInformationType)(
4461     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
4462   static GetLogicalProcessorInformationType pGetLogicalProcessorInformation =
4463     (GetLogicalProcessorInformationType)GetProcAddress(
4464       GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation");
4465 
4466   if (!pGetLogicalProcessorInformation) {
4467     // Fallback to approximate implementation on ancient Windows versions.
4468     SYSTEM_INFO info;
4469     ZeroMemory(&info, sizeof(info));
4470     GetSystemInfo(&info);
4471     this->NumberOfPhysicalCPU =
4472       static_cast<unsigned int>(info.dwNumberOfProcessors);
4473     this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU;
4474     return;
4475   }
4476 
4477   std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> ProcInfo;
4478   {
4479     DWORD Length = 0;
4480     DWORD rc = pGetLogicalProcessorInformation(nullptr, &Length);
4481     assert(FALSE == rc);
4482     (void)rc; // Silence unused variable warning
4483     assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
4484     ProcInfo.resize(Length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
4485     rc = pGetLogicalProcessorInformation(&ProcInfo[0], &Length);
4486     assert(rc != FALSE);
4487     (void)rc; // Silence unused variable warning
4488   }
4489 
4490   typedef std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>::iterator
4491     pinfoIt_t;
4492   for (pinfoIt_t it = ProcInfo.begin(); it != ProcInfo.end(); ++it) {
4493     SYSTEM_LOGICAL_PROCESSOR_INFORMATION PInfo = *it;
4494     if (PInfo.Relationship != RelationProcessorCore) {
4495       continue;
4496     }
4497 
4498     std::bitset<std::numeric_limits<ULONG_PTR>::digits> ProcMask(
4499       (unsigned long long)PInfo.ProcessorMask);
4500     unsigned int count = (unsigned int)ProcMask.count();
4501     if (count == 0) { // I think this should never happen, but just to be safe.
4502       continue;
4503     }
4504     this->NumberOfPhysicalCPU++;
4505     this->NumberOfLogicalCPU += (unsigned int)count;
4506     this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = count;
4507   }
4508   this->NumberOfPhysicalCPU = std::max(1u, this->NumberOfPhysicalCPU);
4509   this->NumberOfLogicalCPU = std::max(1u, this->NumberOfLogicalCPU);
4510 #else
4511 #endif
4512 }
4513 
4514 /** Return the number of logical CPUs on the system */
GetNumberOfLogicalCPU() const4515 unsigned int SystemInformationImplementation::GetNumberOfLogicalCPU() const
4516 {
4517   return this->NumberOfLogicalCPU;
4518 }
4519 
4520 /** Return the number of physical CPUs on the system */
GetNumberOfPhysicalCPU() const4521 unsigned int SystemInformationImplementation::GetNumberOfPhysicalCPU() const
4522 {
4523   return this->NumberOfPhysicalCPU;
4524 }
4525 
4526 #if defined(__APPLE__)
kw_sysctlbyname_int32(const char * name,int32_t * value)4527 static int kw_sysctlbyname_int32(const char* name, int32_t* value)
4528 {
4529   size_t len = sizeof(int32_t);
4530   int err = sysctlbyname(name, value, &len, nullptr, 0);
4531   if (err == 0) {
4532     assert(len == sizeof(int32_t));
4533   }
4534   return err;
4535 }
4536 
kw_sysctlbyname_int64(const char * name,int64_t * value)4537 static int kw_sysctlbyname_int64(const char* name, int64_t* value)
4538 {
4539   size_t len = sizeof(int64_t);
4540   int err = sysctlbyname(name, value, &len, nullptr, 0);
4541   if (err == 0) {
4542     assert(len == sizeof(int64_t));
4543   }
4544   return err;
4545 }
4546 #endif
4547 
4548 /** For Apple use sysctlbyname calls to find system info */
ParseSysCtl()4549 bool SystemInformationImplementation::ParseSysCtl()
4550 {
4551 #if defined(__APPLE__)
4552   char tempBuff[128];
4553   int32_t tempInt32 = 0;
4554   int64_t tempInt64 = 0;
4555   int err = 0;
4556   size_t len;
4557 
4558   this->TotalPhysicalMemory = 0;
4559   err = kw_sysctlbyname_int64("hw.memsize", &tempInt64);
4560   if (err == 0) {
4561     this->TotalPhysicalMemory = static_cast<size_t>(tempInt64 / 1024 / 1024);
4562   }
4563 
4564   this->AvailablePhysicalMemory = 0;
4565   vm_statistics_data_t vmstat;
4566   mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
4567   if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat,
4568                       &count) == KERN_SUCCESS) {
4569     err = kw_sysctlbyname_int64("hw.pagesize", &tempInt64);
4570     if (err == 0) {
4571       int64_t available_memory =
4572         (vmstat.free_count + vmstat.inactive_count) * tempInt64;
4573       this->AvailablePhysicalMemory =
4574         static_cast<size_t>(available_memory / 1024 / 1024);
4575     }
4576   }
4577 
4578   // Virtual memory.
4579   this->AvailableVirtualMemory = 0;
4580   this->TotalVirtualMemory = 0;
4581 #  ifdef VM_SWAPUSAGE
4582   int mib[2] = { CTL_VM, VM_SWAPUSAGE };
4583   unsigned int miblen =
4584     static_cast<unsigned int>(sizeof(mib) / sizeof(mib[0]));
4585   struct xsw_usage swap;
4586   len = sizeof(swap);
4587   err = sysctl(mib, miblen, &swap, &len, nullptr, 0);
4588   if (err == 0) {
4589     this->AvailableVirtualMemory =
4590       static_cast<size_t>(swap.xsu_avail / 1024 / 1024);
4591     this->TotalVirtualMemory =
4592       static_cast<size_t>(swap.xsu_total / 1024 / 1024);
4593   }
4594 #  endif
4595 
4596   // CPU Info
4597   this->NumberOfPhysicalCPU = 1;
4598   err = kw_sysctlbyname_int32("hw.physicalcpu", &tempInt32);
4599   if (err == 0) {
4600     this->NumberOfPhysicalCPU = tempInt32;
4601   }
4602 
4603   this->NumberOfLogicalCPU = 1;
4604   err = kw_sysctlbyname_int32("hw.logicalcpu", &tempInt32);
4605   if (err == 0) {
4606     this->NumberOfLogicalCPU = tempInt32;
4607   }
4608 
4609   this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = 1;
4610   err = kw_sysctlbyname_int32("machdep.cpu.cores_per_package", &tempInt32);
4611   if (err == 0) {
4612     this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = tempInt32;
4613   }
4614 
4615   this->CPUSpeedInMHz = 0;
4616   err = kw_sysctlbyname_int64("hw.cpufrequency", &tempInt64);
4617   if (err == 0) {
4618     this->CPUSpeedInMHz = static_cast<float>(tempInt64) / 1000000.0f;
4619   }
4620 
4621   // Chip family
4622   // Seems only the Intel chips will have this name so if this fails it is
4623   // a PowerPC or ARM, or something unknown
4624   this->ChipID.Vendor = "";
4625   this->ChipID.Family = 0;
4626   this->ChipID.Model = 0;
4627   this->ChipID.Revision = 0;
4628   err = kw_sysctlbyname_int32("machdep.cpu.family", &tempInt32);
4629   if (err != 0) // Go back to names we know but are less descriptive
4630   {
4631     ::memset(tempBuff, 0, sizeof(tempBuff));
4632     len = sizeof(tempBuff) - 1; // leave a byte for null termination
4633     err = sysctlbyname("hw.machine", &tempBuff, &len, nullptr, 0);
4634     if (err == 0) {
4635       std::string machineBuf(tempBuff);
4636       if (machineBuf.find_first_of("Power") != std::string::npos) {
4637         this->ChipID.Vendor = "IBM";
4638 
4639         err = kw_sysctlbyname_int32("hw.cputype", &tempInt32);
4640         if (err == 0) {
4641           this->ChipID.Family = tempInt32;
4642         }
4643 
4644         err = kw_sysctlbyname_int32("hw.cpusubtype", &tempInt32);
4645         if (err == 0) {
4646           this->ChipID.Model = tempInt32;
4647         }
4648 
4649         this->FindManufacturer();
4650       } else if (machineBuf.find_first_of("arm64") != std::string::npos) {
4651         this->ChipID.Vendor = "Apple";
4652 
4653         this->FindManufacturer();
4654       }
4655     }
4656   } else {
4657     // Should be an Intel Chip.
4658     err = kw_sysctlbyname_int32("machdep.cpu.family", &tempInt32);
4659     if (err == 0) {
4660       this->ChipID.Family = tempInt32;
4661     }
4662 
4663     // Chip Vendor
4664     ::memset(tempBuff, 0, sizeof(tempBuff));
4665     len = sizeof(tempBuff) - 1; // leave a byte for null termination
4666     err = sysctlbyname("machdep.cpu.vendor", tempBuff, &len, nullptr, 0);
4667     if (err == 0) {
4668       this->ChipID.Vendor = tempBuff;
4669     }
4670     this->FindManufacturer();
4671 
4672     // Chip Model
4673     err = kw_sysctlbyname_int32("machdep.cpu.model", &tempInt32);
4674     if (err == 0) {
4675       this->ChipID.Model = tempInt32;
4676     }
4677 
4678     // Chip Stepping
4679     err = kw_sysctlbyname_int32("machdep.cpu.stepping", &tempInt32);
4680     if (err == 0) {
4681       this->ChipID.Revision = tempInt32;
4682     }
4683 
4684     // feature string
4685     char* buf = nullptr;
4686     size_t allocSize = 128;
4687 
4688     err = 0;
4689     len = 0;
4690 
4691     // sysctlbyname() will return with err==0 && len==0 if the buffer is too
4692     // small
4693     while (err == 0 && len == 0) {
4694       delete[] buf;
4695       allocSize *= 2;
4696       buf = new char[allocSize];
4697       if (!buf) {
4698         break;
4699       }
4700       buf[0] = ' ';
4701       len = allocSize - 2; // keep space for leading and trailing space
4702       err = sysctlbyname("machdep.cpu.features", buf + 1, &len, nullptr, 0);
4703     }
4704     if (err == 0 && buf && len) {
4705       // now we can match every flags as space + flag + space
4706       buf[len + 1] = ' ';
4707       std::string cpuflags(buf, len + 2);
4708 
4709       if (cpuflags.find(" FPU ") != std::string::npos) {
4710         this->Features.HasFPU = true;
4711       }
4712       if (cpuflags.find(" TSC ") != std::string::npos) {
4713         this->Features.HasTSC = true;
4714       }
4715       if (cpuflags.find(" MMX ") != std::string::npos) {
4716         this->Features.HasMMX = true;
4717       }
4718       if (cpuflags.find(" SSE ") != std::string::npos) {
4719         this->Features.HasSSE = true;
4720       }
4721       if (cpuflags.find(" SSE2 ") != std::string::npos) {
4722         this->Features.HasSSE2 = true;
4723       }
4724       if (cpuflags.find(" APIC ") != std::string::npos) {
4725         this->Features.HasAPIC = true;
4726       }
4727       if (cpuflags.find(" CMOV ") != std::string::npos) {
4728         this->Features.HasCMOV = true;
4729       }
4730       if (cpuflags.find(" MTRR ") != std::string::npos) {
4731         this->Features.HasMTRR = true;
4732       }
4733       if (cpuflags.find(" ACPI ") != std::string::npos) {
4734         this->Features.HasACPI = true;
4735       }
4736     }
4737     delete[] buf;
4738   }
4739 
4740   // brand string
4741   this->ChipID.ProcessorName = "";
4742   this->ChipID.ModelName = "";
4743   ::memset(tempBuff, 0, sizeof(tempBuff));
4744   len = sizeof(tempBuff) - 1; // leave a byte for null termination
4745   err = sysctlbyname("machdep.cpu.brand_string", tempBuff, &len, nullptr, 0);
4746   if (err == 0) {
4747     this->ChipID.ProcessorName = tempBuff;
4748     this->ChipID.ModelName = tempBuff;
4749   }
4750 
4751   // L1 Cache size
4752   this->Features.L1CacheSize = 0;
4753   err = kw_sysctlbyname_int64("hw.l1icachesize", &tempInt64);
4754   if (err == 0) {
4755     this->Features.L1CacheSize = static_cast<int>(tempInt64);
4756   }
4757 
4758   // L2 Cache size
4759   this->Features.L2CacheSize = 0;
4760   err = kw_sysctlbyname_int64("hw.l2cachesize", &tempInt64);
4761   if (err == 0) {
4762     this->Features.L2CacheSize = static_cast<int>(tempInt64);
4763   }
4764 
4765   return true;
4766 #else
4767   return false;
4768 #endif
4769 }
4770 
4771 /** Extract a value from sysctl command */
ExtractValueFromSysCtl(const char * word)4772 std::string SystemInformationImplementation::ExtractValueFromSysCtl(
4773   const char* word)
4774 {
4775   size_t pos = this->SysCtlBuffer.find(word);
4776   if (pos != std::string::npos) {
4777     pos = this->SysCtlBuffer.find(": ", pos);
4778     size_t pos2 = this->SysCtlBuffer.find('\n', pos);
4779     if (pos != std::string::npos && pos2 != std::string::npos) {
4780       return this->SysCtlBuffer.substr(pos + 2, pos2 - pos - 2);
4781     }
4782   }
4783   return "";
4784 }
4785 
4786 /** Run a given process */
RunProcess(std::vector<const char * > args)4787 std::string SystemInformationImplementation::RunProcess(
4788   std::vector<const char*> args)
4789 {
4790   std::string buffer;
4791 
4792   // Run the application
4793   kwsysProcess* gp = kwsysProcess_New();
4794   kwsysProcess_SetCommand(gp, args.data());
4795   kwsysProcess_SetOption(gp, kwsysProcess_Option_HideWindow, 1);
4796 
4797   kwsysProcess_Execute(gp);
4798 
4799   char* data = nullptr;
4800   int length;
4801   double timeout = 255;
4802   int pipe; // pipe id as returned by kwsysProcess_WaitForData()
4803 
4804   while ((static_cast<void>(
4805             pipe = kwsysProcess_WaitForData(gp, &data, &length, &timeout)),
4806           (pipe == kwsysProcess_Pipe_STDOUT ||
4807            pipe == kwsysProcess_Pipe_STDERR))) // wait for 1s
4808   {
4809     buffer.append(data, length);
4810   }
4811   kwsysProcess_WaitForExit(gp, nullptr);
4812 
4813   int result = 0;
4814   switch (kwsysProcess_GetState(gp)) {
4815     case kwsysProcess_State_Exited: {
4816       result = kwsysProcess_GetExitValue(gp);
4817     } break;
4818     case kwsysProcess_State_Error: {
4819       std::cerr << "Error: Could not run " << args[0] << ":\n";
4820       std::cerr << kwsysProcess_GetErrorString(gp) << "\n";
4821     } break;
4822     case kwsysProcess_State_Exception: {
4823       std::cerr << "Error: " << args[0] << " terminated with an exception: "
4824                 << kwsysProcess_GetExceptionString(gp) << "\n";
4825     } break;
4826     case kwsysProcess_State_Starting:
4827     case kwsysProcess_State_Executing:
4828     case kwsysProcess_State_Expired:
4829     case kwsysProcess_State_Killed: {
4830       // Should not get here.
4831       std::cerr << "Unexpected ending state after running " << args[0]
4832                 << std::endl;
4833     } break;
4834   }
4835   kwsysProcess_Delete(gp);
4836   if (result) {
4837     std::cerr << "Error " << args[0] << " returned :" << result << "\n";
4838   }
4839   return buffer;
4840 }
4841 
ParseValueFromKStat(const char * arguments)4842 std::string SystemInformationImplementation::ParseValueFromKStat(
4843   const char* arguments)
4844 {
4845   std::vector<std::string> args_string;
4846   std::string command = arguments;
4847   size_t start = std::string::npos;
4848   size_t pos = command.find(' ', 0);
4849   while (pos != std::string::npos) {
4850     bool inQuotes = false;
4851     // Check if we are between quotes
4852     size_t b0 = command.find('"', 0);
4853     size_t b1 = command.find('"', b0 + 1);
4854     while (b0 != std::string::npos && b1 != std::string::npos && b1 > b0) {
4855       if (pos > b0 && pos < b1) {
4856         inQuotes = true;
4857         break;
4858       }
4859       b0 = command.find('"', b1 + 1);
4860       b1 = command.find('"', b0 + 1);
4861     }
4862 
4863     if (!inQuotes) {
4864       args_string.push_back(command.substr(start + 1, pos - start - 1));
4865       std::string& arg = args_string.back();
4866 
4867       // Remove the quotes if any
4868       arg.erase(std::remove(arg.begin(), arg.end(), '"'), arg.end());
4869       start = pos;
4870     }
4871     pos = command.find(' ', pos + 1);
4872   }
4873   command.erase(0, start + 1);
4874   args_string.push_back(command);
4875 
4876   std::vector<const char*> args;
4877   args.reserve(3 + args_string.size());
4878   args.push_back("kstat");
4879   args.push_back("-p");
4880   for (auto& i : args_string) {
4881     args.push_back(i.c_str());
4882   }
4883   args.push_back(nullptr);
4884 
4885   std::string buffer = this->RunProcess(args);
4886 
4887   std::string value;
4888   for (size_t i = buffer.size() - 1; i > 0; i--) {
4889     if (buffer[i] == ' ' || buffer[i] == '\t') {
4890       break;
4891     }
4892     if (buffer[i] != '\n' && buffer[i] != '\r') {
4893       value.insert(0u, 1, buffer[i]);
4894     }
4895   }
4896   return value;
4897 }
4898 
4899 /** Querying for system information from Solaris */
QuerySolarisMemory()4900 bool SystemInformationImplementation::QuerySolarisMemory()
4901 {
4902 #if defined(__SVR4) && defined(__sun)
4903 // Solaris allows querying this value by sysconf, but if this is
4904 // a 32 bit process on a 64 bit host the returned memory will be
4905 // limited to 4GiB. So if this is a 32 bit process or if the sysconf
4906 // method fails use the kstat interface.
4907 #  if SIZEOF_VOID_P == 8
4908   if (this->QueryMemoryBySysconf()) {
4909     return true;
4910   }
4911 #  endif
4912 
4913   char* tail;
4914   unsigned long totalMemory =
4915     strtoul(this->ParseValueFromKStat("-s physmem").c_str(), &tail, 0);
4916   this->TotalPhysicalMemory = totalMemory / 128;
4917 
4918   return true;
4919 #else
4920   return false;
4921 #endif
4922 }
4923 
QuerySolarisProcessor()4924 bool SystemInformationImplementation::QuerySolarisProcessor()
4925 {
4926   if (!this->QueryProcessorBySysconf()) {
4927     return false;
4928   }
4929 
4930   // Parse values
4931   this->CPUSpeedInMHz = static_cast<float>(
4932     atoi(this->ParseValueFromKStat("-s clock_MHz").c_str()));
4933 
4934   // Chip family
4935   this->ChipID.Family = 0;
4936 
4937   // Chip Model
4938   this->ChipID.ProcessorName = this->ParseValueFromKStat("-s cpu_type");
4939   this->ChipID.Model = 0;
4940 
4941   // Chip Vendor
4942   if (this->ChipID.ProcessorName != "i386") {
4943     this->ChipID.Vendor = "Sun";
4944     this->FindManufacturer();
4945   }
4946 
4947   return true;
4948 }
4949 
4950 /** Querying for system information from Haiku OS */
QueryHaikuInfo()4951 bool SystemInformationImplementation::QueryHaikuInfo()
4952 {
4953 #if defined(__HAIKU__)
4954 
4955   // CPU count
4956   system_info info;
4957   get_system_info(&info);
4958   this->NumberOfPhysicalCPU = info.cpu_count;
4959 
4960   // CPU speed
4961   uint32 topologyNodeCount = 0;
4962   cpu_topology_node_info* topology = 0;
4963   get_cpu_topology_info(0, &topologyNodeCount);
4964   if (topologyNodeCount != 0)
4965     topology = new cpu_topology_node_info[topologyNodeCount];
4966   get_cpu_topology_info(topology, &topologyNodeCount);
4967 
4968   for (uint32 i = 0; i < topologyNodeCount; i++) {
4969     if (topology[i].type == B_TOPOLOGY_CORE) {
4970       this->CPUSpeedInMHz =
4971         topology[i].data.core.default_frequency / 1000000.0f;
4972       break;
4973     }
4974   }
4975 
4976   delete[] topology;
4977 
4978   // Physical Memory
4979   this->TotalPhysicalMemory = (info.max_pages * B_PAGE_SIZE) / (1024 * 1024);
4980   this->AvailablePhysicalMemory = this->TotalPhysicalMemory -
4981     ((info.used_pages * B_PAGE_SIZE) / (1024 * 1024));
4982 
4983   // NOTE: get_system_info_etc is currently a private call so just set to 0
4984   // until it becomes public
4985   this->TotalVirtualMemory = 0;
4986   this->AvailableVirtualMemory = 0;
4987 
4988   // Retrieve cpuid_info union for cpu 0
4989   cpuid_info cpu_info;
4990   get_cpuid(&cpu_info, 0, 0);
4991 
4992   // Chip Vendor
4993   // Use a temporary buffer so that we can add NULL termination to the string
4994   char vbuf[13];
4995   strncpy(vbuf, cpu_info.eax_0.vendor_id, 12);
4996   vbuf[12] = '\0';
4997   this->ChipID.Vendor = vbuf;
4998 
4999   this->FindManufacturer();
5000 
5001   // Retrieve cpuid_info union for cpu 0 this time using a register value of 1
5002   get_cpuid(&cpu_info, 1, 0);
5003 
5004   this->NumberOfLogicalCPU = cpu_info.eax_1.logical_cpus;
5005 
5006   // Chip type
5007   this->ChipID.Type = cpu_info.eax_1.type;
5008 
5009   // Chip family
5010   this->ChipID.Family = cpu_info.eax_1.family;
5011 
5012   // Chip Model
5013   this->ChipID.Model = cpu_info.eax_1.model;
5014 
5015   // Chip Revision
5016   this->ChipID.Revision = cpu_info.eax_1.stepping;
5017 
5018   // Chip Extended Family
5019   this->ChipID.ExtendedFamily = cpu_info.eax_1.extended_family;
5020 
5021   // Chip Extended Model
5022   this->ChipID.ExtendedModel = cpu_info.eax_1.extended_model;
5023 
5024   // Get ChipID.ProcessorName from other information already gathered
5025   this->RetrieveClassicalCPUIdentity();
5026 
5027   // Cache size
5028   this->Features.L1CacheSize = 0;
5029   this->Features.L2CacheSize = 0;
5030 
5031   return true;
5032 
5033 #else
5034   return false;
5035 #endif
5036 }
5037 
QueryQNXMemory()5038 bool SystemInformationImplementation::QueryQNXMemory()
5039 {
5040 #if defined(__QNX__)
5041   std::string buffer;
5042   std::vector<const char*> args;
5043   args.clear();
5044 
5045   args.push_back("showmem");
5046   args.push_back("-S");
5047   args.push_back(0);
5048   buffer = this->RunProcess(args);
5049   args.clear();
5050 
5051   size_t pos = buffer.find("System RAM:");
5052   if (pos == std::string::npos)
5053     return false;
5054   pos = buffer.find(":", pos);
5055   size_t pos2 = buffer.find("M (", pos);
5056   if (pos2 == std::string::npos)
5057     return false;
5058 
5059   pos++;
5060   while (buffer[pos] == ' ')
5061     pos++;
5062 
5063   buffer.erase(0, pos);
5064   buffer.resize(pos2);
5065   this->TotalPhysicalMemory = atoi(buffer.c_str());
5066   return true;
5067 #endif
5068   return false;
5069 }
5070 
QueryBSDMemory()5071 bool SystemInformationImplementation::QueryBSDMemory()
5072 {
5073 #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) ||    \
5074   defined(__DragonFly__)
5075   int ctrl[2] = { CTL_HW, HW_PHYSMEM };
5076 #  if defined(HW_PHYSMEM64)
5077   int64_t k;
5078   ctrl[1] = HW_PHYSMEM64;
5079 #  else
5080   int k;
5081 #  endif
5082   size_t sz = sizeof(k);
5083 
5084   if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
5085     return false;
5086   }
5087 
5088   this->TotalPhysicalMemory = k >> 10 >> 10;
5089 
5090   return true;
5091 #else
5092   return false;
5093 #endif
5094 }
5095 
QueryQNXProcessor()5096 bool SystemInformationImplementation::QueryQNXProcessor()
5097 {
5098 #if defined(__QNX__)
5099   // the output on my QNX 6.4.1 looks like this:
5100   // Processor1: 686 Pentium II Stepping 3 2175MHz FPU
5101   std::string buffer;
5102   std::vector<const char*> args;
5103   args.clear();
5104 
5105   args.push_back("pidin");
5106   args.push_back("info");
5107   args.push_back(0);
5108   buffer = this->RunProcess(args);
5109   args.clear();
5110 
5111   size_t pos = buffer.find("Processor1:");
5112   if (pos == std::string::npos)
5113     return false;
5114 
5115   size_t pos2 = buffer.find("MHz", pos);
5116   if (pos2 == std::string::npos)
5117     return false;
5118 
5119   size_t pos3 = pos2;
5120   while (buffer[pos3] != ' ')
5121     --pos3;
5122 
5123   this->CPUSpeedInMHz = atoi(buffer.substr(pos3 + 1, pos2 - pos3 - 1).c_str());
5124 
5125   pos2 = buffer.find(" Stepping", pos);
5126   if (pos2 != std::string::npos) {
5127     pos2 = buffer.find(" ", pos2 + 1);
5128     if (pos2 != std::string::npos && pos2 < pos3) {
5129       this->ChipID.Revision =
5130         atoi(buffer.substr(pos2 + 1, pos3 - pos2).c_str());
5131     }
5132   }
5133 
5134   this->NumberOfPhysicalCPU = 0;
5135   do {
5136     pos = buffer.find("\nProcessor", pos + 1);
5137     ++this->NumberOfPhysicalCPU;
5138   } while (pos != std::string::npos);
5139   this->NumberOfLogicalCPU = 1;
5140 
5141   return true;
5142 #else
5143   return false;
5144 #endif
5145 }
5146 
QueryBSDProcessor()5147 bool SystemInformationImplementation::QueryBSDProcessor()
5148 {
5149 #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) ||    \
5150   defined(__DragonFly__)
5151   int k;
5152   size_t sz = sizeof(k);
5153   int ctrl[2] = { CTL_HW, HW_NCPU };
5154 
5155   if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
5156     return false;
5157   }
5158 
5159   this->NumberOfPhysicalCPU = k;
5160   this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU;
5161 
5162 #  if defined(HW_CPUSPEED)
5163   ctrl[1] = HW_CPUSPEED;
5164 
5165   if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
5166     return false;
5167   }
5168 
5169   this->CPUSpeedInMHz = (float)k;
5170 #  endif
5171 
5172 #  if defined(CPU_SSE)
5173   ctrl[0] = CTL_MACHDEP;
5174   ctrl[1] = CPU_SSE;
5175 
5176   if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
5177     return false;
5178   }
5179 
5180   this->Features.HasSSE = (k > 0);
5181 #  endif
5182 
5183 #  if defined(CPU_SSE2)
5184   ctrl[0] = CTL_MACHDEP;
5185   ctrl[1] = CPU_SSE2;
5186 
5187   if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
5188     return false;
5189   }
5190 
5191   this->Features.HasSSE2 = (k > 0);
5192 #  endif
5193 
5194 #  if defined(CPU_CPUVENDOR)
5195   ctrl[0] = CTL_MACHDEP;
5196   ctrl[1] = CPU_CPUVENDOR;
5197   char vbuf[25];
5198   ::memset(vbuf, 0, sizeof(vbuf));
5199   sz = sizeof(vbuf) - 1;
5200   if (sysctl(ctrl, 2, vbuf, &sz, nullptr, 0) != 0) {
5201     return false;
5202   }
5203 
5204   this->ChipID.Vendor = vbuf;
5205   this->FindManufacturer();
5206 #  endif
5207 
5208   return true;
5209 #else
5210   return false;
5211 #endif
5212 }
5213 
QueryHPUXMemory()5214 bool SystemInformationImplementation::QueryHPUXMemory()
5215 {
5216 #if defined(__hpux)
5217   unsigned long tv = 0;
5218   unsigned long tp = 0;
5219   unsigned long av = 0;
5220   unsigned long ap = 0;
5221   struct pst_static pst;
5222   struct pst_dynamic pdy;
5223 
5224   unsigned long ps = 0;
5225   if (pstat_getstatic(&pst, sizeof(pst), (size_t)1, 0) == -1) {
5226     return false;
5227   }
5228 
5229   ps = pst.page_size;
5230   tp = pst.physical_memory * ps;
5231   tv = (pst.physical_memory + pst.pst_maxmem) * ps;
5232   if (pstat_getdynamic(&pdy, sizeof(pdy), (size_t)1, 0) == -1) {
5233     return false;
5234   }
5235 
5236   ap = tp - pdy.psd_rm * ps;
5237   av = tv - pdy.psd_vm;
5238   this->TotalVirtualMemory = tv >> 10 >> 10;
5239   this->TotalPhysicalMemory = tp >> 10 >> 10;
5240   this->AvailableVirtualMemory = av >> 10 >> 10;
5241   this->AvailablePhysicalMemory = ap >> 10 >> 10;
5242   return true;
5243 #else
5244   return false;
5245 #endif
5246 }
5247 
QueryHPUXProcessor()5248 bool SystemInformationImplementation::QueryHPUXProcessor()
5249 {
5250 #if defined(__hpux)
5251 #  if defined(KWSYS_SYS_HAS_MPCTL_H)
5252   int c = mpctl(MPC_GETNUMSPUS_SYS, 0, 0);
5253   if (c <= 0) {
5254     return false;
5255   }
5256 
5257   this->NumberOfPhysicalCPU = c;
5258   this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU;
5259 
5260   long t = sysconf(_SC_CPU_VERSION);
5261 
5262   if (t == -1) {
5263     return false;
5264   }
5265 
5266   switch (t) {
5267     case CPU_PA_RISC1_0:
5268       this->ChipID.Vendor = "Hewlett-Packard";
5269       this->ChipID.Family = 0x100;
5270       break;
5271     case CPU_PA_RISC1_1:
5272       this->ChipID.Vendor = "Hewlett-Packard";
5273       this->ChipID.Family = 0x110;
5274       break;
5275     case CPU_PA_RISC2_0:
5276       this->ChipID.Vendor = "Hewlett-Packard";
5277       this->ChipID.Family = 0x200;
5278       break;
5279 #    if defined(CPU_HP_INTEL_EM_1_0) || defined(CPU_IA64_ARCHREV_0)
5280 #      ifdef CPU_HP_INTEL_EM_1_0
5281     case CPU_HP_INTEL_EM_1_0:
5282 #      endif
5283 #      ifdef CPU_IA64_ARCHREV_0
5284     case CPU_IA64_ARCHREV_0:
5285 #      endif
5286       this->ChipID.Vendor = "GenuineIntel";
5287       this->Features.HasIA64 = true;
5288       break;
5289 #    endif
5290     default:
5291       return false;
5292   }
5293 
5294   this->FindManufacturer();
5295 
5296   return true;
5297 #  else
5298   return false;
5299 #  endif
5300 #else
5301   return false;
5302 #endif
5303 }
5304 
5305 /** Query the operating system information */
QueryOSInformation()5306 bool SystemInformationImplementation::QueryOSInformation()
5307 {
5308 #if defined(_WIN32)
5309 
5310   this->OSName = "Windows";
5311 
5312   OSVERSIONINFOEXW osvi;
5313   BOOL bIsWindows64Bit;
5314   BOOL bOsVersionInfoEx;
5315   char operatingSystem[256];
5316 
5317   // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
5318   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
5319   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
5320 #  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
5321 #    pragma warning(push)
5322 #    ifdef __INTEL_COMPILER
5323 #      pragma warning(disable : 1478)
5324 #    elif defined __clang__
5325 #      pragma clang diagnostic push
5326 #      pragma clang diagnostic ignored "-Wdeprecated-declarations"
5327 #    else
5328 #      pragma warning(disable : 4996)
5329 #    endif
5330 #  endif
5331   bOsVersionInfoEx = GetVersionExW((OSVERSIONINFOW*)&osvi);
5332   if (!bOsVersionInfoEx) {
5333     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
5334     if (!GetVersionExW((OSVERSIONINFOW*)&osvi)) {
5335       return false;
5336     }
5337   }
5338 #  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
5339 #    ifdef __clang__
5340 #      pragma clang diagnostic pop
5341 #    else
5342 #      pragma warning(pop)
5343 #    endif
5344 #  endif
5345 
5346   switch (osvi.dwPlatformId) {
5347     case VER_PLATFORM_WIN32_NT:
5348       // Test for the product.
5349       if (osvi.dwMajorVersion <= 4) {
5350         this->OSRelease = "NT";
5351       }
5352       if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
5353         this->OSRelease = "2000";
5354       }
5355       if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
5356         this->OSRelease = "XP";
5357       }
5358       // XP Professional x64
5359       if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
5360         this->OSRelease = "XP";
5361       }
5362 #  ifdef VER_NT_WORKSTATION
5363       // Test for product type.
5364       if (bOsVersionInfoEx) {
5365         if (osvi.wProductType == VER_NT_WORKSTATION) {
5366           if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) {
5367             this->OSRelease = "Vista";
5368           }
5369           if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) {
5370             this->OSRelease = "7";
5371           }
5372 // VER_SUITE_PERSONAL may not be defined
5373 #    ifdef VER_SUITE_PERSONAL
5374           else {
5375             if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
5376               this->OSRelease += " Personal";
5377             } else {
5378               this->OSRelease += " Professional";
5379             }
5380           }
5381 #    endif
5382         } else if (osvi.wProductType == VER_NT_SERVER) {
5383           // Check for .NET Server instead of Windows XP.
5384           if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
5385             this->OSRelease = ".NET";
5386           }
5387 
5388           // Continue with the type detection.
5389           if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
5390             this->OSRelease += " DataCenter Server";
5391           } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
5392             this->OSRelease += " Advanced Server";
5393           } else {
5394             this->OSRelease += " Server";
5395           }
5396         }
5397 
5398         sprintf(operatingSystem, "%ls (Build %ld)", osvi.szCSDVersion,
5399                 osvi.dwBuildNumber & 0xFFFF);
5400         this->OSVersion = operatingSystem;
5401       } else
5402 #  endif // VER_NT_WORKSTATION
5403       {
5404         HKEY hKey;
5405         wchar_t szProductType[80];
5406         DWORD dwBufLen;
5407 
5408         // Query the registry to retrieve information.
5409         RegOpenKeyExW(HKEY_LOCAL_MACHINE,
5410                       L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 0,
5411                       KEY_QUERY_VALUE, &hKey);
5412         RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
5413                          (LPBYTE)szProductType, &dwBufLen);
5414         RegCloseKey(hKey);
5415 
5416         if (lstrcmpiW(L"WINNT", szProductType) == 0) {
5417           this->OSRelease += " Professional";
5418         }
5419         if (lstrcmpiW(L"LANMANNT", szProductType) == 0) {
5420           // Decide between Windows 2000 Advanced Server and Windows .NET
5421           // Enterprise Server.
5422           if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
5423             this->OSRelease += " Standard Server";
5424           } else {
5425             this->OSRelease += " Server";
5426           }
5427         }
5428         if (lstrcmpiW(L"SERVERNT", szProductType) == 0) {
5429           // Decide between Windows 2000 Advanced Server and Windows .NET
5430           // Enterprise Server.
5431           if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
5432             this->OSRelease += " Enterprise Server";
5433           } else {
5434             this->OSRelease += " Advanced Server";
5435           }
5436         }
5437       }
5438 
5439       // Display version, service pack (if any), and build number.
5440       if (osvi.dwMajorVersion <= 4) {
5441         // NB: NT 4.0 and earlier.
5442         sprintf(operatingSystem, "version %ld.%ld %ls (Build %ld)",
5443                 osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.szCSDVersion,
5444                 osvi.dwBuildNumber & 0xFFFF);
5445         this->OSVersion = operatingSystem;
5446       } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
5447         // Windows XP and .NET server.
5448         typedef BOOL(CALLBACK * LPFNPROC)(HANDLE, BOOL*);
5449         HINSTANCE hKernelDLL;
5450         LPFNPROC DLLProc;
5451 
5452         // Load the Kernel32 DLL.
5453         hKernelDLL = LoadLibraryW(L"kernel32");
5454         if (hKernelDLL != nullptr) {
5455           // Only XP and .NET Server support IsWOW64Process so... Load
5456           // dynamically!
5457           DLLProc = (LPFNPROC)GetProcAddress(hKernelDLL, "IsWow64Process");
5458 
5459           // If the function address is valid, call the function.
5460           if (DLLProc != nullptr)
5461             (DLLProc)(GetCurrentProcess(), &bIsWindows64Bit);
5462           else
5463             bIsWindows64Bit = false;
5464 
5465           // Free the DLL module.
5466           FreeLibrary(hKernelDLL);
5467         }
5468       } else {
5469         // Windows 2000 and everything else.
5470         sprintf(operatingSystem, "%ls (Build %ld)", osvi.szCSDVersion,
5471                 osvi.dwBuildNumber & 0xFFFF);
5472         this->OSVersion = operatingSystem;
5473       }
5474       break;
5475 
5476     case VER_PLATFORM_WIN32_WINDOWS:
5477       // Test for the product.
5478       if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
5479         this->OSRelease = "95";
5480         if (osvi.szCSDVersion[1] == 'C') {
5481           this->OSRelease += "OSR 2.5";
5482         } else if (osvi.szCSDVersion[1] == 'B') {
5483           this->OSRelease += "OSR 2";
5484         }
5485       }
5486 
5487       if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
5488         this->OSRelease = "98";
5489         if (osvi.szCSDVersion[1] == 'A') {
5490           this->OSRelease += "SE";
5491         }
5492       }
5493 
5494       if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
5495         this->OSRelease = "Me";
5496       }
5497       break;
5498 
5499     case VER_PLATFORM_WIN32s:
5500       this->OSRelease = "Win32s";
5501       break;
5502 
5503     default:
5504       this->OSRelease = "Unknown";
5505       break;
5506   }
5507 
5508   // Get the hostname
5509   WORD wVersionRequested;
5510   WSADATA wsaData;
5511   char name[255];
5512   wVersionRequested = MAKEWORD(2, 0);
5513 
5514   if (WSAStartup(wVersionRequested, &wsaData) == 0) {
5515     gethostname(name, sizeof(name));
5516     WSACleanup();
5517   }
5518   this->Hostname = name;
5519 
5520   const char* arch = getenv("PROCESSOR_ARCHITECTURE");
5521   const char* wow64 = getenv("PROCESSOR_ARCHITEW6432");
5522   if (arch) {
5523     this->OSPlatform = arch;
5524   }
5525 
5526   if (wow64) {
5527     // the PROCESSOR_ARCHITEW6432 is only defined when running 32bit programs
5528     // on 64bit OS
5529     this->OSIs64Bit = true;
5530   } else if (arch) {
5531     // all values other than x86 map to 64bit architectures
5532     this->OSIs64Bit = (strncmp(arch, "x86", 3) != 0);
5533   }
5534 
5535 #else
5536 
5537   struct utsname unameInfo;
5538   int errorFlag = uname(&unameInfo);
5539   if (errorFlag == 0) {
5540     this->OSName = unameInfo.sysname;
5541     this->Hostname = unameInfo.nodename;
5542     this->OSRelease = unameInfo.release;
5543     this->OSVersion = unameInfo.version;
5544     this->OSPlatform = unameInfo.machine;
5545 
5546     // This is still insufficient to capture 64bit architecture such
5547     // powerpc and possible mips and sparc
5548     if (this->OSPlatform.find_first_of("64") != std::string::npos) {
5549       this->OSIs64Bit = true;
5550     }
5551   }
5552 
5553 #  ifdef __APPLE__
5554   this->OSName = "Unknown Apple OS";
5555   this->OSRelease = "Unknown product version";
5556   this->OSVersion = "Unknown build version";
5557 
5558   this->CallSwVers("-productName", this->OSName);
5559   this->CallSwVers("-productVersion", this->OSRelease);
5560   this->CallSwVers("-buildVersion", this->OSVersion);
5561 #  endif
5562 
5563 #endif
5564 
5565   return true;
5566 }
5567 
CallSwVers(const char * arg,std::string & ver)5568 int SystemInformationImplementation::CallSwVers(const char* arg,
5569                                                 std::string& ver)
5570 {
5571 #ifdef __APPLE__
5572   std::vector<const char*> args;
5573   args.push_back("sw_vers");
5574   args.push_back(arg);
5575   args.push_back(nullptr);
5576   ver = this->RunProcess(args);
5577   this->TrimNewline(ver);
5578 #else
5579   // avoid C4100
5580   (void)arg;
5581   (void)ver;
5582 #endif
5583   return 0;
5584 }
5585 
TrimNewline(std::string & output)5586 void SystemInformationImplementation::TrimNewline(std::string& output)
5587 {
5588   // remove \r
5589   std::string::size_type pos = 0;
5590   while ((pos = output.find('\r', pos)) != std::string::npos) {
5591     output.erase(pos);
5592   }
5593 
5594   // remove \n
5595   pos = 0;
5596   while ((pos = output.find('\n', pos)) != std::string::npos) {
5597     output.erase(pos);
5598   }
5599 }
5600 
5601 /** Return true if the machine is 64 bits */
Is64Bits() const5602 bool SystemInformationImplementation::Is64Bits() const
5603 {
5604   return this->OSIs64Bit;
5605 }
5606 }
5607