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