Fixed Issue 199.
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;
64 private ulong lastTimeStampCount;
65 private long lastTime;
66 private double timeStampCounterFrequency;
68 private readonly Vendor vendor;
70 private readonly CPULoad cpuLoad;
71 private readonly Sensor totalLoad;
72 private readonly Sensor[] coreLoads;
74 protected string CoreString(int i) {
78 return "CPU Core #" + (i + 1);
81 public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
82 : base(cpuid[0][0].Name, CreateIdentifier(cpuid[0][0].Vendor,
83 processorIndex), settings)
87 this.vendor = cpuid[0][0].Vendor;
89 this.family = cpuid[0][0].Family;
90 this.model = cpuid[0][0].Model;
91 this.stepping = cpuid[0][0].Stepping;
93 this.processorIndex = processorIndex;
94 this.coreCount = cpuid.Length;
96 // check if processor has MSRs
97 if (cpuid[0][0].Data.GetLength(0) > 1
98 && (cpuid[0][0].Data[1, 3] & 0x20) != 0)
99 hasModelSpecificRegisters = true;
101 hasModelSpecificRegisters = false;
103 // check if processor has a TSC
104 if (cpuid[0][0].Data.GetLength(0) > 1
105 && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
106 hasTimeStampCounter = true;
108 hasTimeStampCounter = false;
110 // check if processor supports an invariant TSC
111 if (cpuid[0][0].ExtData.GetLength(0) > 7
112 && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
113 isInvariantTimeStampCounter = true;
115 isInvariantTimeStampCounter = false;
118 totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
121 coreLoads = new Sensor[coreCount];
122 for (int i = 0; i < coreLoads.Length; i++)
123 coreLoads[i] = new Sensor(CoreString(i), i + 1,
124 SensorType.Load, this, settings);
125 cpuLoad = new CPULoad(cpuid);
126 if (cpuLoad.IsAvailable) {
127 foreach (Sensor sensor in coreLoads)
128 ActivateSensor(sensor);
129 if (totalLoad != null)
130 ActivateSensor(totalLoad);
133 if (hasTimeStampCounter) {
134 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
136 estimatedTimeStampCounterFrequency =
137 EstimateTimeStampCounterFrequency();
139 ThreadAffinity.Set(mask);
141 estimatedTimeStampCounterFrequency = 0;
144 timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
147 private static Identifier CreateIdentifier(Vendor vendor,
152 case Vendor.AMD: s = "amdcpu"; break;
153 case Vendor.Intel: s = "intelcpu"; break;
154 default: s = "genericcpu"; break;
156 return new Identifier(s,
157 processorIndex.ToString(CultureInfo.InvariantCulture));
160 private static double EstimateTimeStampCounterFrequency() {
161 // preload the function
162 EstimateTimeStampCounterFrequency(0);
163 EstimateTimeStampCounterFrequency(0);
165 // estimate the frequency in MHz
166 List<double> estimatedFrequency = new List<double>(3);
167 for (int i = 0; i < 3; i++)
168 estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
170 estimatedFrequency.Sort();
171 return estimatedFrequency[1];
174 private static double EstimateTimeStampCounterFrequency(double timeWindow) {
175 long ticks = (long)(timeWindow * Stopwatch.Frequency);
176 ulong countBegin, countEnd;
178 long timeBegin = Stopwatch.GetTimestamp() +
179 (long)Math.Ceiling(0.001 * ticks);
180 long timeEnd = timeBegin + ticks;
181 while (Stopwatch.GetTimestamp() < timeBegin) { }
182 countBegin = Opcode.Rdtsc();
183 while (Stopwatch.GetTimestamp() < timeEnd) { }
184 countEnd = Opcode.Rdtsc();
186 return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
187 (timeEnd - timeBegin);
191 private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
193 if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
195 r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
197 r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
199 r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
204 protected virtual uint[] GetMSRs() {
208 public override string GetReport() {
209 StringBuilder r = new StringBuilder();
212 case Vendor.AMD: r.AppendLine("AMD CPU"); break;
213 case Vendor.Intel: r.AppendLine("Intel CPU"); break;
214 default: r.AppendLine("Generic CPU"); break;
218 r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
219 r.AppendFormat("Number of Cores: {0}{1}", coreCount,
220 Environment.NewLine);
221 r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
222 Environment.NewLine);
223 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
224 "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
225 r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
226 isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
227 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
228 "Time Stamp Counter Frequency: {0} MHz",
229 Math.Round(timeStampCounterFrequency * 100) * 0.01));
232 uint[] msrArray = GetMSRs();
233 if (msrArray != null && msrArray.Length > 0) {
234 for (int i = 0; i < cpuid.Length; i++) {
235 r.AppendLine("MSR Core #" + (i + 1));
237 r.AppendLine(" MSR EDX EAX");
238 foreach (uint msr in msrArray)
239 AppendMSRData(r, msr, cpuid[i][0].Thread);
247 public override HardwareType HardwareType {
248 get { return HardwareType.CPU; }
251 public bool HasModelSpecificRegisters {
252 get { return hasModelSpecificRegisters; }
255 public bool HasTimeStampCounter {
256 get { return hasTimeStampCounter; }
259 public double TimeStampCounterFrequency {
260 get { return timeStampCounterFrequency; }
263 public override void Update() {
264 if (hasTimeStampCounter && isInvariantTimeStampCounter) {
266 // make sure always the same thread is used
267 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
269 // read time before and after getting the TSC to estimate the error
270 long firstTime = Stopwatch.GetTimestamp();
271 ulong timeStampCount = Opcode.Rdtsc();
272 long time = Stopwatch.GetTimestamp();
274 // restore the thread affinity mask
275 ThreadAffinity.Set(mask);
277 double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
278 double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
280 // only use data if they are measured accuarte enough (max 0.1ms delay)
281 if (error < 0.0001) {
283 // ignore the first reading because there are no initial values
284 // ignore readings with too large or too small time window
285 if (lastTime != 0 && delta > 0.5 && delta < 2) {
287 // update the TSC frequency with the new value
288 timeStampCounterFrequency =
289 (timeStampCount - lastTimeStampCount) / (1e6 * delta);
292 lastTimeStampCount = timeStampCount;
297 if (cpuLoad.IsAvailable) {
299 for (int i = 0; i < coreLoads.Length; i++)
300 coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
301 if (totalLoad != null)
302 totalLoad.Value = cpuLoad.GetTotalLoad();
306 public virtual void Close() {