Hardware/CPU/GenericCPU.cs
author moel.mich
Thu, 14 Oct 2010 17:30:51 +0000
changeset 222 ba64bb91ebe4
parent 203 ca487ba88c24
child 236 763675f19ff4
permissions -rw-r--r--
Improved the invariant TSC frequency estimation code to ignore readings with a large error.
     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.Text;
    43 using System.Threading;
    44 
    45 namespace OpenHardwareMonitor.Hardware.CPU {
    46   internal class GenericCPU : Hardware {
    47 
    48     protected readonly CPUID[][] cpuid;
    49    
    50     protected readonly uint family;
    51     protected readonly uint model;
    52     protected readonly uint stepping;
    53 
    54     protected readonly int processorIndex;
    55     protected readonly int coreCount;
    56     protected readonly string name;
    57 
    58     private readonly bool hasTimeStampCounter;
    59     private readonly bool isInvariantTimeStampCounter;
    60     private readonly double estimatedTimeStampCounterFrequency;
    61 
    62     private ulong lastTimeStampCount;
    63     private long lastTime;
    64     private double timeStampCounterFrequency;    
    65 
    66     private readonly Vendor vendor;
    67 
    68     private readonly CPULoad cpuLoad;
    69     private readonly Sensor totalLoad;
    70     private readonly Sensor[] coreLoads;
    71 
    72     protected string CoreString(int i) {
    73       if (coreCount == 1)
    74         return "CPU Core";
    75       else
    76         return "CPU Core #" + (i + 1);
    77     }
    78 
    79     public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings) {
    80       this.cpuid = cpuid;
    81 
    82       this.vendor = cpuid[0][0].Vendor;
    83 
    84       this.family = cpuid[0][0].Family;
    85       this.model = cpuid[0][0].Model;
    86       this.stepping = cpuid[0][0].Stepping;
    87 
    88       this.processorIndex = processorIndex;
    89       this.coreCount = cpuid.Length;
    90       this.name = cpuid[0][0].Name;      
    91 
    92       // check if processor has a TSC
    93       if (cpuid[0][0].Data.GetLength(0) > 1
    94         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
    95         hasTimeStampCounter = true;
    96       else
    97         hasTimeStampCounter = false;
    98 
    99       // check if processor supports an invariant TSC 
   100       if (cpuid[0][0].ExtData.GetLength(0) > 7
   101         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
   102         isInvariantTimeStampCounter = true;
   103       else
   104         isInvariantTimeStampCounter = false;
   105 
   106       if (coreCount > 1)
   107         totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
   108       else
   109         totalLoad = null;
   110       coreLoads = new Sensor[coreCount];
   111       for (int i = 0; i < coreLoads.Length; i++)
   112         coreLoads[i] = new Sensor(CoreString(i), i + 1,
   113           SensorType.Load, this, settings);
   114       cpuLoad = new CPULoad(cpuid);
   115       if (cpuLoad.IsAvailable) {
   116         foreach (Sensor sensor in coreLoads)
   117           ActivateSensor(sensor);
   118         if (totalLoad != null)
   119           ActivateSensor(totalLoad);
   120       }
   121 
   122       if (hasTimeStampCounter) {
   123         estimatedTimeStampCounterFrequency = 
   124           EstimateTimeStampCounterFrequency();        
   125       } else {
   126         estimatedTimeStampCounterFrequency = 0;
   127       }
   128 
   129       timeStampCounterFrequency = estimatedTimeStampCounterFrequency;                  
   130     }
   131 
   132     private static double EstimateTimeStampCounterFrequency() {
   133       // preload the function
   134       EstimateTimeStampCounterFrequency(0);
   135       EstimateTimeStampCounterFrequency(0);
   136 
   137       // estimate the frequency in MHz      
   138       List<double> estimatedFrequency = new List<double>(3);
   139       for (int i = 0; i < 3; i++)
   140         estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
   141       estimatedFrequency.Sort();
   142       return estimatedFrequency[1];
   143     }
   144 
   145     private static double EstimateTimeStampCounterFrequency(double timeWindow) {
   146       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   147       uint lsbBegin, msbBegin, lsbEnd, msbEnd;
   148 
   149       Thread.BeginThreadAffinity();
   150       long timeBegin = Stopwatch.GetTimestamp() +
   151         (long)Math.Ceiling(0.001 * ticks);
   152       long timeEnd = timeBegin + ticks;
   153       while (Stopwatch.GetTimestamp() < timeBegin) { }
   154       WinRing0.Rdtsc(out lsbBegin, out msbBegin);
   155       while (Stopwatch.GetTimestamp() < timeEnd) { }
   156       WinRing0.Rdtsc(out lsbEnd, out msbEnd);
   157       Thread.EndThreadAffinity();
   158 
   159       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   160       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   161 
   162       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   163         (timeEnd - timeBegin);
   164     }
   165 
   166     private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
   167       uint eax, edx;
   168       if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
   169         r.Append(" ");
   170         r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
   171         r.Append("  ");
   172         r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
   173         r.Append("  ");
   174         r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
   175         r.AppendLine();
   176       }
   177     }
   178 
   179     protected virtual uint[] GetMSRs() {
   180       return null;
   181     }
   182 
   183     public override string GetReport() {
   184       StringBuilder r = new StringBuilder();
   185 
   186       switch (vendor) {
   187         case Vendor.AMD: r.AppendLine("AMD CPU"); break;
   188         case Vendor.Intel: r.AppendLine("Intel CPU"); break;
   189         default: r.AppendLine("Generic CPU"); break;
   190       }
   191 
   192       r.AppendLine();
   193       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   194       r.AppendFormat("Number of Cores: {0}{1}", coreCount,
   195         Environment.NewLine);
   196       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   197         Environment.NewLine);
   198       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   199         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   200       r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
   201         isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
   202       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   203         "Time Stamp Counter Frequency: {0} MHz",
   204         Math.Round(timeStampCounterFrequency * 100) * 0.01));   
   205       r.AppendLine();
   206 
   207       uint[] msrArray = GetMSRs();
   208       if (msrArray != null && msrArray.Length > 0) {
   209         for (int i = 0; i < cpuid.Length; i++) {
   210           r.AppendLine("MSR Core #" + (i + 1));
   211           r.AppendLine();
   212           r.AppendLine(" MSR       EDX       EAX");
   213           foreach (uint msr in msrArray)
   214             AppendMSRData(r, msr, cpuid[i][0].Thread);
   215           r.AppendLine();
   216         }
   217       }
   218 
   219       return r.ToString();
   220     }
   221 
   222     public override Identifier Identifier {
   223       get {
   224         string s;
   225         switch (vendor) {
   226           case Vendor.AMD: s = "amdcpu"; break;
   227           case Vendor.Intel: s = "intelcpu"; break;
   228           default: s = "genericcpu"; break;
   229         }
   230         return new Identifier(s, 
   231           processorIndex.ToString(CultureInfo.InvariantCulture));
   232       }
   233     }
   234 
   235     public override string Name {
   236       get { return name; }
   237     }
   238 
   239     public override HardwareType HardwareType {
   240       get { return HardwareType.CPU; }
   241     }
   242 
   243     public bool HasTimeStampCounter {
   244       get { return hasTimeStampCounter; }
   245     }
   246 
   247     public double TimeStampCounterFrequency {
   248       get { return timeStampCounterFrequency; }
   249     }
   250 
   251     public override void Update() {
   252       if (hasTimeStampCounter && isInvariantTimeStampCounter) {
   253         uint lsb, msb;
   254 
   255         // read time before and after getting the TSC to estimate the error
   256         long firstTime = Stopwatch.GetTimestamp();
   257         WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
   258         long time = Stopwatch.GetTimestamp();
   259 
   260         ulong timeStampCount = ((ulong)msb << 32) | lsb;
   261         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   262         double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
   263 
   264         // only use data if they are measured accuarte enough (max 0.1ms delay)
   265         if (error < 0.0001) {
   266 
   267           // ignore the first reading because there are no initial values 
   268           // ignore readings with too large or too small time window
   269           if (lastTime != 0 && delta > 0.5 && delta < 2) {
   270 
   271             // update the TSC frequency with the new value
   272             timeStampCounterFrequency =
   273               (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   274           }
   275 
   276           lastTimeStampCount = timeStampCount;
   277           lastTime = time;
   278         }        
   279       }
   280 
   281       if (cpuLoad.IsAvailable) {
   282         cpuLoad.Update();
   283         for (int i = 0; i < coreLoads.Length; i++)
   284           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   285         if (totalLoad != null)
   286           totalLoad.Value = cpuLoad.GetTotalLoad();
   287       }
   288     }
   289   }
   290 }