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