SystemInformation.cxx 104 KB
Newer Older
1
2
3
/*============================================================================
  KWSys - Kitware System Library
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
7

8
9
10
11
  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
Sebastien Barre's avatar
Sebastien Barre committed
12
13
14
15
#ifdef _WIN32
# include <winsock.h> // WSADATA, include before sys/types.h
#endif

16
17
18
19
20
21
22
23
24
25
26
27
// TODO:
// We need an alternative implementation for many functions in this file
// when USE_ASM_INSTRUCTIONS gets defined as 0.
//
// Consider using these on Win32/Win64 for some of them:
//
// IsProcessorFeaturePresent
// http://msdn.microsoft.com/en-us/library/ms724482(VS.85).aspx
//
// GetProcessMemoryInfo
// http://msdn.microsoft.com/en-us/library/ms683219(VS.85).aspx

28
#include "kwsysPrivate.h"
29
30
31
32
#include KWSYS_HEADER(FundamentalType.h)
#include KWSYS_HEADER(stl/string)
#include KWSYS_HEADER(stl/vector)
#include KWSYS_HEADER(ios/iosfwd)
33
34
35
#include KWSYS_HEADER(SystemInformation.hxx)
#include KWSYS_HEADER(Process.h)
#include KWSYS_HEADER(ios/iostream)
36
#include KWSYS_HEADER(ios/sstream)
37

38
39
40
41
42
43
44
45
46
47
48
49
// Work-around CMake dependency scanning limitation.  This must
// duplicate the above list of headers.
#if 0
# include "FundamentalType.h.in"
# include "SystemInformation.hxx.in"
# include "Process.h.in"
# include "Configure.hxx.in"
# include "kwsys_stl.hxx.in"
# include "kwsys_stl_vector.in"
# include "kwsys_stl_iosfwd.in"
# include "kwsys_ios_sstream.h.in"
# include "kwsys_ios_iostream.h.in"
50
51
#endif

52
53
54
55
56
57
58
59
#ifndef WIN32
# include <sys/utsname.h> // int uname(struct utsname *buf);
#endif

#ifdef _WIN32
# include <windows.h>
#endif

60
61
62
63
64
65
66
67
#ifdef __APPLE__
#include <sys/sysctl.h>
#include <mach/vm_statistics.h>
#include <mach/host_info.h>
#include <mach/mach.h>
#include <mach/mach_types.h>
#endif

68
69
70
71
72
73
74
75
76
77
78
79
80
#ifdef __linux
# include <sys/procfs.h>
# include <sys/types.h>
# include <unistd.h>
# include <fcntl.h>
# include <ctype.h> // int isdigit(int c);
# include <errno.h> // extern int errno;
# include <sys/time.h>
#elif __hpux
# include <sys/param.h>
# include <sys/pstat.h>
#endif

81
82
83
84
#ifdef __HAIKU__
#include <OS.h>
#endif

85
86
87
88
89
90
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


91
92
93
namespace KWSYS_NAMESPACE
{

94
95
96
97
98
99
100
101
102
// Create longlong
#if KWSYS_USE_LONG_LONG
  typedef long long LongLong;
#elif KWSYS_USE___INT64
  typedef __int64 LongLong;
#else
# error "No Long Long"
#endif

103

104
105
106
//  Define SystemInformationImplementation class
typedef  void (*DELAY_FUNC)(unsigned int uiMS);

107
108
109

class SystemInformationImplementation
{
110
111
112
113
114
115
116
117
118
119
120
121
122
public:
  SystemInformationImplementation ();
  ~SystemInformationImplementation ();

  const char * GetVendorString();
  const char * GetVendorID();
  kwsys_stl::string GetTypeID();
  kwsys_stl::string GetFamilyID();
  kwsys_stl::string GetModelID();
  kwsys_stl::string GetSteppingCode();
  const char * GetExtendedProcessorName();
  const char * GetProcessorSerialNumber();
  int GetProcessorCacheSize();
123
  unsigned int GetLogicalProcessorsPerPhysical();
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  float GetProcessorClockFrequency();
  int GetProcessorAPICID();
  int GetProcessorCacheXSize(long int);
  bool DoesCPUSupportFeature(long int);
  
  const char * GetOSName();
  const char * GetHostname();
  const char * GetOSRelease();
  const char * GetOSVersion();
  const char * GetOSPlatform();

  bool Is64Bits();

  unsigned int GetNumberOfLogicalCPU(); // per physical cpu
  unsigned int GetNumberOfPhysicalCPU();

  bool DoesCPUSupportCPUID();

  // Retrieve memory information in megabyte.
143
144
145
146
  size_t GetTotalVirtualMemory();
  size_t GetAvailableVirtualMemory();
  size_t GetTotalPhysicalMemory();
  size_t GetAvailablePhysicalMemory();  
147
148
149
150
151

  /** Run the different checks */
  void RunCPUCheck();
  void RunOSCheck();
  void RunMemoryCheck();
152

153
public:
154
  typedef struct tagID
155
156
157
158
159
160
161
    {
    int Type;
    int Family;
    int Model;
    int Revision;
    int ExtendedFamily;
    int ExtendedModel;
162
163
164
    kwsys_stl::string ProcessorName;
    kwsys_stl::string Vendor;
    kwsys_stl::string SerialNumber;
165
166
    } ID;

167
  typedef struct tagCPUPowerManagement
168
169
170
171
172
173
    {
    bool HasVoltageID;
    bool HasFrequencyID;
    bool HasTempSenseDiode;
    } CPUPowerManagement;

174
  typedef struct tagCPUExtendedFeatures
175
176
177
178
179
180
181
    {
    bool Has3DNow;
    bool Has3DNowPlus;
    bool SupportsMP;
    bool HasMMXPlus;
    bool HasSSEMMX;
    bool SupportsHyperthreading;
182
    unsigned int LogicalProcessorsPerPhysical;
183
184
    int APIC_ID;
    CPUPowerManagement PowerManagement;
185
186
187
    } CPUExtendedFeatures;

  typedef struct CPUtagFeatures
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
    {
    bool HasFPU;
    bool HasTSC;
    bool HasMMX;
    bool HasSSE;
    bool HasSSEFP;
    bool HasSSE2;
    bool HasIA64;
    bool HasAPIC;
    bool HasCMOV;
    bool HasMTRR;
    bool HasACPI;
    bool HasSerial;
    bool HasThermal;
    int CPUSpeed;
    int L1CacheSize;
    int L2CacheSize;
    int L3CacheSize;
    CPUExtendedFeatures ExtendedFeatures;
    } CPUFeatures;
208
209

  enum Manufacturer
210
    {
211
212
    AMD, Intel, NSC, UMC, Cyrix, NexGen, IDT, Rise, Transmeta, Sun, IBM,
    Motorola, UnknownManufacturer
213
214
    };

215
protected:
216
217
218
219
220
221
222
223
224
225
226
227
228
  // Functions.
  bool RetrieveCPUFeatures();
  bool RetrieveCPUIdentity();
  bool RetrieveCPUCacheDetails();
  bool RetrieveClassicalCPUCacheDetails();
  bool RetrieveCPUClockSpeed();
  bool RetrieveClassicalCPUClockSpeed();
  bool RetrieveCPUExtendedLevelSupport(int);
  bool RetrieveExtendedCPUFeatures();
  bool RetrieveProcessorSerialNumber();
  bool RetrieveCPUPowerManagement();
  bool RetrieveClassicalCPUIdentity();
  bool RetrieveExtendedCPUIdentity();
229

230
231
232
233
234
235
236
237
238
239
240
241
242
  Manufacturer  ChipManufacturer;
  CPUFeatures   Features;
  ID            ChipID;
  float         CPUSpeedInMHz;
  unsigned int  NumberOfLogicalCPU;
  unsigned int  NumberOfPhysicalCPU;

  int CPUCount();
  unsigned char LogicalCPUPerPhysicalCPU();
  unsigned char GetAPICId();
  unsigned int IsHyperThreadingSupported();
  LongLong GetCyclesDifference(DELAY_FUNC, unsigned int);

243
  // For Linux and Cygwin, /proc/cpuinfo formats are slightly different
244
245
  int RetreiveInformationFromCpuInfoFile();
  kwsys_stl::string ExtractValueFromCpuInfoFile(kwsys_stl::string buffer,
Bill Hoffman's avatar
Bill Hoffman committed
246
                                          const char* word, size_t init=0);
247
248
249
250
251
252
253
254

  static void Delay (unsigned int);
  static void DelayOverhead (unsigned int);

  void FindManufacturer();

  // For Mac
  bool ParseSysCtl();
255
  void CallSwVers();
256
  void TrimNewline(kwsys_stl::string&);
257
258
259
260
261
262
263
264
  kwsys_stl::string ExtractValueFromSysCtl(const char* word);
  kwsys_stl::string SysCtlBuffer;

  // For Solaris
  bool QuerySolarisInfo();
  kwsys_stl::string ParseValueFromKStat(const char* arguments);
  kwsys_stl::string RunProcess(kwsys_stl::vector<const char*> args);

265
266
267
  //For Haiku OS
  bool QueryHaikuInfo();

268
269
  // Evaluate the memory information.
  int QueryMemory();
270
271
272
273
  size_t TotalVirtualMemory;
  size_t AvailableVirtualMemory;
  size_t TotalPhysicalMemory;
  size_t AvailablePhysicalMemory;
274

Bill Hoffman's avatar
Bill Hoffman committed
275
  size_t CurrentPositionInFile;
276
277
278
279
280
281
282
283
284

  // Operating System information
  bool QueryOSInformation();
  kwsys_stl::string OSName;
  kwsys_stl::string Hostname;
  kwsys_stl::string OSRelease;
  kwsys_stl::string OSVersion;
  kwsys_stl::string OSPlatform; 
};
285

286

287
SystemInformation::SystemInformation()
288
289
290
291
{
  this->Implementation = new SystemInformationImplementation;
}

292
SystemInformation::~SystemInformation()
293
294
295
296
297
298
299
300
{
  delete this->Implementation;
}

const char * SystemInformation::GetVendorString()
{
  return this->Implementation->GetVendorString();
}
301

302
303
304
305
const char * SystemInformation::GetVendorID()
{
  return this->Implementation->GetVendorID();
}
306

307
308
309
310
kwsys_stl::string SystemInformation::GetTypeID()
{
  return this->Implementation->GetTypeID();
}
311

312
313
314
315
kwsys_stl::string SystemInformation::GetFamilyID()
{
  return this->Implementation->GetFamilyID();
}
316

317
318
319
320
kwsys_stl::string SystemInformation::GetModelID()
{
  return this->Implementation->GetModelID();
}
321

322
323
324
325
kwsys_stl::string SystemInformation::GetSteppingCode()
{
  return this->Implementation->GetSteppingCode();
}
326

327
328
329
330
const char * SystemInformation::GetExtendedProcessorName()
{
  return this->Implementation->GetExtendedProcessorName();
}
331

332
333
334
335
const char * SystemInformation::GetProcessorSerialNumber()
{
  return this->Implementation->GetProcessorSerialNumber();
}
336

337
338
339
340
int SystemInformation::GetProcessorCacheSize()
{
  return this->Implementation->GetProcessorCacheSize();
}
341

342
unsigned int SystemInformation::GetLogicalProcessorsPerPhysical()
343
344
345
{
  return this->Implementation->GetLogicalProcessorsPerPhysical();
}
346

347
348
349
350
float SystemInformation::GetProcessorClockFrequency()
{
  return this->Implementation->GetProcessorClockFrequency();
}
351

352
353
354
355
int SystemInformation::GetProcessorAPICID()
{
  return this->Implementation->GetProcessorAPICID();
}
356

357
358
359
360
int SystemInformation::GetProcessorCacheXSize(long int l)
{
  return this->Implementation->GetProcessorCacheXSize(l);
}
361

362
363
364
365
366
367
368
369
370
bool SystemInformation::DoesCPUSupportFeature(long int i)
{
  return this->Implementation->DoesCPUSupportFeature(i);
}

const char * SystemInformation::GetOSName()
{
  return this->Implementation->GetOSName();
}
371

372
373
374
375
const char * SystemInformation::GetHostname()
{
  return this->Implementation->GetHostname();
}
376

377
378
379
380
const char * SystemInformation::GetOSRelease()
{
  return this->Implementation->GetOSRelease();
}
381

382
383
384
385
const char * SystemInformation::GetOSVersion()
{
  return this->Implementation->GetOSVersion();
}
386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
const char * SystemInformation::GetOSPlatform()
{
  return this->Implementation->GetOSPlatform();
}

bool SystemInformation::Is64Bits()
{
  return this->Implementation->Is64Bits();
}

unsigned int SystemInformation::GetNumberOfLogicalCPU() // per physical cpu
{
  return this->Implementation->GetNumberOfLogicalCPU();
}
401

402
403
404
405
406
407
408
409
410
411
412
unsigned int SystemInformation::GetNumberOfPhysicalCPU()
{
  return this->Implementation->GetNumberOfPhysicalCPU();
}

bool SystemInformation::DoesCPUSupportCPUID()
{
  return this->Implementation->DoesCPUSupportCPUID();
}

// Retrieve memory information in megabyte.
413
size_t SystemInformation::GetTotalVirtualMemory()
414
415
416
{
  return this->Implementation->GetTotalVirtualMemory();
}
417

418
size_t SystemInformation::GetAvailableVirtualMemory()
419
420
421
{
  return this->Implementation->GetAvailableVirtualMemory();
}
422

423
size_t SystemInformation::GetTotalPhysicalMemory()
424
425
426
427
{
  return this->Implementation->GetTotalPhysicalMemory();
}

428
size_t SystemInformation::GetAvailablePhysicalMemory()
429
430
431
432
433
434
435
{
  return this->Implementation->GetAvailablePhysicalMemory();
}

/** Run the different checks */
void SystemInformation::RunCPUCheck()
{
436
  this->Implementation->RunCPUCheck();
437
}
438

439
440
void SystemInformation::RunOSCheck()
{
441
  this->Implementation->RunOSCheck();
442
}
443

444
445
void SystemInformation::RunMemoryCheck()
{
446
  this->Implementation->RunMemoryCheck();
447
448
449
450
451
452
}


// --------------------------------------------------------------
// SystemInformationImplementation starts here

453
454
455
456
457
458
#if defined(_MSC_VER) && (_MSC_VER >= 1300) && !defined(_WIN64)
#define USE_ASM_INSTRUCTIONS 1
#else
#define USE_ASM_INSTRUCTIONS 0
#endif

459
460
461
462
463
464
465
466
467
468
469
470
#define STORE_TLBCACHE_INFO(x,y)  x = (x < y) ? y : x
#define TLBCACHE_INFO_UNITS      (15)
#define CLASSICAL_CPU_FREQ_LOOP    10000000
#define RDTSC_INSTRUCTION      _asm _emit 0x0f _asm _emit 0x31

#define CPUID_AWARE_COMPILER
#ifdef CPUID_AWARE_COMPILER
  #define CPUID_INSTRUCTION    cpuid
#else
  #define CPUID_INSTRUCTION    _asm _emit 0x0f _asm _emit 0xa2
#endif

471
472
473
474
#define MMX_FEATURE            0x00000001
#define MMX_PLUS_FEATURE       0x00000002
#define SSE_FEATURE            0x00000004
#define SSE2_FEATURE           0x00000008
475
#define AMD_3DNOW_FEATURE      0x00000010
476
477
478
479
480
481
482
#define AMD_3DNOW_PLUS_FEATURE 0x00000020
#define IA64_FEATURE           0x00000040
#define MP_CAPABLE             0x00000080
#define HYPERTHREAD_FEATURE    0x00000100
#define SERIALNUMBER_FEATURE   0x00000200
#define APIC_FEATURE           0x00000400
#define SSE_FP_FEATURE         0x00000800
483
#define SSE_MMX_FEATURE        0x00001000
484
485
#define CMOV_FEATURE           0x00002000
#define MTRR_FEATURE           0x00004000
486
487
488
#define L1CACHE_FEATURE        0x00008000
#define L2CACHE_FEATURE        0x00010000
#define L3CACHE_FEATURE        0x00020000
489
490
491
492
493
#define ACPI_FEATURE           0x00040000
#define THERMALMONITOR_FEATURE 0x00080000
#define TEMPSENSEDIODE_FEATURE 0x00100000
#define FREQUENCYID_FEATURE    0x00200000
#define VOLTAGEID_FREQUENCY    0x00400000
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520

// Status Flag
#define HT_NOT_CAPABLE           0
#define HT_ENABLED               1
#define HT_DISABLED              2
#define HT_SUPPORTED_NOT_ENABLED 3
#define HT_CANNOT_DETECT         4

// EDX[28]  Bit 28 is set if HT is supported
#define HT_BIT             0x10000000   

// EAX[11:8] Bit 8-11 contains family processor ID.
#define FAMILY_ID          0x0F00
#define PENTIUM4_ID        0x0F00         
// EAX[23:20] Bit 20-23 contains extended family processor ID
#define EXT_FAMILY_ID      0x0F00000 
// EBX[23:16] Bit 16-23 in ebx contains the number of logical
#define NUM_LOGICAL_BITS   0x00FF0000  
// processors per physical processor when execute cpuid with 
// eax set to 1
// EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique 
#define INITIAL_APIC_ID_BITS  0xFF000000  
// initial APIC ID for the processor this code is running on.
// Default value = 0xff if HT is not supported


SystemInformationImplementation::SystemInformationImplementation()
521
522
523
524
525
526
527
528
{
  this->TotalVirtualMemory = 0;
  this->AvailableVirtualMemory = 0;
  this->TotalPhysicalMemory = 0;
  this->AvailablePhysicalMemory = 0;
  this->CurrentPositionInFile = 0;
  this->ChipManufacturer = UnknownManufacturer;
  memset(&this->Features, 0, sizeof(CPUFeatures));
529
530
531
532
533
534
  this->ChipID.Type = 0;
  this->ChipID.Family = 0;
  this->ChipID.Model = 0;
  this->ChipID.Revision = 0;
  this->ChipID.ExtendedFamily = 0;
  this->ChipID.ExtendedModel = 0;
535
536
537
538
539
540
541
542
543
544
  this->CPUSpeedInMHz = 0;
  this->NumberOfLogicalCPU = 0;
  this->NumberOfPhysicalCPU = 0;
  this->OSName = "";
  this->Hostname = "";
  this->OSRelease = "";
  this->OSVersion = "";
  this->OSPlatform = "";
}

545
SystemInformationImplementation::~SystemInformationImplementation()
546
547
548
{
}

549
void SystemInformationImplementation::RunCPUCheck()
550
551
552
{
#ifdef WIN32
  // Check to see if this processor supports CPUID.
553
554
555
  bool supportsCPUID = DoesCPUSupportCPUID();

  if (supportsCPUID)
556
557
558
559
    {
    // Retrieve the CPU details.
    RetrieveCPUIdentity();
    RetrieveCPUFeatures();
560
561
562
563
564
565
566
567
568
569
570
    }

  // These two may be called without support for the CPUID instruction.
  // (But if the instruction is there, they should be called *after*
  // the above call to RetrieveCPUIdentity... that's why the two if
  // blocks exist with the same "if (supportsCPUID)" logic...
  //
  if (!RetrieveCPUClockSpeed())
    {
    RetrieveClassicalCPUClockSpeed();
    }
571

572
573
574
  if (supportsCPUID)
    {
    // Retrieve cache information.
575
576
577
578
    if (!RetrieveCPUCacheDetails()) 
      {
      RetrieveClassicalCPUCacheDetails();
      }
579

580
581
582
583
584
    // Retrieve the extended CPU details.
    if (!RetrieveExtendedCPUIdentity()) 
      {
      RetrieveClassicalCPUIdentity();
      }
585

586
    RetrieveExtendedCPUFeatures();
587
    RetrieveCPUPowerManagement();
588
589
590
591

    // Now attempt to retrieve the serial number (if possible).
    RetrieveProcessorSerialNumber();
    }
592

593
  this->CPUCount();
594

595
596
597
598
#elif defined(__APPLE__)
  this->ParseSysCtl();
#elif defined (__SVR4) && defined (__sun)
  this->QuerySolarisInfo();
599
600
#elif defined(__HAIKU__)
  this->QueryHaikuInfo();
601
602
603
604
605
#else
  this->RetreiveInformationFromCpuInfoFile();
#endif
}

606
void SystemInformationImplementation::RunOSCheck()
607
608
609
{
  this->QueryOSInformation();
}
610

611
void SystemInformationImplementation::RunMemoryCheck()
612
613
614
615
616
{
#if defined(__APPLE__)
  this->ParseSysCtl();
#elif defined (__SVR4) && defined (__sun)
  this->QuerySolarisInfo();
617
618
#elif defined(__HAIKU__)
  this->QueryHaikuInfo();
619
620
621
622
623
624
#else
  this->QueryMemory();
#endif
}

/** Get the vendor string */
625
const char * SystemInformationImplementation::GetVendorString()
626
{
627
  return this->ChipID.Vendor.c_str();
628
629
630
}

/** Get the OS Name */
631
const char * SystemInformationImplementation::GetOSName()
632
633
634
635
636
{
  return this->OSName.c_str();
}

/** Get the hostname */
637
const char* SystemInformationImplementation::GetHostname()
638
639
640
641
642
{
  return this->Hostname.c_str();
}

/** Get the OS release */
643
const char* SystemInformationImplementation::GetOSRelease()
644
645
646
647
648
{
  return this->OSRelease.c_str();
}

/** Get the OS version */
649
const char* SystemInformationImplementation::GetOSVersion()
650
651
652
653
654
{
  return this->OSVersion.c_str();
}

/** Get the OS platform */
655
const char* SystemInformationImplementation::GetOSPlatform()
656
657
658
659
660
{
  return this->OSPlatform.c_str();
}

/** Get the vendor ID */
661
const char * SystemInformationImplementation::GetVendorID()
662
663
{
  // Return the vendor ID.
664
  switch (this->ChipManufacturer)
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
    {
    case Intel:
      return "Intel Corporation";
    case AMD:
      return "Advanced Micro Devices";
    case NSC:
      return "National Semiconductor";
    case Cyrix:
      return "Cyrix Corp., VIA Inc.";
    case NexGen:
      return "NexGen Inc., Advanced Micro Devices";
    case IDT:
      return "IDT\\Centaur, Via Inc.";
    case UMC:
      return "United Microelectronics Corp.";
    case Rise:
      return "Rise";
    case Transmeta:
      return "Transmeta";
    case Sun:
      return "Sun Microelectronics";
686
687
688
689
    case IBM:
      return "IBM";
    case Motorola:
      return "Motorola";
690
691
692
693
694
695
    default:
      return "Unknown Manufacturer";
    }
}

/** Return the type ID of the CPU */
696
kwsys_stl::string SystemInformationImplementation::GetTypeID()
697
{
698
699
700
  kwsys_ios::ostringstream str;
  str << this->ChipID.Type;
  return str.str();
701
702
703
}

/** Return the family of the CPU present */
704
kwsys_stl::string SystemInformationImplementation::GetFamilyID()
705
{
706
707
708
  kwsys_ios::ostringstream str;
  str << this->ChipID.Family;
  return str.str();
709
710
711
}

// Return the model of CPU present */
712
kwsys_stl::string SystemInformationImplementation::GetModelID()
713
{
714
715
716
  kwsys_ios::ostringstream str;
  str << this->ChipID.Model;
  return str.str();
717
718
719
}

/** Return the stepping code of the CPU present. */
720
kwsys_stl::string SystemInformationImplementation::GetSteppingCode()
721
722
723
724
{ 
  kwsys_ios::ostringstream str;
  str << this->ChipID.Revision;
  return str.str();
725
726
727
}

/** Return the stepping code of the CPU present. */
728
const char * SystemInformationImplementation::GetExtendedProcessorName()
729
{
730
  return this->ChipID.ProcessorName.c_str();
731
732
733
734
}
  
/** Return the serial number of the processor 
 *  in hexadecimal: xxxx-xxxx-xxxx-xxxx-xxxx-xxxx. */
735
const char * SystemInformationImplementation::GetProcessorSerialNumber()
736
{
737
  return this->ChipID.SerialNumber.c_str();
738
739
740
}

/** Return the logical processors per physical */
741
unsigned int SystemInformationImplementation::GetLogicalProcessorsPerPhysical()
742
743
744
745
746
{
  return this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical;
}

/** Return the processor clock frequency. */
747
float SystemInformationImplementation::GetProcessorClockFrequency()
748
749
750
751
752
{
  return this->CPUSpeedInMHz;
}

/**  Return the APIC ID. */
753
int SystemInformationImplementation::GetProcessorAPICID()
754
755
756
757
758
{
  return this->Features.ExtendedFeatures.APIC_ID;
}

/** Return the L1 cache size. */
759
int SystemInformationImplementation::GetProcessorCacheSize()
760
761
762
763
764
{
  return this->Features.L1CacheSize;
}

/** Return the chosen cache size. */
765
int SystemInformationImplementation::GetProcessorCacheXSize(long int dwCacheID)
766
{
767
  switch (dwCacheID)
768
769
770
771
772
773
774
775
776
777
778
    {
    case L1CACHE_FEATURE:
      return this->Features.L1CacheSize;
    case L2CACHE_FEATURE:
      return this->Features.L2CacheSize;
    case L3CACHE_FEATURE:
      return this->Features.L3CacheSize;
    }
  return -1;
}

779

780
bool SystemInformationImplementation::DoesCPUSupportFeature(long int dwFeature)
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
{
  bool bHasFeature = false;

  // Check for MMX instructions.
  if (((dwFeature & MMX_FEATURE) != 0) && this->Features.HasMMX) bHasFeature = true;

  // Check for MMX+ instructions.
  if (((dwFeature & MMX_PLUS_FEATURE) != 0) && this->Features.ExtendedFeatures.HasMMXPlus) bHasFeature = true;

  // Check for SSE FP instructions.
  if (((dwFeature & SSE_FEATURE) != 0) && this->Features.HasSSE) bHasFeature = true;

  // Check for SSE FP instructions.
  if (((dwFeature & SSE_FP_FEATURE) != 0) && this->Features.HasSSEFP) bHasFeature = true;

  // Check for SSE MMX instructions.
  if (((dwFeature & SSE_MMX_FEATURE) != 0) && this->Features.ExtendedFeatures.HasSSEMMX) bHasFeature = true;

  // Check for SSE2 instructions.
  if (((dwFeature & SSE2_FEATURE) != 0) && this->Features.HasSSE2) bHasFeature = true;

  // Check for 3DNow! instructions.
  if (((dwFeature & AMD_3DNOW_FEATURE) != 0) && this->Features.ExtendedFeatures.Has3DNow) bHasFeature = true;

  // Check for 3DNow+ instructions.
  if (((dwFeature & AMD_3DNOW_PLUS_FEATURE) != 0) && this->Features.ExtendedFeatures.Has3DNowPlus) bHasFeature = true;

  // Check for IA64 instructions.
  if (((dwFeature & IA64_FEATURE) != 0) && this->Features.HasIA64) bHasFeature = true;

  // Check for MP capable.
  if (((dwFeature & MP_CAPABLE) != 0) && this->Features.ExtendedFeatures.SupportsMP) bHasFeature = true;

  // Check for a serial number for the processor.
  if (((dwFeature & SERIALNUMBER_FEATURE) != 0) && this->Features.HasSerial) bHasFeature = true;

  // Check for a local APIC in the processor.
  if (((dwFeature & APIC_FEATURE) != 0) && this->Features.HasAPIC) bHasFeature = true;

  // Check for CMOV instructions.
  if (((dwFeature & CMOV_FEATURE) != 0) && this->Features.HasCMOV) bHasFeature = true;

  // Check for MTRR instructions.
  if (((dwFeature & MTRR_FEATURE) != 0) && this->Features.HasMTRR) bHasFeature = true;

  // Check for L1 cache size.
  if (((dwFeature & L1CACHE_FEATURE) != 0) && (this->Features.L1CacheSize != -1)) bHasFeature = true;

  // Check for L2 cache size.
  if (((dwFeature & L2CACHE_FEATURE) != 0) && (this->Features.L2CacheSize != -1)) bHasFeature = true;

  // Check for L3 cache size.
  if (((dwFeature & L3CACHE_FEATURE) != 0) && (this->Features.L3CacheSize != -1)) bHasFeature = true;

  // Check for ACPI capability.
  if (((dwFeature & ACPI_FEATURE) != 0) && this->Features.HasACPI) bHasFeature = true;

  // Check for thermal monitor support.
  if (((dwFeature & THERMALMONITOR_FEATURE) != 0) && this->Features.HasThermal) bHasFeature = true;

  // Check for temperature sensing diode support.
  if (((dwFeature & TEMPSENSEDIODE_FEATURE) != 0) && this->Features.ExtendedFeatures.PowerManagement.HasTempSenseDiode) bHasFeature = true;

  // Check for frequency ID support.
  if (((dwFeature & FREQUENCYID_FEATURE) != 0) && this->Features.ExtendedFeatures.PowerManagement.HasFrequencyID) bHasFeature = true;

  // Check for voltage ID support.
  if (((dwFeature & VOLTAGEID_FREQUENCY) != 0) && this->Features.ExtendedFeatures.PowerManagement.HasVoltageID) bHasFeature = true;

  return bHasFeature;
}

853

854
void SystemInformationImplementation::Delay(unsigned int uiMS)
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
{
#ifdef WIN32
  LARGE_INTEGER Frequency, StartCounter, EndCounter;
  __int64 x;

  // Get the frequency of the high performance counter.
  if (!QueryPerformanceFrequency (&Frequency)) return;
  x = Frequency.QuadPart / 1000 * uiMS;

  // Get the starting position of the counter.
  QueryPerformanceCounter (&StartCounter);

  do {
    // Get the ending position of the counter.  
    QueryPerformanceCounter (&EndCounter);
    } while (EndCounter.QuadPart - StartCounter.QuadPart < x);
#endif
Bill Hoffman's avatar
Bill Hoffman committed
872
  (void)uiMS;
873
874
}

875

876
bool SystemInformationImplementation::DoesCPUSupportCPUID()
877
{
878
#if USE_ASM_INSTRUCTIONS
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
  // Use SEH to determine CPUID presence
    __try {
        _asm {
#ifdef CPUID_AWARE_COMPILER
       ; we must push/pop the registers <<CPUID>> writes to, as the
      ; optimiser doesn't know about <<CPUID>>, and so doesn't expect
      ; these registers to change.
      push eax
      push ebx
      push ecx
      push edx
#endif
      ; <<CPUID>> 
            mov eax, 0
      CPUID_INSTRUCTION

#ifdef CPUID_AWARE_COMPILER
      pop edx
      pop ecx
      pop ebx
      pop eax
#endif
        }
    }
  __except(1) 
    {
    // Stop the class from trying to use CPUID again!
    return false;
    }
908
909
910

  // The cpuid instruction succeeded.
  return true;
911

912
#else
913
914
  // Assume no cpuid instruction.
  return false;
915
916
917
#endif
}

918

919
bool SystemInformationImplementation::RetrieveCPUFeatures()
920
{
921
#if USE_ASM_INSTRUCTIONS
Bill Hoffman's avatar
Bill Hoffman committed
922
923
924
  int localCPUFeatures = 0;
  int localCPUAdvanced = 0;

925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
  // Use assembly to detect CPUID information...
  __try {
    _asm {
#ifdef CPUID_AWARE_COMPILER
       ; we must push/pop the registers <<CPUID>> writes to, as the
      ; optimiser doesn't know about <<CPUID>>, and so doesn't expect
      ; these registers to change.
      push eax
      push ebx
      push ecx
      push edx
#endif
      ; <<CPUID>> 
      ; eax = 1 --> eax: CPU ID - bits 31..16 - unused, bits 15..12 - type, bits 11..8 - family, bits 7..4 - model, bits 3..0 - mask revision
      ;        ebx: 31..24 - default APIC ID, 23..16 - logical processsor ID, 15..8 - CFLUSH chunk size , 7..0 - brand ID
      ;        edx: CPU feature flags
      mov eax,1
      CPUID_INSTRUCTION
Bill Hoffman's avatar
Bill Hoffman committed
943
944
      mov localCPUFeatures, edx
      mov localCPUAdvanced, ebx
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959

#ifdef CPUID_AWARE_COMPILER
      pop edx
      pop ecx
      pop ebx
      pop eax
#endif
    }
  }
  __except(1) 
    {
    return false;
    }

  // Retrieve the features of CPU present.
960
961
  this->Features.HasFPU =     ((localCPUFeatures & 0x00000001) != 0);    // FPU Present --> Bit 0
  this->Features.HasTSC =     ((localCPUFeatures & 0x00000010) != 0);    // TSC Present --> Bit 4
Bill Hoffman's avatar
Bill Hoffman committed
962
963
964
965
966
  this->Features.HasAPIC =    ((localCPUFeatures & 0x00000200) != 0);    // APIC Present --> Bit 9
  this->Features.HasMTRR =    ((localCPUFeatures & 0x00001000) != 0);    // MTRR Present --> Bit 12
  this->Features.HasCMOV =    ((localCPUFeatures & 0x00008000) != 0);    // CMOV Present --> Bit 15
  this->Features.HasSerial =  ((localCPUFeatures & 0x00040000) != 0);    // Serial Present --> Bit 18
  this->Features.HasACPI =    ((localCPUFeatures & 0x00400000) != 0);    // ACPI Capable --> Bit 22
967
968
  this->Features.HasMMX =     ((localCPUFeatures & 0x00800000) != 0);    // MMX Present --> Bit 23
  this->Features.HasSSE =     ((localCPUFeatures & 0x02000000) != 0);    // SSE Present --> Bit 25
Bill Hoffman's avatar
Bill Hoffman committed
969
  this->Features.HasSSE2 =    ((localCPUFeatures & 0x04000000) != 0);    // SSE2 Present --> Bit 26
970
  this->Features.HasThermal = ((localCPUFeatures & 0x20000000) != 0);    // Thermal Monitor Present --> Bit 29
Bill Hoffman's avatar
Bill Hoffman committed
971
  this->Features.HasIA64 =    ((localCPUFeatures & 0x40000000) != 0);    // IA64 Present --> Bit 30
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004

  // Retrieve extended SSE capabilities if SSE is available.
  if (this->Features.HasSSE) {
    
    // Attempt to __try some SSE FP instructions.
    __try 
      {
      // Perform: orps xmm0, xmm0
      _asm 
        {
        _emit 0x0f
        _emit 0x56
        _emit 0xc0  
        }

      // SSE FP capable processor.
      this->Features.HasSSEFP = true;
      }   
    __except(1) 
      {
      // bad instruction - processor or OS cannot handle SSE FP.
      this->Features.HasSSEFP = false;
      }
    } 
  else 
    {
    // Set the advanced SSE capabilities to not available.
    this->Features.HasSSEFP = false;
    }

  // Retrieve Intel specific extended features.
  if (this->ChipManufacturer == Intel) 
    {
Bill Hoffman's avatar
Bill Hoffman committed
1005
1006
    this->Features.ExtendedFeatures.SupportsHyperthreading =  ((localCPUFeatures &  0x10000000) != 0);  // Intel specific: Hyperthreading --> Bit 28
    this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = (this->Features.ExtendedFeatures.SupportsHyperthreading) ? ((localCPUAdvanced & 0x00FF0000) >> 16) : 1;
1007
1008
1009
1010
    
    if ((this->Features.ExtendedFeatures.SupportsHyperthreading) && (this->Features.HasAPIC))
      {
      // Retrieve APIC information if there is one present.
Bill Hoffman's avatar
Bill Hoffman committed
1011
      this->Features.ExtendedFeatures.APIC_ID = ((localCPUAdvanced & 0xFF000000) >> 24);
1012
1013
      }
    }
1014

1015
  return true;
1016
1017
1018
1019

#else
  return false;
#endif
1020
1021
1022
1023
}


/** Find the manufacturer given the vendor id */
1024
void SystemInformationImplementation::FindManufacturer()
1025
{
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
  if (this->ChipID.Vendor == "GenuineIntel")       this->ChipManufacturer = Intel;        // Intel Corp.
  else if (this->ChipID.Vendor == "UMC UMC UMC ")  this->ChipManufacturer = UMC;          // United Microelectronics Corp.
  else if (this->ChipID.Vendor == "AuthenticAMD")  this->ChipManufacturer = AMD;          // Advanced Micro Devices
  else if (this->ChipID.Vendor == "AMD ISBETTER")  this->ChipManufacturer = AMD;          // Advanced Micro Devices (1994)
  else if (this->ChipID.Vendor == "CyrixInstead")  this->ChipManufacturer = Cyrix;        // Cyrix Corp., VIA Inc.
  else if (this->ChipID.Vendor == "NexGenDriven")  this->ChipManufacturer = NexGen;        // NexGen Inc. (now AMD)
  else if (this->ChipID.Vendor == "CentaurHauls")  this->ChipManufacturer = IDT;          // IDT/Centaur (now VIA)
  else if (this->ChipID.Vendor == "RiseRiseRise")  this->ChipManufacturer = Rise;        // Rise
  else if (this->ChipID.Vendor == "GenuineTMx86")  this->ChipManufacturer = Transmeta;      // Transmeta
  else if (this->ChipID.Vendor == "TransmetaCPU")  this->ChipManufacturer = Transmeta;      // Transmeta
  else if (this->ChipID.Vendor == "Geode By NSC")  this->ChipManufacturer = NSC;          // National Semiconductor
  else if (this->ChipID.Vendor == "Sun")           this->ChipManufacturer = Sun;          // Sun Microelectronics
  else if (this->ChipID.Vendor == "IBM")           this->ChipManufacturer = IBM;          // IBM Microelectronics
  else if (this->ChipID.Vendor == "Motorola")      this->ChipManufacturer = Motorola;          // Motorola Microelectronics
  else                                             this->ChipManufacturer = UnknownManufacturer;  // Unknown manufacturer
1041
1042
}

1043

1044
/** */
1045
bool SystemInformationImplementation::RetrieveCPUIdentity()
1046
{
1047
#if USE_ASM_INSTRUCTIONS
Bill Hoffman's avatar
Bill Hoffman committed
1048
  int localCPUVendor[3];
Bill Hoffman's avatar
Bill Hoffman committed
1049
  int localCPUSignature;
Bill Hoffman's avatar
Bill Hoffman committed
1050

1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
  // Use assembly to detect CPUID information...
  __try 
    {
    _asm 
      {
#ifdef CPUID_AWARE_COMPILER
       ; we must push/pop the registers <<CPUID>> writes to, as the
      ; optimiser doesn't know about <<CPUID>>, and so doesn't expect
      ; these registers to change.
      push eax
      push ebx
      push ecx
      push edx
#endif
      ; <<CPUID>>
      ; eax = 0 --> eax: maximum value of CPUID instruction.
      ;        ebx: part 1 of 3; CPU signature.
      ;        edx: part 2 of 3; CPU signature.
      ;        ecx: part 3 of 3; CPU signature.
      mov eax, 0
      CPUID_INSTRUCTION
Bill Hoffman's avatar
Bill Hoffman committed
1072
1073
1074
      mov localCPUVendor[0 * TYPE int], ebx
      mov localCPUVendor[1 * TYPE int], edx
      mov localCPUVendor[2 * TYPE int], ecx
1075
1076
1077
1078
1079
1080
1081

      ; <<CPUID>> 
      ; eax = 1 --> eax: CPU ID - bits 31..16 - unused, bits 15..12 - type, bits 11..8 - family, bits 7..4 - model, bits 3..0 - mask revision
      ;        ebx: 31..24 - default APIC ID, 23..16 - logical processsor ID, 15..8 - CFLUSH chunk size , 7..0 - brand ID
      ;        edx: CPU feature flags
      mov eax,1
      CPUID_INSTRUCTION
Bill Hoffman's avatar
Bill Hoffman committed
1082
      mov localCPUSignature, eax
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097

#ifdef CPUID_AWARE_COMPILER
      pop edx
      pop ecx
      pop ebx
      pop eax
#endif
    }
  }
  __except(1) 
    {
    return false;
    }

  // Process the returned information.
1098
1099
1100
1101
1102
1103
  char vbuf[13];
  memcpy (&(vbuf[0]), &(localCPUVendor[0]), sizeof (int));
  memcpy (&(vbuf[4]), &(localCPUVendor[1]), sizeof (int));
  memcpy (&(vbuf[8]), &(localCPUVendor[2]), sizeof (int));
  vbuf[12] = '\0';
  this->ChipID.Vendor = vbuf;
1104
1105
1106
1107

  this->FindManufacturer();

  // Retrieve the family of CPU present.
Bill Hoffman's avatar
Bill Hoffman committed
1108
1109
1110
1111
1112
1113
  this->ChipID.ExtendedFamily =    ((localCPUSignature & 0x0FF00000) >> 20);  // Bits 27..20 Used
  this->ChipID.ExtendedModel =    ((localCPUSignature & 0x000F0000) >> 16);  // Bits 19..16 Used
  this->ChipID.Type =        ((localCPUSignature & 0x0000F000) >> 12);  // Bits 15..12 Used
  this->ChipID.Family =        ((localCPUSignature & 0x00000F00) >> 8);    // Bits 11..8 Used
  this->ChipID.Model =        ((localCPUSignature & 0x000000F0) >> 4);    // Bits 7..4 Used
  this->ChipID.Revision =      ((localCPUSignature & 0x0000000F) >> 0);    // Bits 3..0 Used
1114
1115

  return true;
1116
1117
1118
1119

#else
  return false;
#endif
1120
1121
}

1122

1123
/** */
1124
bool SystemInformationImplementation::RetrieveCPUCacheDetails()
1125
{
1126
#if USE_ASM_INSTRUCTIONS
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
  int L1Cache[4] = { 0, 0, 0, 0 };
  int L2Cache[4] = { 0, 0, 0, 0 };

  // Check to see if what we are about to do is supported...
  if (RetrieveCPUExtendedLevelSupport (0x80000005)) 
    {
    // Use assembly to retrieve the L1 cache information ...
    __try 
      {
      _asm 
        {
#ifdef CPUID_AWARE_COMPILER
         ; we must push/pop the registers <<CPUID>> writes to, as the
        ; optimiser doesn't know about <<CPUID>>, and so doesn't expect
        ; these registers to change.
        push eax
        push ebx
        push ecx
        push edx
#endif
        ; <<CPUID>>
        ; eax = 0x80000005 --> eax: L1 cache information - Part 1 of 4.
        ;             ebx: L1 cache information - Part 2 of 4.
        ;             edx: L1 cache information - Part 3 of 4.
        ;              ecx: L1 cache information - Part 4 of 4.
        mov eax, 0x80000005
        CPUID_INSTRUCTION
        mov L1Cache[0 * TYPE int], eax
        mov L1Cache[1 * TYPE int], ebx
        mov L1Cache[2 * TYPE int], ecx
        mov L1Cache[3 * TYPE int], edx

#ifdef CPUID_AWARE_COMPILER
        pop edx
        pop ecx
        pop ebx
        pop eax
#endif
        }
      }
    __except(1) 
      {
      return false;
      }
    // Save the L1 data cache size (in KB) from ecx: bits 31..24 as well as data cache size from edx: bits 31..24.
    this->Features.L1CacheSize = ((L1Cache[2] & 0xFF000000) >> 24);
    this->Features.L1CacheSize += ((L1Cache[3] & 0xFF000000) >> 24);
    } 
  else 
    {
    // Store -1 to indicate the cache could not be queried.
    this->Features.L1CacheSize = -1;
    }

  // Check to see if what we are about to do is supported...
  if (RetrieveCPUExtendedLevelSupport (0x80000006)) 
    {
    // Use assembly to retrieve the L2 cache information ...
    __try 
      {
      _asm 
        {
#ifdef CPUID_AWARE_COMPILER
         ; we must push/pop the registers <<CPUID>> writes to, as the
        ; optimiser doesn't know about <<CPUID>>, and so doesn't expect
        ; these registers to change.
        push eax
        push ebx
        push ecx
        push edx
#endif
        ; <<CPUID>>
        ; eax = 0x80000006 --> eax: L2 cache information - Part 1 of 4.
        ;             ebx: L2 cache information - Part 2 of 4.
        ;             edx: L2 cache information - Part 3 of 4.
        ;              ecx: L2 cache information - Part 4 of 4.
        mov eax, 0x80000006
        CPUID_INSTRUCTION
        mov L2Cache[0 * TYPE int], eax
        mov L2Cache[1 * TYPE int], ebx
        mov L2Cache[2 * TYPE int], ecx
        mov L2Cache[3 * TYPE int], edx

#ifdef CPUID_AWARE_COMPILER
        pop edx
        pop ecx
        pop ebx
        pop eax
#endif
        }
      }
    __except(1) 
      {
      return false;
      }
    // Save the L2 unified cache size (in KB) from ecx: bits 31..16.
    this->Features.L2CacheSize = ((L2Cache[2] & 0xFFFF0000) >> 16);
    } 
  else
    {
    // Store -1 to indicate the cache could not be queried.
    this->Features.L2CacheSize = -1;
    }
  
  // Define L3 as being not present as we cannot test for it.
  this->Features.L3CacheSize = -1;

#endif

  // Return failure if we cannot detect either cache with this method.
  return ((this->Features.L1CacheSize == -1) && (this->Features.L2CacheSize == -1)) ? false : true;
}

1240

1241
/** */
1242
bool SystemInformationImplementation::RetrieveClassicalCPUCacheDetails()
1243
{
1244
#if USE_ASM_INSTRUCTIONS
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
  int TLBCode = -1, TLBData = -1, L1Code = -1, L1Data = -1, L1Trace = -1, L2Unified = -1, L3Unified = -1;
  int TLBCacheData[4] = { 0, 0, 0, 0 };
  int TLBPassCounter = 0;
  int TLBCacheUnit = 0;


  do {
    // Use assembly to retrieve the L2 cache information ...
    __try {
      _asm {
#ifdef CPUID_AWARE_COMPILER
         ; we must push/pop the registers <<CPUID>> writes to, as the
        ; optimiser doesn't know about <<CPUID>>, and so doesn't expect
        ; these registers to change.
        push eax
        push ebx
        push ecx
        push edx
#endif
        ; <<CPUID>>
        ; eax = 2 --> eax: TLB and cache information - Part 1 of 4.
        ;        ebx: TLB and cache information - Part 2 of 4.
        ;        ecx: TLB and cache information - Part 3 of 4.
        ;        edx: TLB and cache information - Part 4 of 4.
        mov eax, 2
        CPUID_INSTRUCTION
        mov TLBCacheData[0 * TYPE int], eax
        mov TLBCacheData[1 * TYPE int], ebx
        mov TLBCacheData[2 * TYPE int], ecx
        mov TLBCacheData[3 * TYPE int], edx

#ifdef CPUID_AWARE_COMPILER
        pop edx
        pop ecx
        pop ebx
        pop eax
#endif
        }
      }
    __except(1)
      {
      return false;
      }

    int bob = ((TLBCacheData[0] & 0x00FF0000) >> 16);
    (void)bob;
    // Process the returned TLB and cache information.
    for (int nCounter = 0; nCounter < TLBCACHE_INFO_UNITS; nCounter ++) 
      {
      // First of all - decide which unit we are dealing with.
      switch (nCounter) 
        {
        // eax: bits 8..15 : bits 16..23 : bits 24..31
        case 0: TLBCacheUnit = ((TLBCacheData[0] & 0x0000FF00) >> 8); break;
        case 1: TLBCacheUnit = ((TLBCacheData[0] & 0x00FF0000) >> 16); break;
        case 2: TLBCacheUnit = ((TLBCacheData[0] & 0xFF000000) >> 24); break;

        // ebx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31
        case 3: TLBCacheUnit = ((TLBCacheData[1] & 0x000000FF) >> 0); break;
        case 4: TLBCacheUnit = ((TLBCacheData[1] & 0x0000FF00) >> 8); break;
        case 5: TLBCacheUnit = ((TLBCacheData[1] & 0x00FF0000) >> 16); break;
        case 6: TLBCacheUnit = ((TLBCacheData[1] & 0xFF000000) >> 24); break;

        // ecx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31
        case 7: TLBCacheUnit = ((TLBCacheData[2] & 0x000000FF) >> 0); break;
        case 8: TLBCacheUnit = ((TLBCacheData[2] & 0x0000FF00) >> 8); break;
        case 9: TLBCacheUnit = ((TLBCacheData[2] & 0x00FF0000) >> 16); break;
        case 10: TLBCacheUnit = ((TLBCacheData[2] & 0xFF000000) >> 24); break;

        // edx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31
        case 11: TLBCacheUnit = ((TLBCacheData[3] & 0x000000FF) >> 0); break;
        case 12: TLBCacheUnit = ((TLBCacheData[3] & 0x0000FF00) >> 8); break;
        case 13: TLBCacheUnit = ((TLBCacheData[3] & 0x00FF0000) >> 16); break;
        case 14: TLBCacheUnit = ((TLBCacheData[3] & 0xFF000000) >> 24); break;

        // Default case - an error has occured.
        default: return false;
        }

      // Now process the resulting unit to see what it means....
      switch (TLBCacheUnit) 
        {
        case 0x00: break;
        case 0x01: STORE_TLBCACHE_INFO (TLBCode, 4); break;
        case 0x02: STORE_TLBCACHE_INFO (TLBCode, 4096); break;
        case 0x03: STORE_TLBCACHE_INFO (TLBData, 4); break;
        case 0x04: STORE_TLBCACHE_INFO (TLBData, 4096); break;
        case 0x06: STORE_TLBCACHE_INFO (L1Code, 8); break;
        case 0x08: STORE_TLBCACHE_INFO (L1Code, 16); break;
        case 0x0a: STORE_TLBCACHE_INFO (L1Data, 8); break;
        case 0x0c: STORE_TLBCACHE_INFO (L1Data, 16); break;
        case 0x10: STORE_TLBCACHE_INFO (L1Data, 16); break;      // <-- FIXME: IA-64 Only
        case 0x15: STORE_TLBCACHE_INFO (L1Code, 16); break;      // <-- FIXME: IA-64 Only
        case 0x1a: STORE_TLBCACHE_INFO (L2Unified, 96); break;    // <-- FIXME: IA-64 Only
        case 0x22: STORE_TLBCACHE_INFO (L3Unified, 512); break;
        case 0x23: STORE_TLBCACHE_INFO (L3Unified, 1024); break;
        case 0x25: STORE_TLBCACHE_INFO (L3Unified, 2048); break;
        case 0x29: STORE_TLBCACHE_INFO (L3Unified, 4096); break;
        case 0x39: STORE_TLBCACHE_INFO (L2Unified, 128); break;
        case 0x3c: STORE_TLBCACHE_INFO (L2Unified, 256); break;
        case 0x40: STORE_TLBCACHE_INFO (L2Unified, 0); break;    // <-- FIXME: No integrated L2 cache (P6 core) or L3 cache (P4 core).
        case 0x41: STORE_TLBCACHE_INFO (L2Unified, 128); break;
        case 0x42: STORE_TLBCACHE_INFO (L2Unified, 256); break;
        case 0x43: STORE_TLBCACHE_INFO (L2Unified, 512); break;
        case 0x44: STORE_TLBCACHE_INFO (L2Unified, 1024); break;
        case 0x45: STORE_TLBCACHE_INFO (L2Unified, 2048); break;
        case 0x50: STORE_TLBCACHE_INFO (TLBCode, 4096); break;
        case 0x51: STORE_TLBCACHE_INFO (TLBCode, 4096); break;
        case 0x52: STORE_TLBCACHE_INFO (TLBCode, 4096); break;
        case 0x5b: STORE_TLBCACHE_INFO (TLBData, 4096); break;
        case 0x5c: STORE_TLBCACHE_INFO (TLBData, 4096); break;
        case 0x5d: STORE_TLBCACHE_INFO (TLBData, 4096); break;
        case 0x66: STORE_TLBCACHE_INFO (L1Data, 8); break;
        case 0x67: STORE_TLBCACHE_INFO (L1Data, 16); break;
        case 0x68: STORE_TLBCACHE_INFO (L1Data, 32); break;
        case 0x70: STORE_TLBCACHE_INFO (L1Trace, 12); break;
        case 0x71: STORE_TLBCACHE_INFO (L1Trace, 16); break;
        case 0x72: STORE_TLBCACHE_INFO (L1Trace, 32); break;
        case 0x77: STORE_TLBCACHE_INFO (L1Code, 16); break;      // <-- FIXME: IA-64 Only
        case 0x79: STORE_TLBCACHE_INFO (L2Unified, 128); break;
        case 0x7a: STORE_TLBCACHE_INFO (L2Unified, 256); break;
        case 0x7b: STORE_TLBCACHE_INFO (L2Unified, 512); break;
        case 0x7c: STORE_TLBCACHE_INFO (L2Unified, 1024); break;
        case 0x7e: STORE_TLBCACHE_INFO (L2Unified, 256); break;
        case 0x81: STORE_TLBCACHE_INFO (L2Unified, 128); break;
        case 0x82: STORE_TLBCACHE_INFO (L2Unified, 256); break;
        case 0x83: STORE_TLBCACHE_INFO (L2Unified, 512); break;
        case 0x84: STORE_TLBCACHE_INFO (L2Unified, 1024); break;
        case 0x85: STORE_TLBCACHE_INFO (L2Unified, 2048); break;
        case 0x88: STORE_TLBCACHE_INFO (L3Unified, 2048); break;  // <-- FIXME: IA-64 Only
        case 0x89: STORE_TLBCACHE_INFO (L3Unified, 4096); break;  // <-- FIXME: IA-64 Only
        case 0x8a: STORE_TLBCACHE_INFO (L3Unified, 8192); break;  // <-- FIXME: IA-64 Only
        case 0x8d: STORE_TLBCACHE_INFO (L3Unified, 3096); break;  // <-- FIXME: IA-64 Only
        case 0x90: STORE_TLBCACHE_INFO (TLBCode, 262144); break;  // <-- FIXME: IA-64 Only
        case 0x96: STORE_TLBCACHE_INFO (TLBCode, 262144); break;  // <-- FIXME: IA-64 Only
        case 0x9b: STORE_TLBCACHE_INFO (TLBCode, 262144); break;  // <-- FIXME: IA-64 Only
        
        // Default case - an error has occured.
        default: return false;
        }
      }

    // Increment the TLB pass counter.
    TLBPassCounter ++;
    } while ((TLBCacheData[0] & 0x000000FF) > TLBPassCounter);

  // Ok - we now have the maximum TLB, L1, L2, and L3 sizes...
  if ((L1Code == -1) && (L1Data == -1) && (L1Trace == -1)) 
    {
    this->Features.L1CacheSize = -1;
    }
  else if ((L1Code == -1) && (L1Data == -1) && (L1Trace != -1)) 
    {
    this->Features.L1CacheSize = L1Trace;
    }
  else if ((L1Code != -1) && (L1Data == -1)) 
    {
    this->Features.L1CacheSize = L1Code;
    }
  else if ((L1Code == -1) && (L1Data != -1)) 
    {
    this->Features.L1CacheSize = L1Data;
    }
  else if ((L1Code != -1) && (L1Data != -1)) 
    {
    this->Features.L1CacheSize = L1Code + L1Data;
    }
  else 
    {
    this->Features.L1CacheSize = -1;
    }

  // Ok - we now have the maximum TLB, L1, L2, and L3 sizes...
  if (L2Unified == -1) 
    {
    this->Features.L2CacheSize = -1;
    }
  else 
    {
    this->Features.L2CacheSize = L2Unified;
    }

  // Ok - we now have the maximum TLB, L1, L2, and L3 sizes...
  if (L3Unified == -1) 
    {
    this->Features.L3CacheSize = -1;
    }
  else 
    {
    this->Features.L3CacheSize = L3Unified;
    }

  return true;
1438
1439
1440
1441

#else
  return false;
#endif
1442
1443
}

1444

1445
/** */
1446
bool SystemInformationImplementation::RetrieveCPUClockSpeed()
1447
{
1448
1449
  bool retrieved = false;

1450
#if _WIN32
1451
1452
1453
1454
1455
  // First of all we check to see if the RDTSC (0x0F, 0x31) instruction is
  // supported. If not, we fallback to trying to read this value from the
  // registry:
  //
  if (!this->Features.HasTSC)
1456
    {
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
    HKEY hKey = NULL;
    LONG err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
      "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
      KEY_READ, &hKey);

    if (ERROR_SUCCESS == err)
      {
      DWORD dwType = 0;
      DWORD data = 0;
      DWORD dwSize = sizeof(DWORD);

      err = RegQueryValueEx(hKey, "~MHz", 0,
        &dwType, (LPBYTE) &data, &dwSize);

      if (ERROR_SUCCESS == err)
        {
        this->CPUSpeedInMHz = (float) data;
        retrieved = true;
        }

      RegCloseKey(hKey);
      hKey = NULL;
      }

    return retrieved;
1482
1483
1484
1485
1486
1487
1488
    }

  unsigned int uiRepetitions = 1;
  unsigned int uiMSecPerRepetition = 50;
  __int64  i64Total = 0;
  __int64 i64Overhead = 0;

1489
  for (unsigned int nCounter = 0; nCounter < uiRepetitions; nCounter ++)
1490
    {
1491
1492
1493
1494
1495
    i64Total += GetCyclesDifference (SystemInformationImplementation::Delay,
                                     uiMSecPerRepetition);
    i64Overhead +=
      GetCyclesDifference (SystemInformationImplementation