/* * Created on Jul 14, 2004 * Updated on Jan 8, 2011 * * 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. */ 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 java.util.Locale; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.util.FileUtil; import net.i2p.util.SystemVersion; /** * A class for retrieveing details about the CPU using the CPUID assembly instruction. * * Ref: http://en.wikipedia.org/wiki/Cpuid * * @author Iakin */ public class CPUID { /** did we load the native lib correctly? */ private static boolean _nativeOk = false; private static int _jcpuidVersion; /** * 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 * * Well, we really want to use Log so if you are one of those "other systems" * then comment out the I2PAppContext usage below. * * Set to false if not in router context, so scripts using TrustedUpdate * don't spew log messages. main() below overrides to true. */ private static boolean _doLog = System.getProperty("jcpuid.dontLog") == null && I2PAppContext.getGlobalContext().isRouterContext(); private static final boolean isX86 = SystemVersion.isX86(); private static final boolean isWindows = SystemVersion.isWindows(); private static final boolean isLinux = System.getProperty("os.name").toLowerCase(Locale.US).contains("linux"); private static final boolean isKFreebsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("kfreebsd"); private static final boolean isFreebsd = (!isKFreebsd) && System.getProperty("os.name").toLowerCase(Locale.US).contains("freebsd"); private static final boolean isNetbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("netbsd"); private static final boolean isOpenbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("openbsd"); private static final boolean isSunos = System.getProperty("os.name").toLowerCase(Locale.US).contains("sunos"); private static final boolean isMac = SystemVersion.isMac(); /** * This isn't always correct. * http://stackoverflow.com/questions/807263/how-do-i-detect-which-kind-of-jre-is-installed-32bit-vs-64bit * http://mark.koli.ch/2009/10/javas-osarch-system-property-is-the-bitness-of-the-jre-not-the-operating-system.html * http://mark.koli.ch/2009/10/reliably-checking-os-bitness-32-or-64-bit-on-windows-with-a-tiny-c-app.html * sun.arch.data.model not on all JVMs * sun.arch.data.model == 64 => 64 bit processor * sun.arch.data.model == 32 => A 32 bit JVM but could be either 32 or 64 bit processor or libs * os.arch contains "64" could be 32 or 64 bit libs */ private static final boolean is64 = SystemVersion.is64Bit(); 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); /** * Get the jbigi version, only available since jbigi version 3 * Caller must catch Throwable * @since 0.9.26 */ private native static int nativeJcpuidVersion(); /** * Get the jcpuid version * @return 0 if no jcpuid available, 2 if version not supported * @since 0.9.26 */ private static int fetchJcpuidVersion() { if (!_nativeOk) return 0; try { return nativeJcpuidVersion(); } catch (Throwable t) { return 2; } } /** * Return the jcpuid version * @return 0 if no jcpuid available, 2 if version not supported * @since 0.9.26 */ public static int getJcpuidVersion() { return _jcpuidVersion; } static String getCPUVendorID() { CPUIDResult c = doCPUID(0); StringBuilder sb= new StringBuilder(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(); } /** @return 0-15 */ static int getCPUFamily() { CPUIDResult c = doCPUID(1); return (c.EAX >> 8) & 0xf; } /** @return 0-15 */ static int getCPUModel() { CPUIDResult c = doCPUID(1); return (c.EAX >> 4) & 0xf; } /** * Only valid if family == 15, or, for Intel only, family == 6. * Left shift by 4 and then add model to get full model. * @return 0-15 */ static int getCPUExtendedModel() { CPUIDResult c = doCPUID(1); return (c.EAX >> 16) & 0xf; } /** @return 0-15 */ static int getCPUType() { CPUIDResult c = doCPUID(1); return (c.EAX >> 12) & 0xf; } /** * Only valid if family == 15. * Add family to get full family. * @return 0-255 */ static int getCPUExtendedFamily() { CPUIDResult c = doCPUID(1); return (c.EAX >> 20) & 0xff; } /** @return 0-15 */ static int getCPUStepping() { CPUIDResult c = doCPUID(1); return c.EAX & 0xf; } static int getEDXCPUFlags() { CPUIDResult c = doCPUID(1); return c.EDX; } static int getECXCPUFlags() { CPUIDResult c = doCPUID(1); return c.ECX; } static int getExtendedECXCPUFlags() { CPUIDResult c = doCPUID(0x80000001); return c.ECX; } /** @since 0.8.7 */ static int getExtendedEDXCPUFlags() { CPUIDResult c = doCPUID(0x80000001); return c.EDX; } /** * @since 0.9.26 */ static int getExtendedEBXFeatureFlags() { // Supposed to set ECX to 0 before calling? // But we don't have support for that in jcpuid. // And it works just fine without that. CPUIDResult c = doCPUID(7); return c.EBX; } /** * There's almost nothing in here. * @since 0.9.26 */ static int getExtendedECXFeatureFlags() { // Supposed to set ECX to 0 before calling? // But we don't have support for that in jcpuid. // And it works just fine without that. CPUIDResult c = doCPUID(7); return c.ECX; } /** * The model name string, up to 48 characters, as reported by * the processor itself. * * @return trimmed string, null if unsupported * @since 0.9.16 */ static String getCPUModelName() { CPUIDResult c = doCPUID(0x80000000); long maxSupported = c.EAX & 0xFFFFFFFFL; if (maxSupported < 0x80000004L) return null; StringBuilder buf = new StringBuilder(48); int[] regs = new int[4]; for (int fn = 0x80000002; fn <= 0x80000004; fn++) { c = doCPUID(fn); regs[0] = c.EAX; regs[1] = c.EBX; regs[2] = c.ECX; regs[3] = c.EDX; for (int i = 0; i < 4; i++) { int reg = regs[i]; for (int j = 0; j < 4; j++) { char ch = (char) (reg & 0xff); if (ch == 0) return buf.toString().trim(); buf.append(ch); reg >>= 8; } } } return buf.toString().trim(); } /** * 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 " + getLibraryPrefix() + "jcpuid " + getLibrarySuffix() + " file."); } String id = getCPUVendorID(); if(id.equals("CentaurHauls")) return new VIAInfoImpl(); if(!isX86) throw new UnknownCPUException("Failed to read CPU information from the system. The CPUID instruction exists on x86 CPUs only."); if(id.equals("AuthenticAMD")) return new AMDInfoImpl(); if(id.equals("GenuineIntel")) return new IntelInfoImpl(); throw new UnknownCPUException("Unknown CPU type: '" + id + '\''); } public static void main(String args[]) { _doLog = true; // this is too late to log anything from above String path = System.getProperty("java.library.path"); String name = getLibraryPrefix() + "jcpuid" + getLibrarySuffix(); System.out.println("Native library search path: " + path); if (_nativeOk) { String sep = System.getProperty("path.separator"); String[] paths = DataHelper.split(path, sep); for (String p : paths) { File f = new File(p, name); if (f.exists()) { System.out.println("Found native library: " + f); break; } } } else { System.out.println("Failed to retrieve CPUInfo. Please verify the existence of the " + name + " file in the library path, or set -Djava.library.path=. in the command line"); } System.out.println("JCPUID Version: " + _jcpuidVersion); System.out.println(" **CPUInfo**"); String mname = getCPUModelName(); if (mname != null) System.out.println("CPU Model Name: " + mname); String vendor = getCPUVendorID(); System.out.println("CPU Vendor: " + vendor); // http://en.wikipedia.org/wiki/Cpuid // http://web.archive.org/web/20110307080258/http://www.intel.com/Assets/PDF/appnote/241618.pdf // http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-2a-manual.pdf int family = getCPUFamily(); int model = getCPUModel(); if (family == 15 || (family == 6 && "GenuineIntel".equals(vendor))) { model += getCPUExtendedModel() << 4; } if (family == 15) { family += getCPUExtendedFamily(); } System.out.println("CPU Family: " + family); System.out.println("CPU Model: " + model); System.out.println("CPU Stepping: " + getCPUStepping()); System.out.println("CPU Flags (EDX): 0x" + Integer.toHexString(getEDXCPUFlags())); System.out.println("CPU Flags (ECX): 0x" + Integer.toHexString(getECXCPUFlags())); System.out.println("CPU Ext. Info. (EDX): 0x" + Integer.toHexString(getExtendedEDXCPUFlags())); System.out.println("CPU Ext. Info. (ECX): 0x" + Integer.toHexString(getExtendedECXCPUFlags())); System.out.println("CPU Ext. Feat. (EBX): 0x" + Integer.toHexString(getExtendedEBXFeatureFlags())); System.out.println("CPU Ext. Feat. (ECX): 0x" + Integer.toHexString(getExtendedECXFeatureFlags())); CPUInfo c = getInfo(); System.out.println("\n **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()); System.out.println("CPU has SSE3: " + c.hasSSE3()); System.out.println("CPU has SSE4.1: " + c.hasSSE41()); System.out.println("CPU has SSE4.2: " + c.hasSSE42()); System.out.println("CPU has SSE4A: " + c.hasSSE4A()); System.out.println("CPU has AES-NI: " + c.hasAES()); System.out.println("CPU has AVX: " + c.hasAVX()); System.out.println("CPU has AVX2: " + c.hasAVX2()); System.out.println("CPU has AVX512: " + c.hasAVX512()); System.out.println("CPU has ADX: " + c.hasADX()); System.out.println("CPU has TBM: " + c.hasTBM()); System.out.println("CPU has BMI1: " + c.hasBMI1()); System.out.println("CPU has BMI2: " + c.hasBMI2()); System.out.println("CPU has FMA3: " + c.hasFMA3()); System.out.println("CPU has MOVBE: " + c.hasMOVBE()); System.out.println("CPU has ABM: " + c.hasABM()); if(c instanceof IntelCPUInfo){ System.out.println("\n **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()); System.out.println("Is Atom-compatible: "+((IntelCPUInfo)c).IsAtomCompatible()); System.out.println("Is Pentium M compatible: "+((IntelCPUInfo)c).IsPentiumMCompatible()); System.out.println("Is Core2-compatible: "+((IntelCPUInfo)c).IsCore2Compatible()); System.out.println("Is Corei-compatible: "+((IntelCPUInfo)c).IsCoreiCompatible()); System.out.println("Is Sandy-compatible: "+((IntelCPUInfo)c).IsSandyCompatible()); System.out.println("Is Ivy-compatible: "+((IntelCPUInfo)c).IsIvyCompatible()); System.out.println("Is Haswell-compatible: "+((IntelCPUInfo)c).IsHaswellCompatible()); System.out.println("Is Broadwell-compatible: "+((IntelCPUInfo)c).IsBroadwellCompatible()); } if(c instanceof AMDCPUInfo){ System.out.println("\n **AMD-info**"); System.out.println("Is K6-compatible: "+((AMDCPUInfo)c).IsK6Compatible()); System.out.println("Is K6_2-compatible: "+((AMDCPUInfo)c).IsK6_2_Compatible()); System.out.println("Is K6_3-compatible: "+((AMDCPUInfo)c).IsK6_3_Compatible()); System.out.println("Is Geode-compatible: "+((AMDCPUInfo)c).IsGeodeCompatible()); System.out.println("Is Athlon-compatible: "+((AMDCPUInfo)c).IsAthlonCompatible()); System.out.println("Is Athlon64-compatible: "+((AMDCPUInfo)c).IsAthlon64Compatible()); System.out.println("Is Bobcat-compatible: "+((AMDCPUInfo)c).IsBobcatCompatible()); System.out.println("Is K10-compatible: "+((AMDCPUInfo)c).IsK10Compatible()); System.out.println("Is Jaguar-compatible: "+((AMDCPUInfo)c).IsJaguarCompatible()); System.out.println("Is Bulldozer-compatible: "+((AMDCPUInfo)c).IsBulldozerCompatible()); System.out.println("Is Piledriver-compatible: "+((AMDCPUInfo)c).IsPiledriverCompatible()); System.out.println("Is Steamroller-compatible: "+((AMDCPUInfo)c).IsSteamrollerCompatible()); System.out.println("Is Excavator-compatible: "+((AMDCPUInfo)c).IsExcavatorCompatible()); } } /** * <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 = Boolean.parseBoolean(wantedProp); if (wantNative) { boolean loaded = loadGeneric(); if (loaded) { _nativeOk = true; if (_doLog) System.err.println("INFO: Native CPUID library " + getLibraryMiddlePart() + " loaded from file"); } else { loaded = loadFromResource(); if (loaded) { _nativeOk = true; if (_doLog) System.err.println("INFO: Native CPUID library " + getResourceName() + " loaded from resource"); } else { _nativeOk = false; if (_doLog) System.err.println("WARNING: Native CPUID library jcpuid not loaded - will not be able to read CPU information using CPUID"); } } _jcpuidVersion = fetchJcpuidVersion(); } 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> * The file name must be (e.g. on linux) either libjcpuid.so or libjcpuid-x86-linux.so. * This method does not search for a filename with "_64" in it. * * @return true if it was loaded successfully, else false * */ private static final boolean loadGeneric() { try { System.loadLibrary("jcpuid"); return true; } catch (UnsatisfiedLinkError ule) { // fallthrough, try the OS-specific filename } // Don't bother trying a 64 bit filename variant. // 32 bit variant: // Note this is unlikely to succeed on a standard installation, since when we extract the library // in loadResource() below, we save it as jcpuid.dll / libcupid.so. // However, a distribution may put the file in, e.g., /usr/lib/jni/ // with the libjcpuid-x86-linux.so name. // Ubuntu packages now use libjcpuid.so //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. * We then attempt to copy the file from the temporary dir to the base install dir, * so we don't have to do this next time - but we don't complain if it fails, * so we transparently support read-only base dirs. * </p> * * This tries the 64 bit version first if we think we may be 64 bit. * Then it tries the 32 bit version. * * @return true if it was loaded successfully, else false * */ private static final boolean loadFromResource() { // Mac info: // Through 0.9.25, we had a libjcpuid-x86_64-osx.jnilib and a libjcpuid-x86-osx.jnilib file. // As of 0.9.26, we have a single libjcpuid-x86_64-osx.jnilib fat binary that has both 64- and 32-bit support. // For updates, the 0.9.27 update contained the new jbigi.jar. // However, in rare cases, a user may have skipped that update, going straight // from 0.9.26 to 0.9.28. Since we can't be sure, always try both for Mac. // getResourceName64() returns non-null for 64-bit OR for 32-bit Mac. // try 64 bit first, if getResourceName64() returns non-null String resourceName = getResourceName64(); if (resourceName != null) { boolean success = extractLoadAndCopy(resourceName); if (success) return true; if (_doLog) System.err.println("WARNING: Resource name [" + resourceName + "] was not found"); } // now try 32 bit resourceName = getResourceName(); boolean success = extractLoadAndCopy(resourceName); if (success) return true; if (_doLog) System.err.println("WARNING: Resource name [" + resourceName + "] was not found"); return false; } /** * Extract a single resource, copy it to a temp location in the file system, * and attempt to load it. If the load succeeds, copy it to the installation * directory. Return value reflects only load success - copy will fail silently. * * @return true if it was loaded successfully, else false. * @since 0.8.7 */ private static final boolean extractLoadAndCopy(String resourceName) { URL resource = CPUID.class.getClassLoader().getResource(resourceName); if (resource == null) return false; File outFile = null; FileOutputStream fos = null; String filename = getLibraryPrefix() + "jcpuid" + getLibrarySuffix(); try { InputStream libStream = resource.openStream(); outFile = new File(I2PAppContext.getGlobalContext().getTempDir(), filename); fos = new FileOutputStream(outFile); DataHelper.copy(libStream, fos); fos.close(); fos = null; System.load(outFile.getAbsolutePath());//System.load requires an absolute path to the lib } catch (UnsatisfiedLinkError ule) { if (_doLog) { System.err.println("WARNING: The resource " + resourceName + " was not a valid library for this platform " + ule); //ule.printStackTrace(); } if (outFile != null) outFile.delete(); return false; } catch (IOException ioe) { if (_doLog) { System.err.println("ERROR: Problem writing out the temporary native library data"); ioe.printStackTrace(); } if (outFile != null) outFile.delete(); return false; } finally { if (fos != null) { try { fos.close(); } catch (IOException ioe) {} } } // copy to install dir, ignore failure File newFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), filename); FileUtil.copy(outFile, newFile, false, true); return true; } /** @return non-null */ private static final String getResourceName() { return getLibraryPrefix() + getLibraryMiddlePart() + getLibrarySuffix(); } /** * @return null if not on a 64 bit platform (except Mac) * @since 0.8.7 */ private static final String getResourceName64() { // As of GMP 6, // libjcpuid-x86_64-osx.jnilib file is a fat binary that contains both 64- and 32-bit binaries // See loadFromResource() for more info. if (!is64 && !isMac) return null; return getLibraryPrefix() + get64LibraryMiddlePart() + getLibrarySuffix(); } private static final String getLibraryPrefix() { if(isWindows) return ""; else return "lib"; } private static final String getLibraryMiddlePart(){ if(isWindows) return "jcpuid-x86-windows"; // The convention on Windows if(isMac) { if(isX86) { // As of GMP6, // our libjcpuid-x86_64.osx.jnilib is a fat binary, // with the 32-bit lib in it also. // Not sure if that was on purpose... return "jcpuid-x86_64-osx"; // The convention on Intel Macs } // this will fail, we don't have any ppc libs, but we can't return null here. return "jcpuid-ppc-osx"; } if(isKFreebsd) return "jcpuid-x86-kfreebsd"; // The convention on kfreebsd... if(isFreebsd) return "jcpuid-x86-freebsd"; // The convention on freebsd... if(isNetbsd) return "jcpuid-x86-netbsd"; // The convention on netbsd... if(isOpenbsd) return "jcpuid-x86-openbsd"; // The convention on openbsd... if(isSunos) return "jcpuid-x86-solaris"; // The convention on SunOS //throw new RuntimeException("Dont know jcpuid library name for os type '"+System.getProperty("os.name")+"'"); // use linux as the default, don't throw exception return "jcpuid-x86-linux"; } /** @since 0.8.7 */ private static final String get64LibraryMiddlePart() { if(isWindows) return "jcpuid-x86_64-windows"; if(isKFreebsd) return "jcpuid-x86_64-kfreebsd"; if(isFreebsd) return "jcpuid-x86_64-freebsd"; if(isNetbsd) return "jcpuid-x86_64-netbsd"; if(isOpenbsd) return "jcpuid-x86_64-openbsd"; if(isMac){ if(isX86){ return "jcpuid-x86_64-osx"; } // this will fail, we don't have any ppc libs, but we can't return null here. return "jcpuid-ppc_64-osx"; } if(isSunos) return "jcpuid-x86_64-solaris"; // use linux as the default, don't throw exception return "jcpuid-x86_64-linux"; } private static final String getLibrarySuffix() { if(isWindows) return ".dll"; if(isMac) return ".jnilib"; else return ".so"; } }