Hardware/CPU/GenericCPU.cs
author paulwerelds
Tue, 21 Sep 2010 09:12:32 +0000
changeset 192 ad190510cddd
child 195 0ee888c485d5
permissions -rw-r--r--
Added core and bus clock readings for AMD's K8 (0F) family of CPUs
     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, IHardware {
    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     protected readonly bool hasTSC;
    59     protected readonly bool invariantTSC;
    60 
    61     private ulong lastTimeStampCount;
    62     private long lastTime;
    63     private double maxClock;
    64     private double estimatedMaxClock;
    65 
    66     private 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 TSC
    93       if (cpuid[0][0].Data.GetLength(0) > 1
    94         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
    95         hasTSC = true;
    96       else
    97         hasTSC = false;
    98 
    99       // check if processor supports invariant TSC 
   100       if (cpuid[0][0].ExtData.GetLength(0) > 7
   101         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
   102         invariantTSC = true;
   103       else
   104         invariantTSC = 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 (hasTSC)
   123         estimatedMaxClock = EstimateMaxClock();
   124       else
   125         estimatedMaxClock = 0;
   126       maxClock = estimatedMaxClock;
   127 
   128       lastTimeStampCount = 0;
   129       lastTime = 0;
   130     }
   131 
   132     private double EstimateMaxClock() {
   133       // preload the function
   134       EstimateMaxClock(0);
   135       EstimateMaxClock(0);
   136 
   137       // estimate the max clock in MHz      
   138       List<double> estimatedMaxClocks = new List<double>(3);
   139       for (int i = 0; i < 3; i++)
   140         estimatedMaxClocks.Add(1e-6 * EstimateMaxClock(0.025));
   141       estimatedMaxClocks.Sort();
   142       return estimatedMaxClocks[1];
   143     }
   144 
   145     private static double EstimateMaxClock(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("Intel 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("TSC: " +
   199         (hasTSC ? (invariantTSC ? "Invariant" : "Not Invariant") : "None"));
   200       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   201         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   202       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   203         "Max Clock: {0} MHz", Math.Round(maxClock * 100) * 0.01));
   204       r.AppendLine();
   205 
   206       uint[] msrArray = GetMSRs();
   207       if (msrArray != null && msrArray.Length > 0) {
   208         for (int i = 0; i < cpuid.Length; i++) {
   209           r.AppendLine("MSR Core #" + (i + 1));
   210           r.AppendLine();
   211           r.AppendLine(" MSR       EDX       EAX");
   212           foreach (uint msr in msrArray)
   213             AppendMSRData(r, msr, cpuid[i][0].Thread);
   214           r.AppendLine();
   215         }
   216       }
   217 
   218       return r.ToString();
   219     }
   220 
   221     public override Identifier Identifier {
   222       get {
   223         string s;
   224         switch (vendor) {
   225           case Vendor.AMD: s = "amdcpu"; break;
   226           case Vendor.Intel: s = "intelcpu"; break;
   227           default: s = "genericcpu"; break;
   228         }
   229         return new Identifier(s, 
   230           processorIndex.ToString(CultureInfo.InvariantCulture));
   231       }
   232     }
   233 
   234     public override string Name {
   235       get { return name; }
   236     }
   237 
   238     public override HardwareType HardwareType {
   239       get { return HardwareType.CPU; }
   240     }
   241 
   242     protected double MaxClock {
   243       get { return maxClock; }
   244     }
   245 
   246     public override void Update() {
   247       if (hasTSC) {
   248         uint lsb, msb;
   249         WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
   250         long time = Stopwatch.GetTimestamp();
   251         ulong timeStampCount = ((ulong)msb << 32) | lsb;
   252         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   253         if (delta > 0.5) {
   254           if (invariantTSC)
   255             maxClock = (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   256           else
   257             maxClock = estimatedMaxClock;
   258 
   259           lastTimeStampCount = timeStampCount;
   260           lastTime = time;
   261         }        
   262       }
   263 
   264       if (cpuLoad.IsAvailable) {
   265         cpuLoad.Update();
   266         for (int i = 0; i < coreLoads.Length; i++)
   267           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   268         if (totalLoad != null)
   269           totalLoad.Value = cpuLoad.GetTotalLoad();
   270       }
   271     }
   272   }
   273 }