Hardware/CPU/GenericCPU.cs
author moel.mich
Sun, 23 Sep 2012 18:37:43 +0000
changeset 380 573f1fff48b2
parent 298 96263190189a
permissions -rw-r--r--
Fixed Issue 387. The new implementation does not try to start a ring 0 driver that already exists, but could not be opened. It tries to delete the driver and install it new. The driver is now stored temporarily in the application folder. The driver is not correctly removed on system shutdown.
     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 }