moel@191: /*
moel@191:   
moel@191:   Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@191: 
moel@191:   The contents of this file are subject to the Mozilla Public License Version
moel@191:   1.1 (the "License"); you may not use this file except in compliance with
moel@191:   the License. You may obtain a copy of the License at
moel@191:  
moel@191:   http://www.mozilla.org/MPL/
moel@191: 
moel@191:   Software distributed under the License is distributed on an "AS IS" basis,
moel@191:   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@191:   for the specific language governing rights and limitations under the License.
moel@191: 
moel@191:   The Original Code is the Open Hardware Monitor code.
moel@191: 
moel@191:   The Initial Developer of the Original Code is 
moel@191:   Michael Möller <m.moeller@gmx.ch>.
moel@191:   Portions created by the Initial Developer are Copyright (C) 2010
moel@191:   the Initial Developer. All Rights Reserved.
moel@191: 
moel@191:   Contributor(s):
moel@191: 
moel@191:   Alternatively, the contents of this file may be used under the terms of
moel@191:   either the GNU General Public License Version 2 or later (the "GPL"), or
moel@191:   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@191:   in which case the provisions of the GPL or the LGPL are applicable instead
moel@191:   of those above. If you wish to allow use of your version of this file only
moel@191:   under the terms of either the GPL or the LGPL, and not to allow others to
moel@191:   use your version of this file under the terms of the MPL, indicate your
moel@191:   decision by deleting the provisions above and replace them with the notice
moel@191:   and other provisions required by the GPL or the LGPL. If you do not delete
moel@191:   the provisions above, a recipient may use your version of this file under
moel@191:   the terms of any one of the MPL, the GPL or the LGPL.
moel@191:  
moel@191: */
moel@191: 
moel@191: using System;
moel@191: using System.Collections.Generic;
moel@191: using System.Diagnostics;
moel@191: using System.Globalization;
moel@236: using System.Runtime.InteropServices;
moel@191: using System.Text;
moel@191: using System.Threading;
moel@191: 
moel@191: namespace OpenHardwareMonitor.Hardware.CPU {
moel@195:   internal class GenericCPU : Hardware {
moel@191: 
moel@191:     protected readonly CPUID[][] cpuid;
moel@191:    
moel@191:     protected readonly uint family;
moel@191:     protected readonly uint model;
moel@191:     protected readonly uint stepping;
moel@191: 
moel@191:     protected readonly int processorIndex;
moel@191:     protected readonly int coreCount;
moel@191:     protected readonly string name;
moel@191: 
moel@236:     private readonly bool hasModelSpecificRegisters;
moel@236: 
moel@201:     private readonly bool hasTimeStampCounter;
moel@201:     private readonly bool isInvariantTimeStampCounter;
moel@201:     private readonly double estimatedTimeStampCounterFrequency;
moel@191: 
moel@191:     private ulong lastTimeStampCount;
moel@191:     private long lastTime;
moel@201:     private double timeStampCounterFrequency;    
moel@191: 
moel@195:     private readonly Vendor vendor;
moel@191: 
moel@191:     private readonly CPULoad cpuLoad;
moel@191:     private readonly Sensor totalLoad;
moel@191:     private readonly Sensor[] coreLoads;
moel@191: 
moel@191:     protected string CoreString(int i) {
moel@191:       if (coreCount == 1)
moel@191:         return "CPU Core";
moel@191:       else
moel@191:         return "CPU Core #" + (i + 1);
moel@191:     }
moel@191: 
moel@191:     public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings) {
moel@191:       this.cpuid = cpuid;
moel@191: 
moel@191:       this.vendor = cpuid[0][0].Vendor;
moel@191: 
moel@191:       this.family = cpuid[0][0].Family;
moel@191:       this.model = cpuid[0][0].Model;
moel@191:       this.stepping = cpuid[0][0].Stepping;
moel@191: 
moel@191:       this.processorIndex = processorIndex;
moel@191:       this.coreCount = cpuid.Length;
moel@236:       this.name = cpuid[0][0].Name;    
moel@236:   
moel@236:       // check if processor has MSRs
moel@236:       if (cpuid[0][0].Data.GetLength(0) > 1
moel@236:         && (cpuid[0][0].Data[1, 3] & 0x20) != 0)
moel@236:         hasModelSpecificRegisters = true;
moel@236:       else
moel@236:         hasModelSpecificRegisters = false;
moel@191: 
moel@201:       // check if processor has a TSC
moel@191:       if (cpuid[0][0].Data.GetLength(0) > 1
moel@191:         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
moel@201:         hasTimeStampCounter = true;
moel@191:       else
moel@201:         hasTimeStampCounter = false;
moel@191: 
moel@201:       // check if processor supports an invariant TSC 
moel@191:       if (cpuid[0][0].ExtData.GetLength(0) > 7
moel@191:         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
moel@201:         isInvariantTimeStampCounter = true;
moel@191:       else
moel@201:         isInvariantTimeStampCounter = false;
moel@191: 
moel@191:       if (coreCount > 1)
moel@191:         totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
moel@191:       else
moel@191:         totalLoad = null;
moel@191:       coreLoads = new Sensor[coreCount];
moel@191:       for (int i = 0; i < coreLoads.Length; i++)
moel@191:         coreLoads[i] = new Sensor(CoreString(i), i + 1,
moel@191:           SensorType.Load, this, settings);
moel@191:       cpuLoad = new CPULoad(cpuid);
moel@191:       if (cpuLoad.IsAvailable) {
moel@191:         foreach (Sensor sensor in coreLoads)
moel@191:           ActivateSensor(sensor);
moel@191:         if (totalLoad != null)
moel@191:           ActivateSensor(totalLoad);
moel@191:       }
moel@191: 
moel@203:       if (hasTimeStampCounter) {
moel@238:         ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
moel@238:         
moel@203:         estimatedTimeStampCounterFrequency = 
moel@238:           EstimateTimeStampCounterFrequency();  
moel@238:         
moel@238:         ThreadAffinity.Set(mask);
moel@203:       } else {
moel@201:         estimatedTimeStampCounterFrequency = 0;
moel@203:       }
moel@203: 
moel@203:       timeStampCounterFrequency = estimatedTimeStampCounterFrequency;                  
moel@191:     }
moel@191: 
moel@238:     private static double EstimateTimeStampCounterFrequency() {           
moel@191:       // preload the function
moel@201:       EstimateTimeStampCounterFrequency(0);
moel@201:       EstimateTimeStampCounterFrequency(0);
moel@191: 
moel@201:       // estimate the frequency in MHz      
moel@201:       List<double> estimatedFrequency = new List<double>(3);
moel@191:       for (int i = 0; i < 3; i++)
moel@201:         estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
moel@238:                  
moel@201:       estimatedFrequency.Sort();
moel@201:       return estimatedFrequency[1];
moel@191:     }
moel@191: 
moel@201:     private static double EstimateTimeStampCounterFrequency(double timeWindow) {
moel@191:       long ticks = (long)(timeWindow * Stopwatch.Frequency);
moel@236:       ulong countBegin, countEnd;
moel@191: 
moel@191:       long timeBegin = Stopwatch.GetTimestamp() +
moel@191:         (long)Math.Ceiling(0.001 * ticks);
moel@191:       long timeEnd = timeBegin + ticks;
moel@191:       while (Stopwatch.GetTimestamp() < timeBegin) { }
moel@236:       countBegin = Opcode.Rdtsc();
moel@191:       while (Stopwatch.GetTimestamp() < timeEnd) { }
moel@236:       countEnd = Opcode.Rdtsc();
moel@191: 
moel@191:       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
moel@191:         (timeEnd - timeBegin);
moel@191:     }
moel@191: 
moel@236: 
moel@191:     private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
moel@191:       uint eax, edx;
moel@238:       if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
moel@191:         r.Append(" ");
moel@191:         r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
moel@191:         r.Append("  ");
moel@191:         r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
moel@191:         r.Append("  ");
moel@191:         r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
moel@191:         r.AppendLine();
moel@191:       }
moel@191:     }
moel@191: 
moel@191:     protected virtual uint[] GetMSRs() {
moel@191:       return null;
moel@191:     }
moel@191: 
moel@191:     public override string GetReport() {
moel@191:       StringBuilder r = new StringBuilder();
moel@191: 
moel@191:       switch (vendor) {
moel@195:         case Vendor.AMD: r.AppendLine("AMD CPU"); break;
moel@191:         case Vendor.Intel: r.AppendLine("Intel CPU"); break;
moel@191:         default: r.AppendLine("Generic CPU"); break;
moel@191:       }
moel@191: 
moel@191:       r.AppendLine();
moel@191:       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
moel@191:       r.AppendFormat("Number of Cores: {0}{1}", coreCount,
moel@191:         Environment.NewLine);
moel@191:       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
moel@191:         Environment.NewLine);
moel@191:       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
moel@191:         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
moel@201:       r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
moel@201:         isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
moel@191:       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
moel@201:         "Time Stamp Counter Frequency: {0} MHz",
moel@201:         Math.Round(timeStampCounterFrequency * 100) * 0.01));   
moel@191:       r.AppendLine();
moel@191: 
moel@191:       uint[] msrArray = GetMSRs();
moel@191:       if (msrArray != null && msrArray.Length > 0) {
moel@191:         for (int i = 0; i < cpuid.Length; i++) {
moel@191:           r.AppendLine("MSR Core #" + (i + 1));
moel@191:           r.AppendLine();
moel@191:           r.AppendLine(" MSR       EDX       EAX");
moel@191:           foreach (uint msr in msrArray)
moel@191:             AppendMSRData(r, msr, cpuid[i][0].Thread);
moel@191:           r.AppendLine();
moel@191:         }
moel@191:       }
moel@191: 
moel@191:       return r.ToString();
moel@191:     }
moel@191: 
moel@191:     public override Identifier Identifier {
moel@191:       get {
moel@191:         string s;
moel@191:         switch (vendor) {
moel@191:           case Vendor.AMD: s = "amdcpu"; break;
moel@191:           case Vendor.Intel: s = "intelcpu"; break;
moel@191:           default: s = "genericcpu"; break;
moel@191:         }
moel@191:         return new Identifier(s, 
moel@191:           processorIndex.ToString(CultureInfo.InvariantCulture));
moel@191:       }
moel@191:     }
moel@191: 
moel@191:     public override string Name {
moel@191:       get { return name; }
moel@191:     }
moel@191: 
moel@191:     public override HardwareType HardwareType {
moel@191:       get { return HardwareType.CPU; }
moel@191:     }
moel@191: 
moel@236:     public bool HasModelSpecificRegisters {
moel@236:       get { return hasModelSpecificRegisters; }
moel@236:     }
moel@236: 
moel@201:     public bool HasTimeStampCounter {
moel@201:       get { return hasTimeStampCounter; }
moel@201:     }
moel@201: 
moel@201:     public double TimeStampCounterFrequency {
moel@201:       get { return timeStampCounterFrequency; }
moel@191:     }
moel@191: 
moel@191:     public override void Update() {
moel@222:       if (hasTimeStampCounter && isInvariantTimeStampCounter) {
moel@236: 
moel@236:         // make sure always the same thread is used
moel@238:         ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
moel@222: 
moel@222:         // read time before and after getting the TSC to estimate the error
moel@222:         long firstTime = Stopwatch.GetTimestamp();
moel@236:         ulong timeStampCount = Opcode.Rdtsc();
moel@191:         long time = Stopwatch.GetTimestamp();
moel@222: 
moel@236:         // restore the thread affinity mask
moel@238:         ThreadAffinity.Set(mask);
moel@236: 
moel@191:         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
moel@222:         double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
moel@222: 
moel@222:         // only use data if they are measured accuarte enough (max 0.1ms delay)
moel@222:         if (error < 0.0001) {
moel@222: 
moel@222:           // ignore the first reading because there are no initial values 
moel@222:           // ignore readings with too large or too small time window
moel@222:           if (lastTime != 0 && delta > 0.5 && delta < 2) {
moel@222: 
moel@222:             // update the TSC frequency with the new value
moel@222:             timeStampCounterFrequency =
moel@201:               (timeStampCount - lastTimeStampCount) / (1e6 * delta);
moel@222:           }
moel@191: 
moel@191:           lastTimeStampCount = timeStampCount;
moel@191:           lastTime = time;
moel@191:         }        
moel@191:       }
moel@191: 
moel@191:       if (cpuLoad.IsAvailable) {
moel@191:         cpuLoad.Update();
moel@191:         for (int i = 0; i < coreLoads.Length; i++)
moel@191:           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
moel@191:         if (totalLoad != null)
moel@191:           totalLoad.Value = cpuLoad.GetTotalLoad();
moel@191:       }
moel@191:     }
moel@191:   }
moel@191: }