moel@191: /* moel@191: moel@344: This Source Code Form is subject to the terms of the Mozilla Public moel@344: License, v. 2.0. If a copy of the MPL was not distributed with this moel@344: file, You can obtain one at http://mozilla.org/MPL/2.0/. moel@191: moel@344: Copyright (C) 2010-2011 Michael Möller moel@344: 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: 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@279: private readonly double estimatedTimeStampCounterFrequencyError; moel@191: moel@191: private ulong lastTimeStampCount; moel@191: private long lastTime; moel@279: private double timeStampCounterFrequency; moel@279: 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@275: public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings) moel@275: : base(cpuid[0][0].Name, CreateIdentifier(cpuid[0][0].Vendor, moel@275: processorIndex), settings) moel@275: { 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@275: this.coreCount = cpuid.Length; 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@279: moel@279: EstimateTimeStampCounterFrequency( moel@279: out estimatedTimeStampCounterFrequency, moel@279: out estimatedTimeStampCounterFrequencyError); 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@275: private static Identifier CreateIdentifier(Vendor vendor, moel@275: int processorIndex) moel@275: { moel@275: string s; moel@275: switch (vendor) { moel@275: case Vendor.AMD: s = "amdcpu"; break; moel@275: case Vendor.Intel: s = "intelcpu"; break; moel@275: default: s = "genericcpu"; break; moel@275: } moel@275: return new Identifier(s, moel@275: processorIndex.ToString(CultureInfo.InvariantCulture)); moel@275: } moel@275: moel@279: private void EstimateTimeStampCounterFrequency(out double frequency, moel@279: out double error) moel@279: { moel@279: double f, e; moel@279: moel@191: // preload the function moel@279: EstimateTimeStampCounterFrequency(0, out f, out e); moel@279: EstimateTimeStampCounterFrequency(0, out f, out e); moel@191: moel@279: // estimate the frequency moel@279: error = double.MaxValue; moel@279: frequency = 0; moel@279: for (int i = 0; i < 5; i++) { moel@279: EstimateTimeStampCounterFrequency(0.025, out f, out e); moel@279: if (e < error) { moel@279: error = e; moel@279: frequency = f; moel@279: } moel@279: moel@279: if (error < 1e-4) moel@279: break; moel@279: } moel@191: } moel@191: moel@279: private void EstimateTimeStampCounterFrequency(double timeWindow, moel@279: out double frequency, out double error) moel@279: { 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@279: moel@191: while (Stopwatch.GetTimestamp() < timeBegin) { } moel@236: countBegin = Opcode.Rdtsc(); moel@279: long afterBegin = Stopwatch.GetTimestamp(); moel@279: moel@191: while (Stopwatch.GetTimestamp() < timeEnd) { } moel@236: countEnd = Opcode.Rdtsc(); moel@279: long afterEnd = Stopwatch.GetTimestamp(); moel@191: moel@279: double delta = (timeEnd - timeBegin); moel@279: frequency = 1e-6 * moel@279: (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / delta; moel@279: moel@279: double beginError = (afterBegin - timeBegin) / delta; moel@279: double endError = (afterEnd - timeEnd) / delta; moel@279: error = beginError + endError; 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@279: "Estimated Time Stamp Counter Frequency: {0} MHz", moel@279: Math.Round(estimatedTimeStampCounterFrequency * 100) * 0.01)); moel@279: r.AppendLine(string.Format(CultureInfo.InvariantCulture, moel@279: "Estimated Time Stamp Counter Frequency Error: {0} Mhz", moel@279: Math.Round(estimatedTimeStampCounterFrequency * moel@279: estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5)); moel@279: 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 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: }