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