Switched to SharpLibDisplay.
3 This Source Code Form is subject to the terms of the Mozilla Public
4 License, v. 2.0. If a copy of the MPL was not distributed with this
5 file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 Copyright (C) 2010-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Collections.Generic;
13 using System.Diagnostics;
14 using System.Globalization;
15 using System.Runtime.InteropServices;
17 using System.Threading;
19 namespace OpenHardwareMonitor.Hardware.CPU {
20 internal class GenericCPU : Hardware {
22 protected readonly CPUID[][] cpuid;
24 protected readonly uint family;
25 protected readonly uint model;
26 protected readonly uint stepping;
28 protected readonly int processorIndex;
29 protected readonly int coreCount;
31 private readonly bool hasModelSpecificRegisters;
33 private readonly bool hasTimeStampCounter;
34 private readonly bool isInvariantTimeStampCounter;
35 private readonly double estimatedTimeStampCounterFrequency;
36 private readonly double estimatedTimeStampCounterFrequencyError;
38 private ulong lastTimeStampCount;
39 private long lastTime;
40 private double timeStampCounterFrequency;
43 private readonly Vendor vendor;
45 private readonly CPULoad cpuLoad;
46 private readonly Sensor totalLoad;
47 private readonly Sensor[] coreLoads;
49 protected string CoreString(int i) {
53 return "CPU Core #" + (i + 1);
56 public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
57 : base(cpuid[0][0].Name, CreateIdentifier(cpuid[0][0].Vendor,
58 processorIndex), settings)
62 this.vendor = cpuid[0][0].Vendor;
64 this.family = cpuid[0][0].Family;
65 this.model = cpuid[0][0].Model;
66 this.stepping = cpuid[0][0].Stepping;
68 this.processorIndex = processorIndex;
69 this.coreCount = cpuid.Length;
71 // check if processor has MSRs
72 if (cpuid[0][0].Data.GetLength(0) > 1
73 && (cpuid[0][0].Data[1, 3] & 0x20) != 0)
74 hasModelSpecificRegisters = true;
76 hasModelSpecificRegisters = false;
78 // check if processor has a TSC
79 if (cpuid[0][0].Data.GetLength(0) > 1
80 && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
81 hasTimeStampCounter = true;
83 hasTimeStampCounter = false;
85 // check if processor supports an invariant TSC
86 if (cpuid[0][0].ExtData.GetLength(0) > 7
87 && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
88 isInvariantTimeStampCounter = true;
90 isInvariantTimeStampCounter = false;
93 totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
96 coreLoads = new Sensor[coreCount];
97 for (int i = 0; i < coreLoads.Length; i++)
98 coreLoads[i] = new Sensor(CoreString(i), i + 1,
99 SensorType.Load, this, settings);
100 cpuLoad = new CPULoad(cpuid);
101 if (cpuLoad.IsAvailable) {
102 foreach (Sensor sensor in coreLoads)
103 ActivateSensor(sensor);
104 if (totalLoad != null)
105 ActivateSensor(totalLoad);
108 if (hasTimeStampCounter) {
109 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
111 EstimateTimeStampCounterFrequency(
112 out estimatedTimeStampCounterFrequency,
113 out estimatedTimeStampCounterFrequencyError);
115 ThreadAffinity.Set(mask);
117 estimatedTimeStampCounterFrequency = 0;
120 timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
123 private static Identifier CreateIdentifier(Vendor vendor,
128 case Vendor.AMD: s = "amdcpu"; break;
129 case Vendor.Intel: s = "intelcpu"; break;
130 default: s = "genericcpu"; break;
132 return new Identifier(s,
133 processorIndex.ToString(CultureInfo.InvariantCulture));
136 private void EstimateTimeStampCounterFrequency(out double frequency,
141 // preload the function
142 EstimateTimeStampCounterFrequency(0, out f, out e);
143 EstimateTimeStampCounterFrequency(0, out f, out e);
145 // estimate the frequency
146 error = double.MaxValue;
148 for (int i = 0; i < 5; i++) {
149 EstimateTimeStampCounterFrequency(0.025, out f, out e);
160 private void EstimateTimeStampCounterFrequency(double timeWindow,
161 out double frequency, out double error)
163 long ticks = (long)(timeWindow * Stopwatch.Frequency);
164 ulong countBegin, countEnd;
166 long timeBegin = Stopwatch.GetTimestamp() +
167 (long)Math.Ceiling(0.001 * ticks);
168 long timeEnd = timeBegin + ticks;
170 while (Stopwatch.GetTimestamp() < timeBegin) { }
171 countBegin = Opcode.Rdtsc();
172 long afterBegin = Stopwatch.GetTimestamp();
174 while (Stopwatch.GetTimestamp() < timeEnd) { }
175 countEnd = Opcode.Rdtsc();
176 long afterEnd = Stopwatch.GetTimestamp();
178 double delta = (timeEnd - timeBegin);
180 (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / delta;
182 double beginError = (afterBegin - timeBegin) / delta;
183 double endError = (afterEnd - timeEnd) / delta;
184 error = beginError + endError;
188 private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
190 if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
192 r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
194 r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
196 r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
201 protected virtual uint[] GetMSRs() {
205 public override string GetReport() {
206 StringBuilder r = new StringBuilder();
209 case Vendor.AMD: r.AppendLine("AMD CPU"); break;
210 case Vendor.Intel: r.AppendLine("Intel CPU"); break;
211 default: r.AppendLine("Generic CPU"); break;
215 r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
216 r.AppendFormat("Number of Cores: {0}{1}", coreCount,
217 Environment.NewLine);
218 r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
219 Environment.NewLine);
220 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
221 "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
222 r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
223 isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
224 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
225 "Estimated Time Stamp Counter Frequency: {0} MHz",
226 Math.Round(estimatedTimeStampCounterFrequency * 100) * 0.01));
227 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
228 "Estimated Time Stamp Counter Frequency Error: {0} Mhz",
229 Math.Round(estimatedTimeStampCounterFrequency *
230 estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5));
231 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
232 "Time Stamp Counter Frequency: {0} MHz",
233 Math.Round(timeStampCounterFrequency * 100) * 0.01));
236 uint[] msrArray = GetMSRs();
237 if (msrArray != null && msrArray.Length > 0) {
238 for (int i = 0; i < cpuid.Length; i++) {
239 r.AppendLine("MSR Core #" + (i + 1));
241 r.AppendLine(" MSR EDX EAX");
242 foreach (uint msr in msrArray)
243 AppendMSRData(r, msr, cpuid[i][0].Thread);
251 public override HardwareType HardwareType {
252 get { return HardwareType.CPU; }
255 public bool HasModelSpecificRegisters {
256 get { return hasModelSpecificRegisters; }
259 public bool HasTimeStampCounter {
260 get { return hasTimeStampCounter; }
263 public double TimeStampCounterFrequency {
264 get { return timeStampCounterFrequency; }
267 public override void Update() {
268 if (hasTimeStampCounter && isInvariantTimeStampCounter) {
270 // make sure always the same thread is used
271 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
273 // read time before and after getting the TSC to estimate the error
274 long firstTime = Stopwatch.GetTimestamp();
275 ulong timeStampCount = Opcode.Rdtsc();
276 long time = Stopwatch.GetTimestamp();
278 // restore the thread affinity mask
279 ThreadAffinity.Set(mask);
281 double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
282 double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
284 // only use data if they are measured accuarte enough (max 0.1ms delay)
285 if (error < 0.0001) {
287 // ignore the first reading because there are no initial values
288 // ignore readings with too large or too small time window
289 if (lastTime != 0 && delta > 0.5 && delta < 2) {
291 // update the TSC frequency with the new value
292 timeStampCounterFrequency =
293 (timeStampCount - lastTimeStampCount) / (1e6 * delta);
296 lastTimeStampCount = timeStampCount;
301 if (cpuLoad.IsAvailable) {
303 for (int i = 0; i < coreLoads.Length; i++)
304 coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
305 if (totalLoad != null)
306 totalLoad.Value = cpuLoad.GetTotalLoad();