/*
* Created on Jul 14, 2004
*/
package freenet.support.CPUInformation;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
//import freenet.support.Logger;
import thaw.core.Logger;
/**
* @author Iakin
* A class for retrieveing details about the CPU using the CPUID assembly instruction.
* A good resource for information about the CPUID instruction can be found here:
* http://www.paradicesoftware.com/specs/cpuid/index.htm
*
* free (adj.): unencumbered; not under the control of others
* Written by Iakin in 2004 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*/
public class CPUID {
/** did we load the native lib correctly? */
private static boolean _nativeOk = false;
/**
* do we want to dump some basic success/failure info to stderr during
* initialization? this would otherwise use the Log component, but this makes
* it easier for other systems to reuse this class
*/
private static final boolean _doLog = true;
private static final boolean isX86 = System.getProperty("os.arch").toLowerCase().matches("(i?[x0-9]86(_64)?|amd64)");
static
{
loadNative();
}
//A class that can (amongst other things I assume) represent the state of the
//different CPU registers after a call to the CPUID assembly method
protected static class CPUIDResult {
final int EAX;
final int EBX;
final int ECX;
final int EDX;
CPUIDResult(int EAX,int EBX,int ECX, int EDX)
{
this.EAX = EAX;
this.EBX = EBX;
this.ECX = ECX;
this.EDX = EDX;
}
}
/**Calls the indicated CPUID function and returns the result of the execution
*
* @param iFunction The CPUID function to call, should be 0 or larger
* @return The contents of the CPU registers after the call to the CPUID function
*/
private static native CPUIDResult doCPUID(int iFunction);
private static String getCPUVendorID()
{
CPUIDResult c = doCPUID(0);
StringBuffer sb= new StringBuffer(13);
sb.append((char)( c.EBX & 0xFF));
sb.append((char)((c.EBX >> 8) & 0xFF));
sb.append((char)((c.EBX >> 16) & 0xFF));
sb.append((char)((c.EBX >> 24) & 0xFF));
sb.append((char)( c.EDX & 0xFF));
sb.append((char)((c.EDX >> 8) & 0xFF));
sb.append((char)((c.EDX >> 16) & 0xFF));
sb.append((char)((c.EDX >> 24) & 0xFF));
sb.append((char)( c.ECX & 0xFF));
sb.append((char)((c.ECX >> 8) & 0xFF));
sb.append((char)((c.ECX >> 16) & 0xFF));
sb.append((char)((c.ECX >> 24) & 0xFF));
return sb.toString();
}
private static int getCPUFamily()
{
CPUIDResult c = doCPUID(1);
return (c.EAX >> 8) & 0xf;
}
private static int getCPUModel()
{
CPUIDResult c = doCPUID(1);
return (c.EAX >> 4) & 0xf;
}
private static int getCPUExtendedFamily()
{
CPUIDResult c = doCPUID(1);
return (c.EAX >> 20) & 0xff;
}
private static int getCPUStepping()
{
CPUIDResult c = doCPUID(1);
return c.EAX & 0xf;
}
private static int getCPUFlags()
{
CPUIDResult c = doCPUID(1);
return c.EDX;
}
//Returns a CPUInfo item for the current type of CPU
//If I could I would declare this method in a interface named
//CPUInfoProvider and implement that interface in this class.
//This would make it easier for other people to understand that there
//is nothing preventing them from coding up new providers, probably using
//other detection methods than the x86-only CPUID instruction
public static CPUInfo getInfo() throws UnknownCPUException
{
if(!_nativeOk)
throw new UnknownCPUException("Failed to read CPU information from the system. Please verify the existence of the jcpuid dll/so.");
if(!isX86)
throw new UnknownCPUException("Failed to read CPU information from the system. The CPUID instruction exists on x86 CPU's only");
if(getCPUVendorID().equals("AuthenticAMD"))
return new AMDInfoImpl();
if(getCPUVendorID().equals("GenuineIntel"))
return new IntelInfoImpl();
throw new UnknownCPUException("Unknown CPU type: '"+getCPUVendorID()+ '\'');
}
protected abstract static class CPUIDCPUInfo
{
public String getVendor()
{
return getCPUVendorID();
}
public boolean hasMMX(){
return (getCPUFlags() & 0x800000) != 0; //Bit 23
}
public boolean hasSSE(){
return (getCPUFlags() & 0x2000000) != 0; //Bit 25
}
public boolean hasSSE2(){
return (getCPUFlags() & 0x4000000) != 0; //Bit 26
}
}
protected static class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
{
public boolean IsK6Compatible()
{
return (getCPUFamily() >= 5) && (getCPUModel() >= 6);
}
public boolean IsK6_2_Compatible()
{
return (getCPUFamily() >= 5) && (getCPUModel() >= 8);
}
public boolean IsK6_3_Compatible()
{
return (getCPUFamily() >= 5) && (getCPUModel() >= 9);
}
public boolean IsAthlonCompatible()
{
return getCPUFamily() >= 6;
}
public boolean IsAthlon64Compatible()
{
return (getCPUFamily() == 15) && (getCPUExtendedFamily() == 0);
}
public String getCPUModelString() throws UnknownCPUException
{
if(getCPUFamily() == 4){
switch(getCPUModel()){
case 3:
return "486 DX/2";
case 7:
return "486 DX/2-WB";
case 8:
return "486 DX/4";
case 9:
return "486 DX/4-WB";
case 14:
return "Am5x86-WT";
case 15:
return "Am5x86-WB";
}
}
if(getCPUFamily() == 5){
switch(getCPUModel()){
case 0:
return "K5/SSA5";
case 1:
return "K5";
case 2:
return "K5";
case 3:
return "K5";
case 6:
return "K6";
case 7:
return "K6";
case 8:
return "K6-2";
case 9:
return "K6-3";
case 13:
return "K6-2+ or K6-III+";
}
}
if(getCPUFamily() == 6){
switch(getCPUModel()){
case 0:
return "Athlon (250 nm)";
case 1:
return "Athlon (250 nm)";
case 2:
return "Athlon (180 nm)";
case 3:
return "Duron";
case 4:
return "Athlon (Thunderbird)";
case 6:
return "Athlon (Palamino)";
case 7:
return "Duron (Morgan)";
case 8:
return "Athlon (Thoroughbred)";
case 10:
return "Athlon (Barton)";
}
}
if(getCPUFamily() == 15){
if(getCPUExtendedFamily() == 0){
switch(getCPUModel()){
case 4:
return "Athlon 64";
case 5:
return "Athlon 64 FX Opteron";
case 12:
return "AMD Athlon(tm) 64 Processor 3000+";
}
}
}
throw new UnknownCPUException("Unknown AMD CPU; Family="+getCPUFamily()+", Model="+getCPUModel());
}
}
protected static class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
{
public boolean IsPentiumCompatible()
{
return getCPUFamily() >= 5;
}
public boolean IsPentiumMMXCompatible()
{
return IsPentium2Compatible() || ((getCPUFamily() == 5) && ((getCPUModel() ==4) || (getCPUModel() == 8)));
}
public boolean IsPentium2Compatible()
{
return (getCPUFamily() > 6) || ((getCPUFamily() == 6) && (getCPUModel() >=3));
}
public boolean IsPentium3Compatible()
{
return (getCPUFamily() > 6) || ((getCPUFamily() == 6) && (getCPUModel() >=7));
}
public boolean IsPentium4Compatible()
{
return getCPUFamily() >= 15;
}
public String getCPUModelString() throws UnknownCPUException {
if(getCPUFamily() == 4){
switch(getCPUModel()){
case 0:
return "486 DX-25/33";
case 1:
return "486 DX-50";
case 2:
return "486 SX";
case 3:
return "486 DX/2";
case 4:
return "486 SL";
case 5:
return "486 SX/2";
case 7:
return "486 DX/2-WB";
case 8:
return "486 DX/4";
case 9:
return "486 DX/4-WB";
}
}
if(getCPUFamily() == 5){
switch(getCPUModel()){
case 0:
return "Pentium 60/66 A-step";
case 1:
return "Pentium 60/66";
case 2:
return "Pentium 75 - 200";
case 3:
return "OverDrive PODP5V83";
case 4:
return "Pentium MMX";
case 7:
return "Mobile Pentium 75 - 200";
case 8:
return "Mobile Pentium MMX";
}
}
if(getCPUFamily() == 6){
switch(getCPUModel()){
case 0:
return "Pentium Pro A-step";
case 1:
return "Pentium Pro";
case 3:
return "Pentium II (Klamath)";
case 5:
return "Pentium II (Deschutes), Celeron (Covington), Mobile Pentium II (Dixon)";
case 6:
return "Mobile Pentium II, Celeron (Mendocino)";
case 7:
return "Pentium III (Katmai)";
case 8:
return "Pentium III (Coppermine), Celeron w/SSE";
case 9:
return "Mobile Pentium III";
case 10:
return "Pentium III Xeon (Cascades)";
case 11:
return "Pentium III (130 nm)";
}
}
if(getCPUFamily() == 7){
switch(getCPUModel()){
//Itanium.. TODO
}
}
if(getCPUFamily() == 15){
if(getCPUExtendedFamily() == 0){
switch(getCPUModel()){
case 0:
return "Pentium IV (180 nm)";
case 1:
return "Pentium IV (180 nm)";
case 2:
return "Pentium IV (130 nm)";
case 3:
return "Pentium IV (90 nm)";
}
}
if(getCPUExtendedFamily() == 1){
switch(getCPUModel()){
// Itanium 2.. TODO
}
}
}
throw new UnknownCPUException("Unknown Intel CPU; Family="+getCPUFamily()+", Model="+getCPUModel());
}
}
public static void main(String args[])
{
System.out.println(System.getProperty("os.arch").toLowerCase());
if(!_nativeOk){
System.out.println("**Failed to retrieve CPUInfo. Please verify the existence of jcpuid dll/so**");
}
System.out.println("**CPUInfo**");
System.out.println("CPU Vendor: " + getCPUVendorID());
System.out.println("CPU Family: " + getCPUFamily());
System.out.println("CPU Model: " + getCPUModel());
System.out.println("CPU Stepping: " + getCPUStepping());
System.out.println("CPU Flags: " + getCPUFlags());
CPUInfo c = getInfo();
System.out.println(" **More CPUInfo**");
System.out.println(" CPU model string: " + c.getCPUModelString());
System.out.println(" CPU has MMX: " + c.hasMMX());
System.out.println(" CPU has SSE: " + c.hasSSE());
System.out.println(" CPU has SSE2: " + c.hasSSE2());
if(c instanceof IntelCPUInfo){
System.out.println(" **Intel-info**");
System.out.println(" Is pII-compatible: "+((IntelCPUInfo)c).IsPentium2Compatible());
System.out.println(" Is pIII-compatible: "+((IntelCPUInfo)c).IsPentium3Compatible());
System.out.println(" Is pIV-compatible: "+((IntelCPUInfo)c).IsPentium4Compatible());
}
if(c instanceof AMDCPUInfo){
System.out.println(" **AMD-info**");
System.out.println(" Is Athlon-compatible: "+((AMDCPUInfo)c).IsAthlonCompatible());
System.out.println(" Is Athlon64-compatible: "+((AMDCPUInfo)c).IsAthlon64Compatible());
}
}
/**
* <p>Do whatever we can to load up the native library.
* If it can find a custom built jcpuid.dll / libjcpuid.so, it'll use that. Otherwise
* it'll try to look in the classpath for the correct library (see loadFromResource).
* If the user specifies -Djcpuid.enable=false it'll skip all of this.</p>
*
*/
private static final void loadNative() {
try{
String wantedProp = System.getProperty("jcpuid.enable", "true");
boolean wantNative = "true".equalsIgnoreCase(wantedProp);
if (wantNative) {
boolean loaded = loadFromResource();
if (loaded) {
_nativeOk = true;
if (_doLog)
System.err.println("INFO: Native CPUID library '"+getResourceName()+"' loaded from resource");
} else {
loaded = loadGeneric();
if (loaded) {
_nativeOk = true;
if (_doLog)
System.err.println("INFO: Native CPUID library '"+getLibraryMiddlePart()+"' loaded from somewhere in the path");
} else {
_nativeOk = false;
if (_doLog)
System.err.println("WARN: Native CPUID library jcpuid not loaded - will not be able to read CPU information using CPUID");
}
}
} else {
if (_doLog)
System.err.println("INFO: Native CPUID library jcpuid not loaded - will not be able to read CPU information using CPUID");
}
}catch(Exception e){
if (_doLog)
System.err.println("INFO: Native CPUID library jcpuid not loaded, reason: '"+e.getMessage()+"' - will not be able to read CPU information using CPUID");
}
}
/**
* <p>Try loading it from an explictly built jcpuid.dll / libjcpuid.so</p>
*
* @return true if it was loaded successfully, else false
*
*/
private static final boolean loadGeneric() {
try {
System.loadLibrary(getLibraryMiddlePart());
return true;
} catch (UnsatisfiedLinkError ule) {
return false;
}
}
/**
* <p>Check all of the jars in the classpath for the jcpuid dll/so.
* This file should be stored in the resource in the same package as this class.
*
* <p>This is a pretty ugly hack, using the general technique illustrated by the
* onion FEC libraries. It works by pulling the resource, writing out the
* byte stream to a temporary file, loading the native library from that file,
* then deleting the file.</p>
*
* @return true if it was loaded successfully, else false
*
*/
private static final boolean loadFromResource() {
String resourceName = getResourceName();
if (resourceName == null) return false;
URL resource = CPUID.class.getClassLoader().getResource(resourceName);
if (resource == null) {
if (_doLog)
System.err.println("ERROR: Resource name [" + resourceName + "] was not found");
return false;
}
File outFile = null;
try {
InputStream libStream = resource.openStream();
try{
outFile = File.createTempFile("jcpuid", "lib.tmp");
}catch (IOException e){
Logger.error("CPUID", "CPUID : Can't create the temporary file in "+System.getProperty("java.io.tmpdir")+" trying something else now.");
outFile = new File("cpuid-lib.tmp");
}
FileOutputStream fos = new FileOutputStream(outFile);
byte buf[] = new byte[4096*1024];
while (true) {
int read = libStream.read(buf);
if (read < 0) break;
fos.write(buf, 0, read);
}
fos.close();
System.load(outFile.getAbsolutePath());//System.load requires an absolute path to the lib
return true;
} catch (UnsatisfiedLinkError ule) {
if (_doLog) {
System.err.println("ERROR: The resource " + resourceName
+ " was not a valid library for this platform");
ule.printStackTrace();
}
return false;
} catch (IOException ioe) {
if (_doLog) {
System.err.println("ERROR: Problem writing out the temporary native library data");
ioe.printStackTrace();
}
return false;
} finally {
if (outFile != null) {
outFile.deleteOnExit();
}
}
}
private static final String getResourceName()
{
String pname = CPUID.class.getPackage().getName().replace('.','/');
return pname+ '/' +getLibraryPrefix()+getLibraryMiddlePart()+ '.' +getLibrarySuffix();
}
private static final String getLibraryPrefix()
{
boolean isWindows =System.getProperty("os.name").toLowerCase().indexOf("windows") != -1;
if(isWindows)
return "";
else
return "lib";
}
private static final String getLibraryMiddlePart(){
boolean isWindows =(System.getProperty("os.name").toLowerCase().indexOf("windows") != -1);
boolean isLinux =(System.getProperty("os.name").toLowerCase().indexOf("linux") != -1);
boolean isFreebsd =(System.getProperty("os.name").toLowerCase().indexOf("freebsd") != -1);
boolean isMacOS =(System.getProperty("os.name").toLowerCase().indexOf("mac os x") != -1);
if(isWindows)
return "jcpuid-x86-windows"; // The convention on Windows
if(isLinux)
return "jcpuid-x86-linux"; // The convention on linux...
if(isFreebsd)
return "jcpuid-x86-freebsd"; // The convention on freebsd...
if(isMacOS)
return "jcpuid-x86-osx"; // The convention on Mac OS X...
throw new RuntimeException("Dont know jcpuid library name for os type '"+System.getProperty("os.name")+ '\'');
}
private static final String getLibrarySuffix()
{
boolean isWindows =System.getProperty("os.name").toLowerCase().indexOf("windows") != -1;
if(isWindows)
return "dll";
else
return "so";
}
}