Hardware/CPU/GenericCPU.cs
author moel.mich
Sun, 10 Oct 2010 16:57:11 +0000
changeset 219 ce04404774d6
parent 201 958e9fe8afdf
child 222 ba64bb91ebe4
permissions -rw-r--r--
Reading the timeStampCounterMultiplier right at the beginning when the time stamp counter is estimated instead of reading it at each update. Refactored the IntelCPU code a bit.
     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         
   126         // set initial values 
   127         uint lsb, msb;
   128         WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
   129         lastTime = Stopwatch.GetTimestamp();
   130         lastTimeStampCount = ((ulong)msb << 32) | lsb;
   131 
   132       } else {
   133         estimatedTimeStampCounterFrequency = 0;
   134 
   135         lastTime = 0;
   136         lastTimeStampCount = 0;
   137       }
   138 
   139       timeStampCounterFrequency = estimatedTimeStampCounterFrequency;                  
   140     }
   141 
   142     private static double EstimateTimeStampCounterFrequency() {
   143       // preload the function
   144       EstimateTimeStampCounterFrequency(0);
   145       EstimateTimeStampCounterFrequency(0);
   146 
   147       // estimate the frequency in MHz      
   148       List<double> estimatedFrequency = new List<double>(3);
   149       for (int i = 0; i < 3; i++)
   150         estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
   151       estimatedFrequency.Sort();
   152       return estimatedFrequency[1];
   153     }
   154 
   155     private static double EstimateTimeStampCounterFrequency(double timeWindow) {
   156       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   157       uint lsbBegin, msbBegin, lsbEnd, msbEnd;
   158 
   159       Thread.BeginThreadAffinity();
   160       long timeBegin = Stopwatch.GetTimestamp() +
   161         (long)Math.Ceiling(0.001 * ticks);
   162       long timeEnd = timeBegin + ticks;
   163       while (Stopwatch.GetTimestamp() < timeBegin) { }
   164       WinRing0.Rdtsc(out lsbBegin, out msbBegin);
   165       while (Stopwatch.GetTimestamp() < timeEnd) { }
   166       WinRing0.Rdtsc(out lsbEnd, out msbEnd);
   167       Thread.EndThreadAffinity();
   168 
   169       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   170       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   171 
   172       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   173         (timeEnd - timeBegin);
   174     }
   175 
   176     private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
   177       uint eax, edx;
   178       if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
   179         r.Append(" ");
   180         r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
   181         r.Append("  ");
   182         r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
   183         r.Append("  ");
   184         r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
   185         r.AppendLine();
   186       }
   187     }
   188 
   189     protected virtual uint[] GetMSRs() {
   190       return null;
   191     }
   192 
   193     public override string GetReport() {
   194       StringBuilder r = new StringBuilder();
   195 
   196       switch (vendor) {
   197         case Vendor.AMD: r.AppendLine("AMD CPU"); break;
   198         case Vendor.Intel: r.AppendLine("Intel CPU"); break;
   199         default: r.AppendLine("Generic CPU"); break;
   200       }
   201 
   202       r.AppendLine();
   203       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   204       r.AppendFormat("Number of Cores: {0}{1}", coreCount,
   205         Environment.NewLine);
   206       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   207         Environment.NewLine);
   208       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   209         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   210       r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
   211         isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
   212       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   213         "Time Stamp Counter Frequency: {0} MHz",
   214         Math.Round(timeStampCounterFrequency * 100) * 0.01));   
   215       r.AppendLine();
   216 
   217       uint[] msrArray = GetMSRs();
   218       if (msrArray != null && msrArray.Length > 0) {
   219         for (int i = 0; i < cpuid.Length; i++) {
   220           r.AppendLine("MSR Core #" + (i + 1));
   221           r.AppendLine();
   222           r.AppendLine(" MSR       EDX       EAX");
   223           foreach (uint msr in msrArray)
   224             AppendMSRData(r, msr, cpuid[i][0].Thread);
   225           r.AppendLine();
   226         }
   227       }
   228 
   229       return r.ToString();
   230     }
   231 
   232     public override Identifier Identifier {
   233       get {
   234         string s;
   235         switch (vendor) {
   236           case Vendor.AMD: s = "amdcpu"; break;
   237           case Vendor.Intel: s = "intelcpu"; break;
   238           default: s = "genericcpu"; break;
   239         }
   240         return new Identifier(s, 
   241           processorIndex.ToString(CultureInfo.InvariantCulture));
   242       }
   243     }
   244 
   245     public override string Name {
   246       get { return name; }
   247     }
   248 
   249     public override HardwareType HardwareType {
   250       get { return HardwareType.CPU; }
   251     }
   252 
   253     public bool HasTimeStampCounter {
   254       get { return hasTimeStampCounter; }
   255     }
   256 
   257     public double TimeStampCounterFrequency {
   258       get { return timeStampCounterFrequency; }
   259     }
   260 
   261     public override void Update() {
   262       if (hasTimeStampCounter) {
   263         uint lsb, msb;
   264         WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
   265         long time = Stopwatch.GetTimestamp();
   266         ulong timeStampCount = ((ulong)msb << 32) | lsb;
   267         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   268         if (delta > 0.5) {
   269           if (isInvariantTimeStampCounter)
   270             timeStampCounterFrequency = 
   271               (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   272           else
   273             timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
   274 
   275           lastTimeStampCount = timeStampCount;
   276           lastTime = time;
   277         }        
   278       }
   279 
   280       if (cpuLoad.IsAvailable) {
   281         cpuLoad.Update();
   282         for (int i = 0; i < coreLoads.Length; i++)
   283           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   284         if (totalLoad != null)
   285           totalLoad.Value = cpuLoad.GetTotalLoad();
   286       }
   287     }
   288   }
   289 }