Fixed Issue 156.
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
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;
57 protected readonly string name;
59 private readonly bool hasModelSpecificRegisters;
61 private readonly bool hasTimeStampCounter;
62 private readonly bool isInvariantTimeStampCounter;
63 private readonly double estimatedTimeStampCounterFrequency;
65 private ulong lastTimeStampCount;
66 private long lastTime;
67 private double timeStampCounterFrequency;
69 private readonly Vendor vendor;
71 private readonly CPULoad cpuLoad;
72 private readonly Sensor totalLoad;
73 private readonly Sensor[] coreLoads;
75 protected string CoreString(int i) {
79 return "CPU Core #" + (i + 1);
82 public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings) {
85 this.vendor = cpuid[0][0].Vendor;
87 this.family = cpuid[0][0].Family;
88 this.model = cpuid[0][0].Model;
89 this.stepping = cpuid[0][0].Stepping;
91 this.processorIndex = processorIndex;
92 this.coreCount = cpuid.Length;
93 this.name = cpuid[0][0].Name;
95 // check if processor has MSRs
96 if (cpuid[0][0].Data.GetLength(0) > 1
97 && (cpuid[0][0].Data[1, 3] & 0x20) != 0)
98 hasModelSpecificRegisters = true;
100 hasModelSpecificRegisters = false;
102 // check if processor has a TSC
103 if (cpuid[0][0].Data.GetLength(0) > 1
104 && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
105 hasTimeStampCounter = true;
107 hasTimeStampCounter = false;
109 // check if processor supports an invariant TSC
110 if (cpuid[0][0].ExtData.GetLength(0) > 7
111 && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
112 isInvariantTimeStampCounter = true;
114 isInvariantTimeStampCounter = false;
117 totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
120 coreLoads = new Sensor[coreCount];
121 for (int i = 0; i < coreLoads.Length; i++)
122 coreLoads[i] = new Sensor(CoreString(i), i + 1,
123 SensorType.Load, this, settings);
124 cpuLoad = new CPULoad(cpuid);
125 if (cpuLoad.IsAvailable) {
126 foreach (Sensor sensor in coreLoads)
127 ActivateSensor(sensor);
128 if (totalLoad != null)
129 ActivateSensor(totalLoad);
132 if (hasTimeStampCounter) {
133 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
135 estimatedTimeStampCounterFrequency =
136 EstimateTimeStampCounterFrequency();
138 ThreadAffinity.Set(mask);
140 estimatedTimeStampCounterFrequency = 0;
143 timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
146 private static double EstimateTimeStampCounterFrequency() {
147 // preload the function
148 EstimateTimeStampCounterFrequency(0);
149 EstimateTimeStampCounterFrequency(0);
151 // estimate the frequency in MHz
152 List<double> estimatedFrequency = new List<double>(3);
153 for (int i = 0; i < 3; i++)
154 estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
156 estimatedFrequency.Sort();
157 return estimatedFrequency[1];
160 private static double EstimateTimeStampCounterFrequency(double timeWindow) {
161 long ticks = (long)(timeWindow * Stopwatch.Frequency);
162 ulong countBegin, countEnd;
164 long timeBegin = Stopwatch.GetTimestamp() +
165 (long)Math.Ceiling(0.001 * ticks);
166 long timeEnd = timeBegin + ticks;
167 while (Stopwatch.GetTimestamp() < timeBegin) { }
168 countBegin = Opcode.Rdtsc();
169 while (Stopwatch.GetTimestamp() < timeEnd) { }
170 countEnd = Opcode.Rdtsc();
172 return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
173 (timeEnd - timeBegin);
177 private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
179 if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
181 r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
183 r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
185 r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
190 protected virtual uint[] GetMSRs() {
194 public override string GetReport() {
195 StringBuilder r = new StringBuilder();
198 case Vendor.AMD: r.AppendLine("AMD CPU"); break;
199 case Vendor.Intel: r.AppendLine("Intel CPU"); break;
200 default: r.AppendLine("Generic CPU"); break;
204 r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
205 r.AppendFormat("Number of Cores: {0}{1}", coreCount,
206 Environment.NewLine);
207 r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
208 Environment.NewLine);
209 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
210 "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
211 r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
212 isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
213 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
214 "Time Stamp Counter Frequency: {0} MHz",
215 Math.Round(timeStampCounterFrequency * 100) * 0.01));
218 uint[] msrArray = GetMSRs();
219 if (msrArray != null && msrArray.Length > 0) {
220 for (int i = 0; i < cpuid.Length; i++) {
221 r.AppendLine("MSR Core #" + (i + 1));
223 r.AppendLine(" MSR EDX EAX");
224 foreach (uint msr in msrArray)
225 AppendMSRData(r, msr, cpuid[i][0].Thread);
233 public override Identifier Identifier {
237 case Vendor.AMD: s = "amdcpu"; break;
238 case Vendor.Intel: s = "intelcpu"; break;
239 default: s = "genericcpu"; break;
241 return new Identifier(s,
242 processorIndex.ToString(CultureInfo.InvariantCulture));
246 public override string Name {
250 public override HardwareType HardwareType {
251 get { return HardwareType.CPU; }
254 public bool HasModelSpecificRegisters {
255 get { return hasModelSpecificRegisters; }
258 public bool HasTimeStampCounter {
259 get { return hasTimeStampCounter; }
262 public double TimeStampCounterFrequency {
263 get { return timeStampCounterFrequency; }
266 public override void Update() {
267 if (hasTimeStampCounter && isInvariantTimeStampCounter) {
269 // make sure always the same thread is used
270 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
272 // read time before and after getting the TSC to estimate the error
273 long firstTime = Stopwatch.GetTimestamp();
274 ulong timeStampCount = Opcode.Rdtsc();
275 long time = Stopwatch.GetTimestamp();
277 // restore the thread affinity mask
278 ThreadAffinity.Set(mask);
280 double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
281 double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
283 // only use data if they are measured accuarte enough (max 0.1ms delay)
284 if (error < 0.0001) {
286 // ignore the first reading because there are no initial values
287 // ignore readings with too large or too small time window
288 if (lastTime != 0 && delta > 0.5 && delta < 2) {
290 // update the TSC frequency with the new value
291 timeStampCounterFrequency =
292 (timeStampCount - lastTimeStampCount) / (1e6 * delta);
295 lastTimeStampCount = timeStampCount;
300 if (cpuLoad.IsAvailable) {
302 for (int i = 0; i < coreLoads.Length; i++)
303 coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
304 if (totalLoad != null)
305 totalLoad.Value = cpuLoad.GetTotalLoad();