Modified and extended version of the patch v4 by Roland Reinl (see Issue 256). Main differences to the original patch: DeviceIoControl refactorings removed, SmartAttribute is now descriptive only and does not hold any state, report is written as one 80 columns table, sensors are created only for meaningful values and without duplicates (remaining life, temperatures, host writes and reads). Also the current implementation should really preserve all the functionality of the old system. Additionally there is now a simple SMART devices emulation class (DebugSmart) that can be used in place of WindowsSmart for testing with reported data.
3 Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 The contents of this file are subject to the Mozilla Public License Version
6 1.1 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 for the specific language governing rights and limitations under the License.
15 The Original Code is the Open Hardware Monitor code.
17 The Initial Developer of the Original Code is
18 Michael Möller <m.moeller@gmx.ch>.
19 Portions created by the Initial Developer are Copyright (C) 2010-2011
20 the Initial Developer. All Rights Reserved.
24 Alternatively, the contents of this file may be used under the terms of
25 either the GNU General Public License Version 2 or later (the "GPL"), or
26 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 in which case the provisions of the GPL or the LGPL are applicable instead
28 of those above. If you wish to allow use of your version of this file only
29 under the terms of either the GPL or the LGPL, and not to allow others to
30 use your version of this file under the terms of the MPL, indicate your
31 decision by deleting the provisions above and replace them with the notice
32 and other provisions required by the GPL or the LGPL. If you do not delete
33 the provisions above, a recipient may use your version of this file under
34 the terms of any one of the MPL, the GPL or the LGPL.
39 using System.Collections.Generic;
40 using System.Diagnostics;
41 using System.Globalization;
42 using System.Runtime.InteropServices;
44 using System.Threading;
46 namespace OpenHardwareMonitor.Hardware.CPU {
47 internal class GenericCPU : Hardware {
49 protected readonly CPUID[][] cpuid;
51 protected readonly uint family;
52 protected readonly uint model;
53 protected readonly uint stepping;
55 protected readonly int processorIndex;
56 protected readonly int coreCount;
58 private readonly bool hasModelSpecificRegisters;
60 private readonly bool hasTimeStampCounter;
61 private readonly bool isInvariantTimeStampCounter;
62 private readonly double estimatedTimeStampCounterFrequency;
63 private readonly double estimatedTimeStampCounterFrequencyError;
65 private ulong lastTimeStampCount;
66 private long lastTime;
67 private double timeStampCounterFrequency;
70 private readonly Vendor vendor;
72 private readonly CPULoad cpuLoad;
73 private readonly Sensor totalLoad;
74 private readonly Sensor[] coreLoads;
76 protected string CoreString(int i) {
80 return "CPU Core #" + (i + 1);
83 public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
84 : base(cpuid[0][0].Name, CreateIdentifier(cpuid[0][0].Vendor,
85 processorIndex), settings)
89 this.vendor = cpuid[0][0].Vendor;
91 this.family = cpuid[0][0].Family;
92 this.model = cpuid[0][0].Model;
93 this.stepping = cpuid[0][0].Stepping;
95 this.processorIndex = processorIndex;
96 this.coreCount = cpuid.Length;
98 // check if processor has MSRs
99 if (cpuid[0][0].Data.GetLength(0) > 1
100 && (cpuid[0][0].Data[1, 3] & 0x20) != 0)
101 hasModelSpecificRegisters = true;
103 hasModelSpecificRegisters = false;
105 // check if processor has a TSC
106 if (cpuid[0][0].Data.GetLength(0) > 1
107 && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
108 hasTimeStampCounter = true;
110 hasTimeStampCounter = false;
112 // check if processor supports an invariant TSC
113 if (cpuid[0][0].ExtData.GetLength(0) > 7
114 && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
115 isInvariantTimeStampCounter = true;
117 isInvariantTimeStampCounter = false;
120 totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
123 coreLoads = new Sensor[coreCount];
124 for (int i = 0; i < coreLoads.Length; i++)
125 coreLoads[i] = new Sensor(CoreString(i), i + 1,
126 SensorType.Load, this, settings);
127 cpuLoad = new CPULoad(cpuid);
128 if (cpuLoad.IsAvailable) {
129 foreach (Sensor sensor in coreLoads)
130 ActivateSensor(sensor);
131 if (totalLoad != null)
132 ActivateSensor(totalLoad);
135 if (hasTimeStampCounter) {
136 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
138 EstimateTimeStampCounterFrequency(
139 out estimatedTimeStampCounterFrequency,
140 out estimatedTimeStampCounterFrequencyError);
142 ThreadAffinity.Set(mask);
144 estimatedTimeStampCounterFrequency = 0;
147 timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
150 private static Identifier CreateIdentifier(Vendor vendor,
155 case Vendor.AMD: s = "amdcpu"; break;
156 case Vendor.Intel: s = "intelcpu"; break;
157 default: s = "genericcpu"; break;
159 return new Identifier(s,
160 processorIndex.ToString(CultureInfo.InvariantCulture));
163 private void EstimateTimeStampCounterFrequency(out double frequency,
168 // preload the function
169 EstimateTimeStampCounterFrequency(0, out f, out e);
170 EstimateTimeStampCounterFrequency(0, out f, out e);
172 // estimate the frequency
173 error = double.MaxValue;
175 for (int i = 0; i < 5; i++) {
176 EstimateTimeStampCounterFrequency(0.025, out f, out e);
187 private void EstimateTimeStampCounterFrequency(double timeWindow,
188 out double frequency, out double error)
190 long ticks = (long)(timeWindow * Stopwatch.Frequency);
191 ulong countBegin, countEnd;
193 long timeBegin = Stopwatch.GetTimestamp() +
194 (long)Math.Ceiling(0.001 * ticks);
195 long timeEnd = timeBegin + ticks;
197 while (Stopwatch.GetTimestamp() < timeBegin) { }
198 countBegin = Opcode.Rdtsc();
199 long afterBegin = Stopwatch.GetTimestamp();
201 while (Stopwatch.GetTimestamp() < timeEnd) { }
202 countEnd = Opcode.Rdtsc();
203 long afterEnd = Stopwatch.GetTimestamp();
205 double delta = (timeEnd - timeBegin);
207 (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / delta;
209 double beginError = (afterBegin - timeBegin) / delta;
210 double endError = (afterEnd - timeEnd) / delta;
211 error = beginError + endError;
215 private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
217 if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
219 r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
221 r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
223 r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
228 protected virtual uint[] GetMSRs() {
232 public override string GetReport() {
233 StringBuilder r = new StringBuilder();
236 case Vendor.AMD: r.AppendLine("AMD CPU"); break;
237 case Vendor.Intel: r.AppendLine("Intel CPU"); break;
238 default: r.AppendLine("Generic CPU"); break;
242 r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
243 r.AppendFormat("Number of Cores: {0}{1}", coreCount,
244 Environment.NewLine);
245 r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
246 Environment.NewLine);
247 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
248 "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
249 r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
250 isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
251 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
252 "Estimated Time Stamp Counter Frequency: {0} MHz",
253 Math.Round(estimatedTimeStampCounterFrequency * 100) * 0.01));
254 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
255 "Estimated Time Stamp Counter Frequency Error: {0} Mhz",
256 Math.Round(estimatedTimeStampCounterFrequency *
257 estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5));
258 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
259 "Time Stamp Counter Frequency: {0} MHz",
260 Math.Round(timeStampCounterFrequency * 100) * 0.01));
263 uint[] msrArray = GetMSRs();
264 if (msrArray != null && msrArray.Length > 0) {
265 for (int i = 0; i < cpuid.Length; i++) {
266 r.AppendLine("MSR Core #" + (i + 1));
268 r.AppendLine(" MSR EDX EAX");
269 foreach (uint msr in msrArray)
270 AppendMSRData(r, msr, cpuid[i][0].Thread);
278 public override HardwareType HardwareType {
279 get { return HardwareType.CPU; }
282 public bool HasModelSpecificRegisters {
283 get { return hasModelSpecificRegisters; }
286 public bool HasTimeStampCounter {
287 get { return hasTimeStampCounter; }
290 public double TimeStampCounterFrequency {
291 get { return timeStampCounterFrequency; }
294 public override void Update() {
295 if (hasTimeStampCounter && isInvariantTimeStampCounter) {
297 // make sure always the same thread is used
298 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
300 // read time before and after getting the TSC to estimate the error
301 long firstTime = Stopwatch.GetTimestamp();
302 ulong timeStampCount = Opcode.Rdtsc();
303 long time = Stopwatch.GetTimestamp();
305 // restore the thread affinity mask
306 ThreadAffinity.Set(mask);
308 double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
309 double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
311 // only use data if they are measured accuarte enough (max 0.1ms delay)
312 if (error < 0.0001) {
314 // ignore the first reading because there are no initial values
315 // ignore readings with too large or too small time window
316 if (lastTime != 0 && delta > 0.5 && delta < 2) {
318 // update the TSC frequency with the new value
319 timeStampCounterFrequency =
320 (timeStampCount - lastTimeStampCount) / (1e6 * delta);
323 lastTimeStampCount = timeStampCount;
328 if (cpuLoad.IsAvailable) {
330 for (int i = 0; i < coreLoads.Length; i++)
331 coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
332 if (totalLoad != null)
333 totalLoad.Value = cpuLoad.GetTotalLoad();