2010-09-20 19:28:25 +00:00
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
|
|
|
|
|
|
The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
|
1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
|
the License. You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.mozilla.org/MPL/
|
|
|
|
|
|
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
|
for the specific language governing rights and limitations under the License.
|
|
|
|
|
|
|
|
|
|
The Original Code is the Open Hardware Monitor code.
|
|
|
|
|
|
|
|
|
|
The Initial Developer of the Original Code is
|
|
|
|
|
Michael Möller <m.moeller@gmx.ch>.
|
|
|
|
|
Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
|
the Initial Developer. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
Contributor(s):
|
|
|
|
|
|
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
|
|
|
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
|
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
|
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
|
of those above. If you wish to allow use of your version of this file only
|
|
|
|
|
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
|
use your version of this file under the terms of the MPL, indicate your
|
|
|
|
|
decision by deleting the provisions above and replace them with the notice
|
|
|
|
|
and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
|
the provisions above, a recipient may use your version of this file under
|
|
|
|
|
the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Globalization;
|
2010-10-31 22:08:47 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
namespace OpenHardwareMonitor.Hardware.CPU {
|
2010-09-21 20:32:36 +00:00
|
|
|
|
internal class GenericCPU : Hardware {
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
|
|
|
|
protected readonly CPUID[][] cpuid;
|
|
|
|
|
|
|
|
|
|
protected readonly uint family;
|
|
|
|
|
protected readonly uint model;
|
|
|
|
|
protected readonly uint stepping;
|
|
|
|
|
|
|
|
|
|
protected readonly int processorIndex;
|
|
|
|
|
protected readonly int coreCount;
|
|
|
|
|
protected readonly string name;
|
|
|
|
|
|
2010-10-31 22:08:47 +00:00
|
|
|
|
private readonly bool hasModelSpecificRegisters;
|
|
|
|
|
|
2010-09-30 16:51:09 +00:00
|
|
|
|
private readonly bool hasTimeStampCounter;
|
|
|
|
|
private readonly bool isInvariantTimeStampCounter;
|
|
|
|
|
private readonly double estimatedTimeStampCounterFrequency;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
|
|
|
|
private ulong lastTimeStampCount;
|
|
|
|
|
private long lastTime;
|
2010-09-30 16:51:09 +00:00
|
|
|
|
private double timeStampCounterFrequency;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
2010-09-21 20:32:36 +00:00
|
|
|
|
private readonly Vendor vendor;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
|
|
|
|
private readonly CPULoad cpuLoad;
|
|
|
|
|
private readonly Sensor totalLoad;
|
|
|
|
|
private readonly Sensor[] coreLoads;
|
|
|
|
|
|
|
|
|
|
protected string CoreString(int i) {
|
|
|
|
|
if (coreCount == 1)
|
|
|
|
|
return "CPU Core";
|
|
|
|
|
else
|
|
|
|
|
return "CPU Core #" + (i + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings) {
|
|
|
|
|
this.cpuid = cpuid;
|
|
|
|
|
|
|
|
|
|
this.vendor = cpuid[0][0].Vendor;
|
|
|
|
|
|
|
|
|
|
this.family = cpuid[0][0].Family;
|
|
|
|
|
this.model = cpuid[0][0].Model;
|
|
|
|
|
this.stepping = cpuid[0][0].Stepping;
|
|
|
|
|
|
|
|
|
|
this.processorIndex = processorIndex;
|
|
|
|
|
this.coreCount = cpuid.Length;
|
2010-10-31 22:08:47 +00:00
|
|
|
|
this.name = cpuid[0][0].Name;
|
|
|
|
|
|
|
|
|
|
// check if processor has MSRs
|
|
|
|
|
if (cpuid[0][0].Data.GetLength(0) > 1
|
|
|
|
|
&& (cpuid[0][0].Data[1, 3] & 0x20) != 0)
|
|
|
|
|
hasModelSpecificRegisters = true;
|
|
|
|
|
else
|
|
|
|
|
hasModelSpecificRegisters = false;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
2010-09-30 16:51:09 +00:00
|
|
|
|
// check if processor has a TSC
|
2010-09-20 19:28:25 +00:00
|
|
|
|
if (cpuid[0][0].Data.GetLength(0) > 1
|
|
|
|
|
&& (cpuid[0][0].Data[1, 3] & 0x10) != 0)
|
2010-09-30 16:51:09 +00:00
|
|
|
|
hasTimeStampCounter = true;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
else
|
2010-09-30 16:51:09 +00:00
|
|
|
|
hasTimeStampCounter = false;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
2010-09-30 16:51:09 +00:00
|
|
|
|
// check if processor supports an invariant TSC
|
2010-09-20 19:28:25 +00:00
|
|
|
|
if (cpuid[0][0].ExtData.GetLength(0) > 7
|
|
|
|
|
&& (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
|
2010-09-30 16:51:09 +00:00
|
|
|
|
isInvariantTimeStampCounter = true;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
else
|
2010-09-30 16:51:09 +00:00
|
|
|
|
isInvariantTimeStampCounter = false;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
|
|
|
|
if (coreCount > 1)
|
|
|
|
|
totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
|
|
|
|
|
else
|
|
|
|
|
totalLoad = null;
|
|
|
|
|
coreLoads = new Sensor[coreCount];
|
|
|
|
|
for (int i = 0; i < coreLoads.Length; i++)
|
|
|
|
|
coreLoads[i] = new Sensor(CoreString(i), i + 1,
|
|
|
|
|
SensorType.Load, this, settings);
|
|
|
|
|
cpuLoad = new CPULoad(cpuid);
|
|
|
|
|
if (cpuLoad.IsAvailable) {
|
|
|
|
|
foreach (Sensor sensor in coreLoads)
|
|
|
|
|
ActivateSensor(sensor);
|
|
|
|
|
if (totalLoad != null)
|
|
|
|
|
ActivateSensor(totalLoad);
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-01 19:01:09 +00:00
|
|
|
|
if (hasTimeStampCounter) {
|
|
|
|
|
estimatedTimeStampCounterFrequency =
|
2010-10-14 17:30:51 +00:00
|
|
|
|
EstimateTimeStampCounterFrequency();
|
2010-10-01 19:01:09 +00:00
|
|
|
|
} else {
|
2010-09-30 16:51:09 +00:00
|
|
|
|
estimatedTimeStampCounterFrequency = 0;
|
2010-10-01 19:01:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-09-30 16:51:09 +00:00
|
|
|
|
private static double EstimateTimeStampCounterFrequency() {
|
2010-09-20 19:28:25 +00:00
|
|
|
|
// preload the function
|
2010-09-30 16:51:09 +00:00
|
|
|
|
EstimateTimeStampCounterFrequency(0);
|
|
|
|
|
EstimateTimeStampCounterFrequency(0);
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
2010-09-30 16:51:09 +00:00
|
|
|
|
// estimate the frequency in MHz
|
|
|
|
|
List<double> estimatedFrequency = new List<double>(3);
|
2010-09-20 19:28:25 +00:00
|
|
|
|
for (int i = 0; i < 3; i++)
|
2010-09-30 16:51:09 +00:00
|
|
|
|
estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
|
|
|
|
|
estimatedFrequency.Sort();
|
|
|
|
|
return estimatedFrequency[1];
|
2010-09-20 19:28:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-09-30 16:51:09 +00:00
|
|
|
|
private static double EstimateTimeStampCounterFrequency(double timeWindow) {
|
2010-09-20 19:28:25 +00:00
|
|
|
|
long ticks = (long)(timeWindow * Stopwatch.Frequency);
|
2010-10-31 22:08:47 +00:00
|
|
|
|
ulong countBegin, countEnd;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
|
|
|
|
Thread.BeginThreadAffinity();
|
|
|
|
|
long timeBegin = Stopwatch.GetTimestamp() +
|
|
|
|
|
(long)Math.Ceiling(0.001 * ticks);
|
|
|
|
|
long timeEnd = timeBegin + ticks;
|
|
|
|
|
while (Stopwatch.GetTimestamp() < timeBegin) { }
|
2010-10-31 22:08:47 +00:00
|
|
|
|
countBegin = Opcode.Rdtsc();
|
2010-09-20 19:28:25 +00:00
|
|
|
|
while (Stopwatch.GetTimestamp() < timeEnd) { }
|
2010-10-31 22:08:47 +00:00
|
|
|
|
countEnd = Opcode.Rdtsc();
|
2010-09-20 19:28:25 +00:00
|
|
|
|
Thread.EndThreadAffinity();
|
|
|
|
|
|
|
|
|
|
return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
|
|
|
|
|
(timeEnd - timeBegin);
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-31 22:08:47 +00:00
|
|
|
|
|
2010-09-20 19:28:25 +00:00
|
|
|
|
private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
|
|
|
|
|
uint eax, edx;
|
2010-10-31 22:08:47 +00:00
|
|
|
|
if (Ring0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
|
2010-09-20 19:28:25 +00:00
|
|
|
|
r.Append(" ");
|
|
|
|
|
r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
|
|
|
|
|
r.Append(" ");
|
|
|
|
|
r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
|
|
|
|
|
r.Append(" ");
|
|
|
|
|
r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
|
|
|
|
|
r.AppendLine();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual uint[] GetMSRs() {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string GetReport() {
|
|
|
|
|
StringBuilder r = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
switch (vendor) {
|
2010-09-21 20:32:36 +00:00
|
|
|
|
case Vendor.AMD: r.AppendLine("AMD CPU"); break;
|
2010-09-20 19:28:25 +00:00
|
|
|
|
case Vendor.Intel: r.AppendLine("Intel CPU"); break;
|
|
|
|
|
default: r.AppendLine("Generic CPU"); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.AppendLine();
|
|
|
|
|
r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
|
|
|
|
|
r.AppendFormat("Number of Cores: {0}{1}", coreCount,
|
|
|
|
|
Environment.NewLine);
|
|
|
|
|
r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
|
|
|
|
|
Environment.NewLine);
|
|
|
|
|
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
|
|
|
|
|
"Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
|
2010-09-30 16:51:09 +00:00
|
|
|
|
r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
|
|
|
|
|
isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
|
2010-09-20 19:28:25 +00:00
|
|
|
|
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
|
2010-09-30 16:51:09 +00:00
|
|
|
|
"Time Stamp Counter Frequency: {0} MHz",
|
|
|
|
|
Math.Round(timeStampCounterFrequency * 100) * 0.01));
|
2010-09-20 19:28:25 +00:00
|
|
|
|
r.AppendLine();
|
|
|
|
|
|
|
|
|
|
uint[] msrArray = GetMSRs();
|
|
|
|
|
if (msrArray != null && msrArray.Length > 0) {
|
|
|
|
|
for (int i = 0; i < cpuid.Length; i++) {
|
|
|
|
|
r.AppendLine("MSR Core #" + (i + 1));
|
|
|
|
|
r.AppendLine();
|
|
|
|
|
r.AppendLine(" MSR EDX EAX");
|
|
|
|
|
foreach (uint msr in msrArray)
|
|
|
|
|
AppendMSRData(r, msr, cpuid[i][0].Thread);
|
|
|
|
|
r.AppendLine();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return r.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override Identifier Identifier {
|
|
|
|
|
get {
|
|
|
|
|
string s;
|
|
|
|
|
switch (vendor) {
|
|
|
|
|
case Vendor.AMD: s = "amdcpu"; break;
|
|
|
|
|
case Vendor.Intel: s = "intelcpu"; break;
|
|
|
|
|
default: s = "genericcpu"; break;
|
|
|
|
|
}
|
|
|
|
|
return new Identifier(s,
|
|
|
|
|
processorIndex.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string Name {
|
|
|
|
|
get { return name; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override HardwareType HardwareType {
|
|
|
|
|
get { return HardwareType.CPU; }
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-31 22:08:47 +00:00
|
|
|
|
public bool HasModelSpecificRegisters {
|
|
|
|
|
get { return hasModelSpecificRegisters; }
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-30 16:51:09 +00:00
|
|
|
|
public bool HasTimeStampCounter {
|
|
|
|
|
get { return hasTimeStampCounter; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public double TimeStampCounterFrequency {
|
|
|
|
|
get { return timeStampCounterFrequency; }
|
2010-09-20 19:28:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Update() {
|
2010-10-14 17:30:51 +00:00
|
|
|
|
if (hasTimeStampCounter && isInvariantTimeStampCounter) {
|
2010-10-31 22:08:47 +00:00
|
|
|
|
|
|
|
|
|
// make sure always the same thread is used
|
|
|
|
|
IntPtr thread = NativeMethods.GetCurrentThread();
|
|
|
|
|
UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
|
|
|
|
|
(UIntPtr)(1L << cpuid[0][0].Thread));
|
2010-10-14 17:30:51 +00:00
|
|
|
|
|
|
|
|
|
// read time before and after getting the TSC to estimate the error
|
|
|
|
|
long firstTime = Stopwatch.GetTimestamp();
|
2010-10-31 22:08:47 +00:00
|
|
|
|
ulong timeStampCount = Opcode.Rdtsc();
|
2010-09-20 19:28:25 +00:00
|
|
|
|
long time = Stopwatch.GetTimestamp();
|
2010-10-14 17:30:51 +00:00
|
|
|
|
|
2010-10-31 22:08:47 +00:00
|
|
|
|
// restore the thread affinity mask
|
|
|
|
|
NativeMethods.SetThreadAffinityMask(thread, mask);
|
|
|
|
|
|
2010-09-20 19:28:25 +00:00
|
|
|
|
double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
|
2010-10-14 17:30:51 +00:00
|
|
|
|
double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
|
|
|
|
|
|
|
|
|
|
// only use data if they are measured accuarte enough (max 0.1ms delay)
|
|
|
|
|
if (error < 0.0001) {
|
|
|
|
|
|
|
|
|
|
// ignore the first reading because there are no initial values
|
|
|
|
|
// ignore readings with too large or too small time window
|
|
|
|
|
if (lastTime != 0 && delta > 0.5 && delta < 2) {
|
|
|
|
|
|
|
|
|
|
// update the TSC frequency with the new value
|
|
|
|
|
timeStampCounterFrequency =
|
2010-09-30 16:51:09 +00:00
|
|
|
|
(timeStampCount - lastTimeStampCount) / (1e6 * delta);
|
2010-10-14 17:30:51 +00:00
|
|
|
|
}
|
2010-09-20 19:28:25 +00:00
|
|
|
|
|
|
|
|
|
lastTimeStampCount = timeStampCount;
|
|
|
|
|
lastTime = time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cpuLoad.IsAvailable) {
|
|
|
|
|
cpuLoad.Update();
|
|
|
|
|
for (int i = 0; i < coreLoads.Length; i++)
|
|
|
|
|
coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
|
|
|
|
|
if (totalLoad != null)
|
|
|
|
|
totalLoad.Value = cpuLoad.GetTotalLoad();
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-31 22:08:47 +00:00
|
|
|
|
|
|
|
|
|
private static class NativeMethods {
|
|
|
|
|
private const string KERNEL = "kernel32.dll";
|
|
|
|
|
|
|
|
|
|
[DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
|
|
|
|
|
public static extern UIntPtr
|
|
|
|
|
SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
|
|
|
|
|
|
|
|
|
|
[DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
|
|
|
|
|
public static extern IntPtr GetCurrentThread();
|
|
|
|
|
}
|
2010-09-20 19:28:25 +00:00
|
|
|
|
}
|
|
|
|
|
}
|