moel@191
|
1 |
/*
|
moel@191
|
2 |
|
moel@191
|
3 |
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
moel@191
|
4 |
|
moel@191
|
5 |
The contents of this file are subject to the Mozilla Public License Version
|
moel@191
|
6 |
1.1 (the "License"); you may not use this file except in compliance with
|
moel@191
|
7 |
the License. You may obtain a copy of the License at
|
moel@191
|
8 |
|
moel@191
|
9 |
http://www.mozilla.org/MPL/
|
moel@191
|
10 |
|
moel@191
|
11 |
Software distributed under the License is distributed on an "AS IS" basis,
|
moel@191
|
12 |
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
moel@191
|
13 |
for the specific language governing rights and limitations under the License.
|
moel@191
|
14 |
|
moel@191
|
15 |
The Original Code is the Open Hardware Monitor code.
|
moel@191
|
16 |
|
moel@191
|
17 |
The Initial Developer of the Original Code is
|
moel@191
|
18 |
Michael Möller <m.moeller@gmx.ch>.
|
moel@266
|
19 |
Portions created by the Initial Developer are Copyright (C) 2010-2011
|
moel@191
|
20 |
the Initial Developer. All Rights Reserved.
|
moel@191
|
21 |
|
moel@191
|
22 |
Contributor(s):
|
moel@191
|
23 |
|
moel@191
|
24 |
Alternatively, the contents of this file may be used under the terms of
|
moel@191
|
25 |
either the GNU General Public License Version 2 or later (the "GPL"), or
|
moel@191
|
26 |
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
moel@191
|
27 |
in which case the provisions of the GPL or the LGPL are applicable instead
|
moel@191
|
28 |
of those above. If you wish to allow use of your version of this file only
|
moel@191
|
29 |
under the terms of either the GPL or the LGPL, and not to allow others to
|
moel@191
|
30 |
use your version of this file under the terms of the MPL, indicate your
|
moel@191
|
31 |
decision by deleting the provisions above and replace them with the notice
|
moel@191
|
32 |
and other provisions required by the GPL or the LGPL. If you do not delete
|
moel@191
|
33 |
the provisions above, a recipient may use your version of this file under
|
moel@191
|
34 |
the terms of any one of the MPL, the GPL or the LGPL.
|
moel@191
|
35 |
|
moel@191
|
36 |
*/
|
moel@191
|
37 |
|
moel@191
|
38 |
using System;
|
moel@191
|
39 |
using System.Collections.Generic;
|
moel@191
|
40 |
using System.Diagnostics;
|
moel@191
|
41 |
using System.Globalization;
|
moel@236
|
42 |
using System.Runtime.InteropServices;
|
moel@191
|
43 |
using System.Text;
|
moel@191
|
44 |
using System.Threading;
|
moel@191
|
45 |
|
moel@191
|
46 |
namespace OpenHardwareMonitor.Hardware.CPU {
|
moel@195
|
47 |
internal class GenericCPU : Hardware {
|
moel@191
|
48 |
|
moel@191
|
49 |
protected readonly CPUID[][] cpuid;
|
moel@191
|
50 |
|
moel@191
|
51 |
protected readonly uint family;
|
moel@191
|
52 |
protected readonly uint model;
|
moel@191
|
53 |
protected readonly uint stepping;
|
moel@191
|
54 |
|
moel@191
|
55 |
protected readonly int processorIndex;
|
moel@191
|
56 |
protected readonly int coreCount;
|
moel@191
|
57 |
|
moel@236
|
58 |
private readonly bool hasModelSpecificRegisters;
|
moel@236
|
59 |
|
moel@201
|
60 |
private readonly bool hasTimeStampCounter;
|
moel@201
|
61 |
private readonly bool isInvariantTimeStampCounter;
|
moel@201
|
62 |
private readonly double estimatedTimeStampCounterFrequency;
|
moel@279
|
63 |
private readonly double estimatedTimeStampCounterFrequencyError;
|
moel@191
|
64 |
|
moel@191
|
65 |
private ulong lastTimeStampCount;
|
moel@191
|
66 |
private long lastTime;
|
moel@279
|
67 |
private double timeStampCounterFrequency;
|
moel@279
|
68 |
|
moel@191
|
69 |
|
moel@195
|
70 |
private readonly Vendor vendor;
|
moel@191
|
71 |
|
moel@191
|
72 |
private readonly CPULoad cpuLoad;
|
moel@191
|
73 |
private readonly Sensor totalLoad;
|
moel@191
|
74 |
private readonly Sensor[] coreLoads;
|
moel@191
|
75 |
|
moel@191
|
76 |
protected string CoreString(int i) {
|
moel@191
|
77 |
if (coreCount == 1)
|
moel@191
|
78 |
return "CPU Core";
|
moel@191
|
79 |
else
|
moel@191
|
80 |
return "CPU Core #" + (i + 1);
|
moel@191
|
81 |
}
|
moel@191
|
82 |
|
moel@275
|
83 |
public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
|
moel@275
|
84 |
: base(cpuid[0][0].Name, CreateIdentifier(cpuid[0][0].Vendor,
|
moel@275
|
85 |
processorIndex), settings)
|
moel@275
|
86 |
{
|
moel@191
|
87 |
this.cpuid = cpuid;
|
moel@191
|
88 |
|
moel@191
|
89 |
this.vendor = cpuid[0][0].Vendor;
|
moel@191
|
90 |
|
moel@191
|
91 |
this.family = cpuid[0][0].Family;
|
moel@191
|
92 |
this.model = cpuid[0][0].Model;
|
moel@191
|
93 |
this.stepping = cpuid[0][0].Stepping;
|
moel@191
|
94 |
|
moel@191
|
95 |
this.processorIndex = processorIndex;
|
moel@275
|
96 |
this.coreCount = cpuid.Length;
|
moel@236
|
97 |
|
moel@236
|
98 |
// check if processor has MSRs
|
moel@236
|
99 |
if (cpuid[0][0].Data.GetLength(0) > 1
|
moel@236
|
100 |
&& (cpuid[0][0].Data[1, 3] & 0x20) != 0)
|
moel@236
|
101 |
hasModelSpecificRegisters = true;
|
moel@236
|
102 |
else
|
moel@236
|
103 |
hasModelSpecificRegisters = false;
|
moel@191
|
104 |
|
moel@201
|
105 |
// check if processor has a TSC
|
moel@191
|
106 |
if (cpuid[0][0].Data.GetLength(0) > 1
|
moel@191
|
107 |
&& (cpuid[0][0].Data[1, 3] & 0x10) != 0)
|
moel@201
|
108 |
hasTimeStampCounter = true;
|
moel@191
|
109 |
else
|
moel@201
|
110 |
hasTimeStampCounter = false;
|
moel@191
|
111 |
|
moel@201
|
112 |
// check if processor supports an invariant TSC
|
moel@191
|
113 |
if (cpuid[0][0].ExtData.GetLength(0) > 7
|
moel@191
|
114 |
&& (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
|
moel@201
|
115 |
isInvariantTimeStampCounter = true;
|
moel@191
|
116 |
else
|
moel@201
|
117 |
isInvariantTimeStampCounter = false;
|
moel@191
|
118 |
|
moel@191
|
119 |
if (coreCount > 1)
|
moel@191
|
120 |
totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
|
moel@191
|
121 |
else
|
moel@191
|
122 |
totalLoad = null;
|
moel@191
|
123 |
coreLoads = new Sensor[coreCount];
|
moel@191
|
124 |
for (int i = 0; i < coreLoads.Length; i++)
|
moel@191
|
125 |
coreLoads[i] = new Sensor(CoreString(i), i + 1,
|
moel@191
|
126 |
SensorType.Load, this, settings);
|
moel@191
|
127 |
cpuLoad = new CPULoad(cpuid);
|
moel@191
|
128 |
if (cpuLoad.IsAvailable) {
|
moel@191
|
129 |
foreach (Sensor sensor in coreLoads)
|
moel@191
|
130 |
ActivateSensor(sensor);
|
moel@191
|
131 |
if (totalLoad != null)
|
moel@191
|
132 |
ActivateSensor(totalLoad);
|
moel@191
|
133 |
}
|
moel@191
|
134 |
|
moel@203
|
135 |
if (hasTimeStampCounter) {
|
moel@238
|
136 |
ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
|
moel@279
|
137 |
|
moel@279
|
138 |
EstimateTimeStampCounterFrequency(
|
moel@279
|
139 |
out estimatedTimeStampCounterFrequency,
|
moel@279
|
140 |
out estimatedTimeStampCounterFrequencyError);
|
moel@238
|
141 |
|
moel@238
|
142 |
ThreadAffinity.Set(mask);
|
moel@203
|
143 |
} else {
|
moel@201
|
144 |
estimatedTimeStampCounterFrequency = 0;
|
moel@203
|
145 |
}
|
moel@203
|
146 |
|
moel@203
|
147 |
timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
|
moel@191
|
148 |
}
|
moel@191
|
149 |
|
moel@275
|
150 |
private static Identifier CreateIdentifier(Vendor vendor,
|
moel@275
|
151 |
int processorIndex)
|
moel@275
|
152 |
{
|
moel@275
|
153 |
string s;
|
moel@275
|
154 |
switch (vendor) {
|
moel@275
|
155 |
case Vendor.AMD: s = "amdcpu"; break;
|
moel@275
|
156 |
case Vendor.Intel: s = "intelcpu"; break;
|
moel@275
|
157 |
default: s = "genericcpu"; break;
|
moel@275
|
158 |
}
|
moel@275
|
159 |
return new Identifier(s,
|
moel@275
|
160 |
processorIndex.ToString(CultureInfo.InvariantCulture));
|
moel@275
|
161 |
}
|
moel@275
|
162 |
|
moel@279
|
163 |
private void EstimateTimeStampCounterFrequency(out double frequency,
|
moel@279
|
164 |
out double error)
|
moel@279
|
165 |
{
|
moel@279
|
166 |
double f, e;
|
moel@279
|
167 |
|
moel@191
|
168 |
// preload the function
|
moel@279
|
169 |
EstimateTimeStampCounterFrequency(0, out f, out e);
|
moel@279
|
170 |
EstimateTimeStampCounterFrequency(0, out f, out e);
|
moel@191
|
171 |
|
moel@279
|
172 |
// estimate the frequency
|
moel@279
|
173 |
error = double.MaxValue;
|
moel@279
|
174 |
frequency = 0;
|
moel@279
|
175 |
for (int i = 0; i < 5; i++) {
|
moel@279
|
176 |
EstimateTimeStampCounterFrequency(0.025, out f, out e);
|
moel@279
|
177 |
if (e < error) {
|
moel@279
|
178 |
error = e;
|
moel@279
|
179 |
frequency = f;
|
moel@279
|
180 |
}
|
moel@279
|
181 |
|
moel@279
|
182 |
if (error < 1e-4)
|
moel@279
|
183 |
break;
|
moel@279
|
184 |
}
|
moel@191
|
185 |
}
|
moel@191
|
186 |
|
moel@279
|
187 |
private void EstimateTimeStampCounterFrequency(double timeWindow,
|
moel@279
|
188 |
out double frequency, out double error)
|
moel@279
|
189 |
{
|
moel@191
|
190 |
long ticks = (long)(timeWindow * Stopwatch.Frequency);
|
moel@236
|
191 |
ulong countBegin, countEnd;
|
moel@191
|
192 |
|
moel@191
|
193 |
long timeBegin = Stopwatch.GetTimestamp() +
|
moel@191
|
194 |
(long)Math.Ceiling(0.001 * ticks);
|
moel@191
|
195 |
long timeEnd = timeBegin + ticks;
|
moel@279
|
196 |
|
moel@191
|
197 |
while (Stopwatch.GetTimestamp() < timeBegin) { }
|
moel@236
|
198 |
countBegin = Opcode.Rdtsc();
|
moel@279
|
199 |
long afterBegin = Stopwatch.GetTimestamp();
|
moel@279
|
200 |
|
moel@191
|
201 |
while (Stopwatch.GetTimestamp() < timeEnd) { }
|
moel@236
|
202 |
countEnd = Opcode.Rdtsc();
|
moel@279
|
203 |
long afterEnd = Stopwatch.GetTimestamp();
|
moel@191
|
204 |
|
moel@279
|
205 |
double delta = (timeEnd - timeBegin);
|
moel@279
|
206 |
frequency = 1e-6 *
|
moel@279
|
207 |
(((double)(countEnd - countBegin)) * Stopwatch.Frequency) / delta;
|
moel@279
|
208 |
|
moel@279
|
209 |
double beginError = (afterBegin - timeBegin) / delta;
|
moel@279
|
210 |
double endError = (afterEnd - timeEnd) / delta;
|
moel@279
|
211 |
error = beginError + endError;
|
moel@191
|
212 |
}
|
moel@191
|
213 |
|
moel@236
|
214 |
|
moel@191
|
215 |
private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
|
moel@191
|
216 |
uint eax, edx;
|
moel@238
|
217 |
if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
|
moel@191
|
218 |
r.Append(" ");
|
moel@191
|
219 |
r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
|
moel@191
|
220 |
r.Append(" ");
|
moel@191
|
221 |
r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
|
moel@191
|
222 |
r.Append(" ");
|
moel@191
|
223 |
r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
|
moel@191
|
224 |
r.AppendLine();
|
moel@191
|
225 |
}
|
moel@191
|
226 |
}
|
moel@191
|
227 |
|
moel@191
|
228 |
protected virtual uint[] GetMSRs() {
|
moel@191
|
229 |
return null;
|
moel@191
|
230 |
}
|
moel@191
|
231 |
|
moel@191
|
232 |
public override string GetReport() {
|
moel@191
|
233 |
StringBuilder r = new StringBuilder();
|
moel@191
|
234 |
|
moel@191
|
235 |
switch (vendor) {
|
moel@195
|
236 |
case Vendor.AMD: r.AppendLine("AMD CPU"); break;
|
moel@191
|
237 |
case Vendor.Intel: r.AppendLine("Intel CPU"); break;
|
moel@191
|
238 |
default: r.AppendLine("Generic CPU"); break;
|
moel@191
|
239 |
}
|
moel@191
|
240 |
|
moel@191
|
241 |
r.AppendLine();
|
moel@191
|
242 |
r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
|
moel@191
|
243 |
r.AppendFormat("Number of Cores: {0}{1}", coreCount,
|
moel@191
|
244 |
Environment.NewLine);
|
moel@191
|
245 |
r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
|
moel@191
|
246 |
Environment.NewLine);
|
moel@191
|
247 |
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
|
moel@191
|
248 |
"Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
|
moel@201
|
249 |
r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
|
moel@201
|
250 |
isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
|
moel@191
|
251 |
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
|
moel@279
|
252 |
"Estimated Time Stamp Counter Frequency: {0} MHz",
|
moel@279
|
253 |
Math.Round(estimatedTimeStampCounterFrequency * 100) * 0.01));
|
moel@279
|
254 |
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
|
moel@279
|
255 |
"Estimated Time Stamp Counter Frequency Error: {0} Mhz",
|
moel@279
|
256 |
Math.Round(estimatedTimeStampCounterFrequency *
|
moel@279
|
257 |
estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5));
|
moel@279
|
258 |
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
|
moel@201
|
259 |
"Time Stamp Counter Frequency: {0} MHz",
|
moel@201
|
260 |
Math.Round(timeStampCounterFrequency * 100) * 0.01));
|
moel@191
|
261 |
r.AppendLine();
|
moel@191
|
262 |
|
moel@191
|
263 |
uint[] msrArray = GetMSRs();
|
moel@191
|
264 |
if (msrArray != null && msrArray.Length > 0) {
|
moel@191
|
265 |
for (int i = 0; i < cpuid.Length; i++) {
|
moel@191
|
266 |
r.AppendLine("MSR Core #" + (i + 1));
|
moel@191
|
267 |
r.AppendLine();
|
moel@191
|
268 |
r.AppendLine(" MSR EDX EAX");
|
moel@191
|
269 |
foreach (uint msr in msrArray)
|
moel@191
|
270 |
AppendMSRData(r, msr, cpuid[i][0].Thread);
|
moel@191
|
271 |
r.AppendLine();
|
moel@191
|
272 |
}
|
moel@191
|
273 |
}
|
moel@191
|
274 |
|
moel@191
|
275 |
return r.ToString();
|
moel@191
|
276 |
}
|
moel@191
|
277 |
|
moel@191
|
278 |
public override HardwareType HardwareType {
|
moel@191
|
279 |
get { return HardwareType.CPU; }
|
moel@191
|
280 |
}
|
moel@191
|
281 |
|
moel@236
|
282 |
public bool HasModelSpecificRegisters {
|
moel@236
|
283 |
get { return hasModelSpecificRegisters; }
|
moel@236
|
284 |
}
|
moel@236
|
285 |
|
moel@201
|
286 |
public bool HasTimeStampCounter {
|
moel@201
|
287 |
get { return hasTimeStampCounter; }
|
moel@201
|
288 |
}
|
moel@201
|
289 |
|
moel@201
|
290 |
public double TimeStampCounterFrequency {
|
moel@201
|
291 |
get { return timeStampCounterFrequency; }
|
moel@191
|
292 |
}
|
moel@191
|
293 |
|
moel@191
|
294 |
public override void Update() {
|
moel@222
|
295 |
if (hasTimeStampCounter && isInvariantTimeStampCounter) {
|
moel@236
|
296 |
|
moel@236
|
297 |
// make sure always the same thread is used
|
moel@238
|
298 |
ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
|
moel@222
|
299 |
|
moel@222
|
300 |
// read time before and after getting the TSC to estimate the error
|
moel@222
|
301 |
long firstTime = Stopwatch.GetTimestamp();
|
moel@236
|
302 |
ulong timeStampCount = Opcode.Rdtsc();
|
moel@191
|
303 |
long time = Stopwatch.GetTimestamp();
|
moel@222
|
304 |
|
moel@236
|
305 |
// restore the thread affinity mask
|
moel@238
|
306 |
ThreadAffinity.Set(mask);
|
moel@236
|
307 |
|
moel@191
|
308 |
double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
|
moel@222
|
309 |
double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
|
moel@222
|
310 |
|
moel@222
|
311 |
// only use data if they are measured accuarte enough (max 0.1ms delay)
|
moel@222
|
312 |
if (error < 0.0001) {
|
moel@222
|
313 |
|
moel@222
|
314 |
// ignore the first reading because there are no initial values
|
moel@222
|
315 |
// ignore readings with too large or too small time window
|
moel@222
|
316 |
if (lastTime != 0 && delta > 0.5 && delta < 2) {
|
moel@222
|
317 |
|
moel@222
|
318 |
// update the TSC frequency with the new value
|
moel@222
|
319 |
timeStampCounterFrequency =
|
moel@201
|
320 |
(timeStampCount - lastTimeStampCount) / (1e6 * delta);
|
moel@222
|
321 |
}
|
moel@191
|
322 |
|
moel@191
|
323 |
lastTimeStampCount = timeStampCount;
|
moel@191
|
324 |
lastTime = time;
|
moel@191
|
325 |
}
|
moel@191
|
326 |
}
|
moel@191
|
327 |
|
moel@191
|
328 |
if (cpuLoad.IsAvailable) {
|
moel@191
|
329 |
cpuLoad.Update();
|
moel@191
|
330 |
for (int i = 0; i < coreLoads.Length; i++)
|
moel@191
|
331 |
coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
|
moel@191
|
332 |
if (totalLoad != null)
|
moel@191
|
333 |
totalLoad.Value = cpuLoad.GetTotalLoad();
|
moel@191
|
334 |
}
|
moel@191
|
335 |
}
|
moel@266
|
336 |
|
moel@266
|
337 |
public virtual void Close() {
|
moel@266
|
338 |
|
moel@266
|
339 |
}
|
moel@191
|
340 |
}
|
moel@191
|
341 |
}
|