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 . 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 estimatedFrequency = new List(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: }