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@191: using System.Text; moel@191: using System.Threading; moel@191: moel@191: namespace OpenHardwareMonitor.Hardware.CPU { moel@191: internal class GenericCPU : Hardware, IHardware { 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@191: protected readonly bool hasTSC; moel@191: protected readonly bool invariantTSC; moel@191: moel@191: private ulong lastTimeStampCount; moel@191: private long lastTime; moel@191: private double maxClock; moel@191: private double estimatedMaxClock; moel@191: moel@191: private 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@191: this.name = cpuid[0][0].Name; moel@191: moel@191: // check if processor has TSC moel@191: if (cpuid[0][0].Data.GetLength(0) > 1 moel@191: && (cpuid[0][0].Data[1, 3] & 0x10) != 0) moel@191: hasTSC = true; moel@191: else moel@191: hasTSC = false; moel@191: moel@191: // check if processor supports invariant TSC moel@191: if (cpuid[0][0].ExtData.GetLength(0) > 7 moel@191: && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0) moel@191: invariantTSC = true; moel@191: else moel@191: invariantTSC = 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@191: if (hasTSC) moel@191: estimatedMaxClock = EstimateMaxClock(); moel@191: else moel@191: estimatedMaxClock = 0; moel@191: maxClock = estimatedMaxClock; moel@191: moel@191: lastTimeStampCount = 0; moel@191: lastTime = 0; moel@191: } moel@191: moel@191: private double EstimateMaxClock() { moel@191: // preload the function moel@191: EstimateMaxClock(0); moel@191: EstimateMaxClock(0); moel@191: moel@191: // estimate the max clock in MHz moel@191: List estimatedMaxClocks = new List(3); moel@191: for (int i = 0; i < 3; i++) moel@191: estimatedMaxClocks.Add(1e-6 * EstimateMaxClock(0.025)); moel@191: estimatedMaxClocks.Sort(); moel@191: return estimatedMaxClocks[1]; moel@191: } moel@191: moel@191: private static double EstimateMaxClock(double timeWindow) { moel@191: long ticks = (long)(timeWindow * Stopwatch.Frequency); moel@191: uint lsbBegin, msbBegin, lsbEnd, msbEnd; moel@191: moel@191: Thread.BeginThreadAffinity(); 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@191: WinRing0.Rdtsc(out lsbBegin, out msbBegin); moel@191: while (Stopwatch.GetTimestamp() < timeEnd) { } moel@191: WinRing0.Rdtsc(out lsbEnd, out msbEnd); moel@191: Thread.EndThreadAffinity(); moel@191: moel@191: ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin; moel@191: ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd; moel@191: moel@191: return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / moel@191: (timeEnd - timeBegin); moel@191: } moel@191: moel@191: private static void AppendMSRData(StringBuilder r, uint msr, int thread) { moel@191: uint eax, edx; moel@191: if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << 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@191: case Vendor.AMD: r.AppendLine("Intel 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("TSC: " + moel@191: (hasTSC ? (invariantTSC ? "Invariant" : "Not Invariant") : "None")); moel@191: r.AppendLine(string.Format(CultureInfo.InvariantCulture, moel@191: "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6)); moel@191: r.AppendLine(string.Format(CultureInfo.InvariantCulture, moel@191: "Max Clock: {0} MHz", Math.Round(maxClock * 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@191: protected double MaxClock { moel@191: get { return maxClock; } moel@191: } moel@191: moel@191: public override void Update() { moel@191: if (hasTSC) { moel@191: uint lsb, msb; moel@191: WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1); moel@191: long time = Stopwatch.GetTimestamp(); moel@191: ulong timeStampCount = ((ulong)msb << 32) | lsb; moel@191: double delta = ((double)(time - lastTime)) / Stopwatch.Frequency; moel@191: if (delta > 0.5) { moel@191: if (invariantTSC) moel@191: maxClock = (timeStampCount - lastTimeStampCount) / (1e6 * delta); moel@191: else moel@191: maxClock = estimatedMaxClock; 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: }