Hardware/CPU/GenericCPU.cs
author moel.mich
Sun, 31 Oct 2010 22:08:47 +0000
changeset 236 763675f19ff4
parent 222 ba64bb91ebe4
child 238 bddc6e01840a
permissions -rw-r--r--
Replaced the non-kernel code of WinRing0 with a managed implementation. The new implementation should fix Issue 32 and simplify further work on Issue 46.
     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         estimatedTimeStampCounterFrequency = 
   134           EstimateTimeStampCounterFrequency();        
   135       } else {
   136         estimatedTimeStampCounterFrequency = 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       ulong countBegin, countEnd;
   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       countBegin = Opcode.Rdtsc();
   165       while (Stopwatch.GetTimestamp() < timeEnd) { }
   166       countEnd = Opcode.Rdtsc();
   167       Thread.EndThreadAffinity();
   168 
   169       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   170         (timeEnd - timeBegin);
   171     }
   172 
   173 
   174     private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
   175       uint eax, edx;
   176       if (Ring0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
   177         r.Append(" ");
   178         r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
   179         r.Append("  ");
   180         r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
   181         r.Append("  ");
   182         r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
   183         r.AppendLine();
   184       }
   185     }
   186 
   187     protected virtual uint[] GetMSRs() {
   188       return null;
   189     }
   190 
   191     public override string GetReport() {
   192       StringBuilder r = new StringBuilder();
   193 
   194       switch (vendor) {
   195         case Vendor.AMD: r.AppendLine("AMD CPU"); break;
   196         case Vendor.Intel: r.AppendLine("Intel CPU"); break;
   197         default: r.AppendLine("Generic CPU"); break;
   198       }
   199 
   200       r.AppendLine();
   201       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   202       r.AppendFormat("Number of Cores: {0}{1}", coreCount,
   203         Environment.NewLine);
   204       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   205         Environment.NewLine);
   206       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   207         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   208       r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
   209         isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
   210       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   211         "Time Stamp Counter Frequency: {0} MHz",
   212         Math.Round(timeStampCounterFrequency * 100) * 0.01));   
   213       r.AppendLine();
   214 
   215       uint[] msrArray = GetMSRs();
   216       if (msrArray != null && msrArray.Length > 0) {
   217         for (int i = 0; i < cpuid.Length; i++) {
   218           r.AppendLine("MSR Core #" + (i + 1));
   219           r.AppendLine();
   220           r.AppendLine(" MSR       EDX       EAX");
   221           foreach (uint msr in msrArray)
   222             AppendMSRData(r, msr, cpuid[i][0].Thread);
   223           r.AppendLine();
   224         }
   225       }
   226 
   227       return r.ToString();
   228     }
   229 
   230     public override Identifier Identifier {
   231       get {
   232         string s;
   233         switch (vendor) {
   234           case Vendor.AMD: s = "amdcpu"; break;
   235           case Vendor.Intel: s = "intelcpu"; break;
   236           default: s = "genericcpu"; break;
   237         }
   238         return new Identifier(s, 
   239           processorIndex.ToString(CultureInfo.InvariantCulture));
   240       }
   241     }
   242 
   243     public override string Name {
   244       get { return name; }
   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         IntPtr thread = NativeMethods.GetCurrentThread();
   268         UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
   269           (UIntPtr)(1L << cpuid[0][0].Thread));
   270 
   271         // read time before and after getting the TSC to estimate the error
   272         long firstTime = Stopwatch.GetTimestamp();
   273         ulong timeStampCount = Opcode.Rdtsc();
   274         long time = Stopwatch.GetTimestamp();
   275 
   276         // restore the thread affinity mask
   277         NativeMethods.SetThreadAffinityMask(thread, mask);
   278 
   279         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   280         double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
   281 
   282         // only use data if they are measured accuarte enough (max 0.1ms delay)
   283         if (error < 0.0001) {
   284 
   285           // ignore the first reading because there are no initial values 
   286           // ignore readings with too large or too small time window
   287           if (lastTime != 0 && delta > 0.5 && delta < 2) {
   288 
   289             // update the TSC frequency with the new value
   290             timeStampCounterFrequency =
   291               (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   292           }
   293 
   294           lastTimeStampCount = timeStampCount;
   295           lastTime = time;
   296         }        
   297       }
   298 
   299       if (cpuLoad.IsAvailable) {
   300         cpuLoad.Update();
   301         for (int i = 0; i < coreLoads.Length; i++)
   302           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   303         if (totalLoad != null)
   304           totalLoad.Value = cpuLoad.GetTotalLoad();
   305       }
   306     }
   307 
   308     private static class NativeMethods {
   309       private const string KERNEL = "kernel32.dll";
   310 
   311       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   312       public static extern UIntPtr
   313         SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
   314 
   315       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   316       public static extern IntPtr GetCurrentThread();
   317     }
   318   }
   319 }