Replaced the non-kernel code of WinRing0 with a managed implementation. The new implementation should fix Issue 32 and simplify further work on Issue 46.
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 estimatedTimeStampCounterFrequency =
134 EstimateTimeStampCounterFrequency();
136 estimatedTimeStampCounterFrequency = 0;
139 timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
142 private static double EstimateTimeStampCounterFrequency() {
143 // preload the function
144 EstimateTimeStampCounterFrequency(0);
145 EstimateTimeStampCounterFrequency(0);
147 // estimate the frequency in MHz
148 List<double> estimatedFrequency = new List<double>(3);
149 for (int i = 0; i < 3; i++)
150 estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
151 estimatedFrequency.Sort();
152 return estimatedFrequency[1];
155 private static double EstimateTimeStampCounterFrequency(double timeWindow) {
156 long ticks = (long)(timeWindow * Stopwatch.Frequency);
157 ulong countBegin, countEnd;
159 Thread.BeginThreadAffinity();
160 long timeBegin = Stopwatch.GetTimestamp() +
161 (long)Math.Ceiling(0.001 * ticks);
162 long timeEnd = timeBegin + ticks;
163 while (Stopwatch.GetTimestamp() < timeBegin) { }
164 countBegin = Opcode.Rdtsc();
165 while (Stopwatch.GetTimestamp() < timeEnd) { }
166 countEnd = Opcode.Rdtsc();
167 Thread.EndThreadAffinity();
169 return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
170 (timeEnd - timeBegin);
174 private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
176 if (Ring0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
178 r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
180 r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
182 r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
187 protected virtual uint[] GetMSRs() {
191 public override string GetReport() {
192 StringBuilder r = new StringBuilder();
195 case Vendor.AMD: r.AppendLine("AMD CPU"); break;
196 case Vendor.Intel: r.AppendLine("Intel CPU"); break;
197 default: r.AppendLine("Generic CPU"); break;
201 r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
202 r.AppendFormat("Number of Cores: {0}{1}", coreCount,
203 Environment.NewLine);
204 r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
205 Environment.NewLine);
206 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
207 "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
208 r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
209 isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
210 r.AppendLine(string.Format(CultureInfo.InvariantCulture,
211 "Time Stamp Counter Frequency: {0} MHz",
212 Math.Round(timeStampCounterFrequency * 100) * 0.01));
215 uint[] msrArray = GetMSRs();
216 if (msrArray != null && msrArray.Length > 0) {
217 for (int i = 0; i < cpuid.Length; i++) {
218 r.AppendLine("MSR Core #" + (i + 1));
220 r.AppendLine(" MSR EDX EAX");
221 foreach (uint msr in msrArray)
222 AppendMSRData(r, msr, cpuid[i][0].Thread);
230 public override Identifier Identifier {
234 case Vendor.AMD: s = "amdcpu"; break;
235 case Vendor.Intel: s = "intelcpu"; break;
236 default: s = "genericcpu"; break;
238 return new Identifier(s,
239 processorIndex.ToString(CultureInfo.InvariantCulture));
243 public override string Name {
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 IntPtr thread = NativeMethods.GetCurrentThread();
268 UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
269 (UIntPtr)(1L << cpuid[0][0].Thread));
271 // read time before and after getting the TSC to estimate the error
272 long firstTime = Stopwatch.GetTimestamp();
273 ulong timeStampCount = Opcode.Rdtsc();
274 long time = Stopwatch.GetTimestamp();
276 // restore the thread affinity mask
277 NativeMethods.SetThreadAffinityMask(thread, mask);
279 double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
280 double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
282 // only use data if they are measured accuarte enough (max 0.1ms delay)
283 if (error < 0.0001) {
285 // ignore the first reading because there are no initial values
286 // ignore readings with too large or too small time window
287 if (lastTime != 0 && delta > 0.5 && delta < 2) {
289 // update the TSC frequency with the new value
290 timeStampCounterFrequency =
291 (timeStampCount - lastTimeStampCount) / (1e6 * delta);
294 lastTimeStampCount = timeStampCount;
299 if (cpuLoad.IsAvailable) {
301 for (int i = 0; i < coreLoads.Length; i++)
302 coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
303 if (totalLoad != null)
304 totalLoad.Value = cpuLoad.GetTotalLoad();
308 private static class NativeMethods {
309 private const string KERNEL = "kernel32.dll";
311 [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
312 public static extern UIntPtr
313 SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
315 [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
316 public static extern IntPtr GetCurrentThread();