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