Hardware/CPU/GenericCPU.cs
author Stephane Lenclud
Sat, 30 Jan 2016 23:01:51 +0100
branchMiniDisplay
changeset 454 f84878f52cd9
parent 298 96263190189a
permissions -rw-r--r--
Disabling Nuvoton NCT6791D because of fan full speed bug on Asus Z97 WS.
     1 /*
     2  
     3   This Source Code Form is subject to the terms of the Mozilla Public
     4   License, v. 2.0. If a copy of the MPL was not distributed with this
     5   file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7   Copyright (C) 2010-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.Collections.Generic;
    13 using System.Diagnostics;
    14 using System.Globalization;
    15 using System.Runtime.InteropServices;
    16 using System.Text;
    17 using System.Threading;
    18 
    19 namespace OpenHardwareMonitor.Hardware.CPU {
    20   internal class GenericCPU : Hardware {
    21 
    22     protected readonly CPUID[][] cpuid;
    23    
    24     protected readonly uint family;
    25     protected readonly uint model;
    26     protected readonly uint stepping;
    27 
    28     protected readonly int processorIndex;
    29     protected readonly int coreCount;
    30 
    31     private readonly bool hasModelSpecificRegisters;
    32 
    33     private readonly bool hasTimeStampCounter;
    34     private readonly bool isInvariantTimeStampCounter;
    35     private readonly double estimatedTimeStampCounterFrequency;
    36     private readonly double estimatedTimeStampCounterFrequencyError;
    37 
    38     private ulong lastTimeStampCount;
    39     private long lastTime;
    40     private double timeStampCounterFrequency;
    41     
    42 
    43     private readonly Vendor vendor;
    44 
    45     private readonly CPULoad cpuLoad;
    46     private readonly Sensor totalLoad;
    47     private readonly Sensor[] coreLoads;
    48 
    49     protected string CoreString(int i) {
    50       if (coreCount == 1)
    51         return "CPU Core";
    52       else
    53         return "CPU Core #" + (i + 1);
    54     }
    55 
    56     public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    57       : base(cpuid[0][0].Name, CreateIdentifier(cpuid[0][0].Vendor, 
    58       processorIndex), settings)
    59     {
    60       this.cpuid = cpuid;
    61 
    62       this.vendor = cpuid[0][0].Vendor;
    63 
    64       this.family = cpuid[0][0].Family;
    65       this.model = cpuid[0][0].Model;
    66       this.stepping = cpuid[0][0].Stepping;
    67 
    68       this.processorIndex = processorIndex;
    69       this.coreCount = cpuid.Length;  
    70   
    71       // check if processor has MSRs
    72       if (cpuid[0][0].Data.GetLength(0) > 1
    73         && (cpuid[0][0].Data[1, 3] & 0x20) != 0)
    74         hasModelSpecificRegisters = true;
    75       else
    76         hasModelSpecificRegisters = false;
    77 
    78       // check if processor has a TSC
    79       if (cpuid[0][0].Data.GetLength(0) > 1
    80         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
    81         hasTimeStampCounter = true;
    82       else
    83         hasTimeStampCounter = false;
    84 
    85       // check if processor supports an invariant TSC 
    86       if (cpuid[0][0].ExtData.GetLength(0) > 7
    87         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
    88         isInvariantTimeStampCounter = true;
    89       else
    90         isInvariantTimeStampCounter = false;
    91 
    92       if (coreCount > 1)
    93         totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
    94       else
    95         totalLoad = null;
    96       coreLoads = new Sensor[coreCount];
    97       for (int i = 0; i < coreLoads.Length; i++)
    98         coreLoads[i] = new Sensor(CoreString(i), i + 1,
    99           SensorType.Load, this, settings);
   100       cpuLoad = new CPULoad(cpuid);
   101       if (cpuLoad.IsAvailable) {
   102         foreach (Sensor sensor in coreLoads)
   103           ActivateSensor(sensor);
   104         if (totalLoad != null)
   105           ActivateSensor(totalLoad);
   106       }
   107 
   108       if (hasTimeStampCounter) {
   109         ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
   110 
   111         EstimateTimeStampCounterFrequency(
   112           out estimatedTimeStampCounterFrequency, 
   113           out estimatedTimeStampCounterFrequencyError);  
   114         
   115         ThreadAffinity.Set(mask);
   116       } else {
   117         estimatedTimeStampCounterFrequency = 0;
   118       }
   119 
   120       timeStampCounterFrequency = estimatedTimeStampCounterFrequency;                  
   121     }
   122 
   123     private static Identifier CreateIdentifier(Vendor vendor,
   124       int processorIndex) 
   125     {
   126       string s;
   127       switch (vendor) {
   128         case Vendor.AMD: s = "amdcpu"; break;
   129         case Vendor.Intel: s = "intelcpu"; break;
   130         default: s = "genericcpu"; break;
   131       }
   132       return new Identifier(s,
   133         processorIndex.ToString(CultureInfo.InvariantCulture));
   134     }
   135 
   136     private void EstimateTimeStampCounterFrequency(out double frequency, 
   137       out double error) 
   138   {     
   139       double f, e;
   140       
   141       // preload the function
   142       EstimateTimeStampCounterFrequency(0, out f, out e);
   143       EstimateTimeStampCounterFrequency(0, out f, out e);
   144 
   145       // estimate the frequency
   146       error = double.MaxValue;
   147       frequency = 0;
   148       for (int i = 0; i < 5; i++) {
   149         EstimateTimeStampCounterFrequency(0.025, out f, out e);
   150         if (e < error) {
   151           error = e;
   152           frequency = f;
   153         }
   154 
   155         if (error < 1e-4)
   156           break;
   157       }                
   158     }
   159 
   160     private void EstimateTimeStampCounterFrequency(double timeWindow, 
   161       out double frequency, out double error) 
   162     {
   163       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   164       ulong countBegin, countEnd;
   165 
   166       long timeBegin = Stopwatch.GetTimestamp() +
   167         (long)Math.Ceiling(0.001 * ticks);
   168       long timeEnd = timeBegin + ticks;
   169 
   170       while (Stopwatch.GetTimestamp() < timeBegin) { }
   171       countBegin = Opcode.Rdtsc();
   172       long afterBegin = Stopwatch.GetTimestamp();
   173 
   174       while (Stopwatch.GetTimestamp() < timeEnd) { }
   175       countEnd = Opcode.Rdtsc();
   176       long afterEnd = Stopwatch.GetTimestamp();
   177 
   178       double delta = (timeEnd - timeBegin);
   179       frequency = 1e-6 * 
   180         (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / delta;
   181 
   182       double beginError = (afterBegin - timeBegin) / delta;
   183       double endError = (afterEnd - timeEnd) / delta;
   184       error = beginError + endError;
   185     }
   186 
   187 
   188     private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
   189       uint eax, edx;
   190       if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
   191         r.Append(" ");
   192         r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
   193         r.Append("  ");
   194         r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
   195         r.Append("  ");
   196         r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
   197         r.AppendLine();
   198       }
   199     }
   200 
   201     protected virtual uint[] GetMSRs() {
   202       return null;
   203     }
   204 
   205     public override string GetReport() {
   206       StringBuilder r = new StringBuilder();
   207 
   208       switch (vendor) {
   209         case Vendor.AMD: r.AppendLine("AMD CPU"); break;
   210         case Vendor.Intel: r.AppendLine("Intel CPU"); break;
   211         default: r.AppendLine("Generic CPU"); break;
   212       }
   213 
   214       r.AppendLine();
   215       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   216       r.AppendFormat("Number of Cores: {0}{1}", coreCount,
   217         Environment.NewLine);
   218       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   219         Environment.NewLine);
   220       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   221         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   222       r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
   223         isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
   224       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   225         "Estimated Time Stamp Counter Frequency: {0} MHz",
   226         Math.Round(estimatedTimeStampCounterFrequency * 100) * 0.01));
   227       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   228         "Estimated Time Stamp Counter Frequency Error: {0} Mhz",
   229         Math.Round(estimatedTimeStampCounterFrequency *
   230         estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5));
   231       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   232         "Time Stamp Counter Frequency: {0} MHz",
   233         Math.Round(timeStampCounterFrequency * 100) * 0.01));   
   234       r.AppendLine();
   235 
   236       uint[] msrArray = GetMSRs();
   237       if (msrArray != null && msrArray.Length > 0) {
   238         for (int i = 0; i < cpuid.Length; i++) {
   239           r.AppendLine("MSR Core #" + (i + 1));
   240           r.AppendLine();
   241           r.AppendLine(" MSR       EDX       EAX");
   242           foreach (uint msr in msrArray)
   243             AppendMSRData(r, msr, cpuid[i][0].Thread);
   244           r.AppendLine();
   245         }
   246       }
   247 
   248       return r.ToString();
   249     }
   250 
   251     public override HardwareType HardwareType {
   252       get { return HardwareType.CPU; }
   253     }
   254 
   255     public bool HasModelSpecificRegisters {
   256       get { return hasModelSpecificRegisters; }
   257     }
   258 
   259     public bool HasTimeStampCounter {
   260       get { return hasTimeStampCounter; }
   261     }
   262 
   263     public double TimeStampCounterFrequency {
   264       get { return timeStampCounterFrequency; }
   265     }
   266 
   267     public override void Update() {
   268       if (hasTimeStampCounter && isInvariantTimeStampCounter) {
   269 
   270         // make sure always the same thread is used
   271         ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
   272 
   273         // read time before and after getting the TSC to estimate the error
   274         long firstTime = Stopwatch.GetTimestamp();
   275         ulong timeStampCount = Opcode.Rdtsc();
   276         long time = Stopwatch.GetTimestamp();
   277 
   278         // restore the thread affinity mask
   279         ThreadAffinity.Set(mask);
   280 
   281         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   282         double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
   283 
   284         // only use data if they are measured accuarte enough (max 0.1ms delay)
   285         if (error < 0.0001) {
   286 
   287           // ignore the first reading because there are no initial values 
   288           // ignore readings with too large or too small time window
   289           if (lastTime != 0 && delta > 0.5 && delta < 2) {
   290 
   291             // update the TSC frequency with the new value
   292             timeStampCounterFrequency =
   293               (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   294           }
   295 
   296           lastTimeStampCount = timeStampCount;
   297           lastTime = time;
   298         }        
   299       }
   300 
   301       if (cpuLoad.IsAvailable) {
   302         cpuLoad.Update();
   303         for (int i = 0; i < coreLoads.Length; i++)
   304           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   305         if (totalLoad != null)
   306           totalLoad.Value = cpuLoad.GetTotalLoad();
   307       }
   308     }
   309   }
   310 }