/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.ext.jvmti; import static com.sun.max.platform.Platform.*; import static com.sun.max.vm.ext.jvmti.JVMTIConstants.*; import com.sun.max.memory.*; import com.sun.max.platform.*; import com.sun.max.unsafe.*; import com.sun.max.util.*; import com.sun.max.vm.*; import com.sun.max.vm.ext.jvmti.JJVMTI.*; import com.sun.max.vm.jdk.*; import com.sun.max.vm.jni.*; /** * Custom access to system properties for JVMTI. * * Much of this is similar code to that in {@link System#initProperties} but works in PRISTINE mode. * Really wish this wasn't necessary. It is needed to locate agent libraries specified without explicit paths, * i.e., {@code -agentlib}. */ public class JVMTISystem { private static final byte[] JAVA_HOME_BYTES = "JAVA_HOME".getBytes(); private static final byte[] DARWIN_JAVA_HOME_JDK6_DEFAULT_BYTES = JDK_java_lang_System.DARWIN_JAVA_HOME_JDK6_DEFAULT.getBytes(); private static final byte[] DARWIN_JAVA_HOME_JDK7_DEFAULT_BYTES = JDK_java_lang_System.DARWIN_JAVA_HOME_JDK7_DEFAULT.getBytes(); private static final byte[] LD_LIBRARY_PATH_BYTES = "LD_LIBRARY_PATH".getBytes(); private static final byte[] LIB_BYTES = "lib".getBytes(); private static final byte[] JNILIB_BYTES = "jnilib".getBytes(); private static final byte[] DYLIB_BYTES = "dylib".getBytes(); /** * Finds the Java home path, where the JDK libraries are found. * Code should be functionally equivalent to {@link JDK_java_lang_System#findJavaHome}. * * @return a C string representing the path of the Java home */ static Pointer findJavaHome() { switch (platform().os) { case MAXVE: case SOLARIS: case LINUX: { // TODO: Assume we are in the JRE and walk around from there. // For now, we just rely on the JAVA_HOME environment variable being set: // String javaHome = getenv("JAVA_HOME", false); Pointer javaHome = JVMTIEnvVar.getValue(JVMTIUtil.getByteArrayStart(JAVA_HOME_BYTES)); // FatalError.check(javaHome != null, "Environment variable JAVA_HOME not set"); if (javaHome.isZero()) { Log.println("Environment variable JAVA_HOME not set"); MaxineVM.native_exit(1); } // FatalError.check(javaHome != null, "Environment variable JAVA_HOME not set"); if (!CString.endsWith(javaHome, "/jre")) { // A lot of JDK code expects java.home to point to the "jre" subdirectory in a JDK. // This makes interpreting java.home easier if only a JRE is installed. // However, quite often JAVA_HOME points to the top level directory of a JDK // installation and so it needs to be adjusted to append the "jre" subdirectory. javaHome = CString.append(javaHome, "/jre"); } return javaHome; } case WINDOWS: { FatalError.check(false, "Initialization of java.home is unimplemented"); break; } case DARWIN: { // TODO: Assume we are in the JRE and walk around from there. // For now, we just rely on the JAVA_HOME environment variable being set: // String javaHome = getenv("JAVA_HOME", false); Pointer javaHome = JVMTIEnvVar.getValue(JVMTIUtil.getByteArrayStart(JAVA_HOME_BYTES)); if (javaHome.isZero()) { if (JDK.JDK_VERSION == JDK.JDK_6) { javaHome = JVMTIUtil.getByteArrayStart(DARWIN_JAVA_HOME_JDK6_DEFAULT_BYTES); } else if (JDK.JDK_VERSION == JDK.JDK_7) { javaHome = JVMTIUtil.getByteArrayStart(DARWIN_JAVA_HOME_JDK7_DEFAULT_BYTES); } else { FatalError.check(false, "Unsupported DARWIN JDK version"); } } if (JDK.JDK_VERSION == JDK.JDK_6) { if (!CString.endsWith(javaHome, "/Home")) { javaHome = CString.append(javaHome, "/Home"); } } else if (JDK.JDK_VERSION == JDK.JDK_7) { if (!CString.endsWith(javaHome, "/jre")) { javaHome = CString.append(javaHome, "/jre"); } } return javaHome; } } return Pointer.zero(); } /** * Retrieves the java library path from OS-specific environment variable(s). * * @return a string representing the java library path as determined from the OS environment */ private static Pointer getLdLibraryPath() { switch (platform().os) { case DARWIN: case LINUX: case SOLARIS: { return JVMTIEnvVar.getValue(JVMTIUtil.getByteArrayStart(LD_LIBRARY_PATH_BYTES)); } case WINDOWS: case MAXVE: default: { FatalError.check(false, "unimplemented"); return Pointer.zero(); } } } static Pointer getBootLibraryPath() { Pointer javaHome = findJavaHome(); switch (platform().os) { case DARWIN: { if (JDK.JDK_VERSION == JDK.JDK_7) { FatalError.check(CString.endsWith(javaHome, "/jre"), "The java.home system property should end with \"/jre\""); return CString.append(javaHome, "/lib"); } else if (JDK.JDK_VERSION == JDK.JDK_6) { FatalError.check(CString.endsWith(javaHome, "/Home"), "The java.home system property should end with \"/Home\""); final Pointer javaPath = CString.chopSuffix(javaHome, "/Home"); return CString.append(javaPath, "/Libraries"); } } case LINUX: case SOLARIS: { FatalError.check(CString.endsWith(javaHome, "jre"), "The java.home system property should end with \"/jre\""); final Pointer jreLibPath = CString.append(javaHome, "/lib"); return CString.append(jreLibPath, "/amd64"); } case WINDOWS: case MAXVE: default: { return Pointer.zero(); } } } static Pointer asPath(Pointer head, Pointer tail) { return CString.appendCString(CString.append(head, "/"), tail); } /** * Converts a simple library name to a platform-specific name with either or both * a prefix and a suffix. * * @param libraryName the name of the library * @return a string representing the traditional name of a native library on this platform */ static Pointer mapLibraryName(Pointer libraryName, int attempt) { Pointer libName = CString.appendCString(JVMTIUtil.getByteArrayStart(LIB_BYTES), libraryName); switch (platform().os) { case DARWIN: return CString.append(libName, attempt == 0 ? ".jnilib" : ".dylib"); case LINUX: case SOLARIS: return CString.append(libName, ".so"); case WINDOWS: case MAXVE: default: return Pointer.zero(); } } static Word load(Pointer name) { Word handle = Pointer.zero(); Pointer libPath = JVMTISystem.getBootLibraryPath(); int mapAttempt = 0; boolean triedBoth = false; while (true) { Pointer libName = JVMTISystem.mapLibraryName(name, mapAttempt); Pointer absPath = JVMTISystem.asPath(libPath, libName); handle = DynamicLinker.load(absPath); if (handle.isNotZero()) { break; } if (platform().os == OS.DARWIN && mapAttempt == 0) { mapAttempt++; } else { if (triedBoth) { break; } triedBoth = true; libPath = getLdLibraryPath(); if (libPath.isZero()) { break; } } } return handle; } static int setVerboseFlag(int flag, boolean value) { // PHASES: ANY switch (flag) { case JVMTI_VERBOSE_GC: VMOptions.verboseOption.verboseGC = value; break; case JVMTI_VERBOSE_CLASS: VMOptions.verboseOption.verboseClass = value; break; case JVMTI_VERBOSE_JNI: VMOptions.verboseOption.verboseJNI = value; break; case JVMTI_VERBOSE_OTHER: VMOptions.verboseOption.verboseCompilation = value; break; default: return JVMTI_ERROR_ILLEGAL_ARGUMENT; } return JVMTI_ERROR_NONE; } /* * Access to VM properties (misleadingly named "system" properties in the API). Simple enough, except for one issue: * * 1. The values are all supposed to be available in the OnLoad phase, which is a problem for Maxine as * many of them are not computable until the VM is initialized. TODO not implemented. * With a lot of grunt work the existing code that sets these properties could be made to work "C style", * but does anyone care? */ /** * Called during OnLoad to make sure the command line properties are processed here before they get processed * {@link VMOptions} which destroys them. */ static void initSystemProperties() { VMOptionsAccess.getCommandLineSystemProperties(); } static String getSystemProperty(String name) { // This is access to a VM property (including command line properties), not an arbitrary system property. int clCount = VMOptionsAccess.getCommandLineSystemProperties(); int vmpCount = VMProperty.VALUES.length; int count = clCount + vmpCount; for (int i = 0; i < count; i++) { if (i < vmpCount) { VMProperty vmProperty = VMProperty.VALUES[i]; if (name.equals(vmProperty.property)) { String value = vmProperty.value(); if (value != null) { return value; } else { throw new JJVMTIException(JVMTI_ERROR_NOT_AVAILABLE); } } } else { Pointer key = VMOptionsAccess.keysArray.getWord(i - vmpCount).asPointer(); if (CString.equals(key, name)) { Pointer propValPtr = VMOptionsAccess.valuesArray.getWord(i - vmpCount).asPointer(); try { return CString.utf8ToJava(propValPtr); } catch (Utf8Exception ex) { FatalError.check(false, "JVMTI: unexpected Utf8Exception"); } } } } throw new JJVMTIException(JVMTI_ERROR_NOT_AVAILABLE); } static String[] getSystemProperties() throws JJVMTIException { int clCount = VMOptionsAccess.getCommandLineSystemProperties(); int vmpCount = VMProperty.VALUES.length; int count = clCount + vmpCount; String[] result = new String[count]; for (int i = 0; i < count; i++) { if (i < vmpCount) { result[i] = VMProperty.VALUES[i].property; } else { try { result[i] = CString.utf8ToJava(VMOptionsAccess.keysArray.getWord(i - vmpCount).asPointer()); } catch (Utf8Exception ex) { FatalError.check(false, "JVMTI: unexpected Utf8Exception"); } } } return result; } static int getSystemProperty(Pointer env, Pointer property, Pointer valuePtr) { // This is access to a VM property (including command line properties), not an arbitrary system property. int clCount = VMOptionsAccess.getCommandLineSystemProperties(); int vmpCount = VMProperty.VALUES.length; int count = clCount + vmpCount; for (int i = 0; i < count; i++) { Pointer propValPtr = Pointer.zero(); int length = 0; if (i < vmpCount) { VMProperty vmProperty = VMProperty.VALUES[i]; if (CString.equals(property, vmProperty.property)) { if (vmProperty.valueAsCString().isNotZero()) { propValPtr = vmProperty.valueAsCString(); length = CString.length(propValPtr).toInt(); } else { return JVMTI_ERROR_NOT_AVAILABLE; } } } else { Pointer key = VMOptionsAccess.keysArray.getWord(i - vmpCount).asPointer(); if (CString.equals(property, key)) { propValPtr = VMOptionsAccess.valuesArray.getWord(i - vmpCount).asPointer(); length = CString.length(propValPtr).toInt(); } } if (propValPtr.isNotZero()) { Pointer propValCopyPtr = Memory.allocate(Size.fromInt(length + 1)); if (propValCopyPtr.isZero()) { return JVMTI_ERROR_OUT_OF_MEMORY; } Memory.copyBytes(propValPtr, propValCopyPtr, Size.fromInt(length)); propValCopyPtr.setByte(length, (byte) 0); valuePtr.setWord(propValCopyPtr); return JVMTI_ERROR_NONE; } } return JVMTI_ERROR_NOT_AVAILABLE; } static int getSystemProperties(Pointer env, Pointer countPtr, Pointer propertyArrayPtrPtr) { int clCount = VMOptionsAccess.getCommandLineSystemProperties(); int vmpCount = VMProperty.VALUES.length; int count = clCount + vmpCount; countPtr.setInt(count); Pointer propertyArrayPtr = Memory.allocate(Size.fromInt(count * Word.size())); if (propertyArrayPtr.isZero()) { return JVMTI_ERROR_OUT_OF_MEMORY; } propertyArrayPtrPtr.setWord(propertyArrayPtr); for (int i = 0; i < count; i++) { Pointer propertyCString; int length; if (i < vmpCount) { VMProperty vmProperty = VMProperty.VALUES[i]; String property = vmProperty.property; length = property.length(); propertyCString = vmProperty.propertyAsCString(); } else { propertyCString = VMOptionsAccess.keysArray.getWord(i - vmpCount).asPointer(); length = CString.length(propertyCString).toInt(); } Pointer propertyCopyPtr = Memory.allocate(Size.fromInt(length + 1)); if (propertyCopyPtr.isZero()) { return JVMTI_ERROR_OUT_OF_MEMORY; } Memory.copyBytes(propertyCString, propertyCopyPtr, Size.fromInt(length)); propertyCopyPtr.setByte(length, (byte) 0); propertyArrayPtr.setWord(i, propertyCopyPtr); } return JVMTI_ERROR_NONE; } static int setSystemProperty(Pointer env, Pointer property, Pointer valuePtr) { // we only process VM properties for (int i = 0; i < VMProperty.VALUES.length; i++) { VMProperty vmProperty = VMProperty.isVMProperty(property); if (vmProperty != null) { if (valuePtr.isZero()) { // writeable test return vmProperty.mutable ? JVMTI_ERROR_NONE : JVMTI_ERROR_NOT_AVAILABLE; } else { vmProperty.setValue(valuePtr); return JVMTI_ERROR_NONE; } } } return JVMTI_ERROR_NOT_AVAILABLE; } /** * Extracts any system properties from the raw command line and saves them as key/value pairs. */ private static class VMOptionsAccess extends VMOptions { private static int count; private static Pointer keysArray; private static Pointer valuesArray; private static boolean initialized; private static int getCommandLineSystemProperties() { if (initialized) { return count; } int argc = VMOptions.getArgumentCount(); for (int i = 0; i < argc; i++) { Pointer arg = VMOptions.getArgumentCString(i); if (arg.isNotZero() && CString.startsWith(arg, "-D")) { count++; } } keysArray = allocate(count * Word.size()); valuesArray = allocate(count * Word.size()); count = 0; for (int i = 0; i < argc; i++) { Pointer arg = VMOptions.getArgumentCString(i); if (arg.isNotZero() && CString.startsWith(arg, "-D")) { Pointer keyStart = arg.plus(2); Pointer p = keyStart; Pointer valueStart = Pointer.zero(); while (p.readByte(0) != (byte) 0) { if (p.readByte(0) == '=') { valueStart = p.plus(1); } p = p.plus(1); } int keyLength; int valueLength; if (valueStart.isZero()) { keyLength = p.minus(keyStart).toInt(); valueLength = 0; } else { keyLength = valueStart.minus(keyStart).toInt() - 1; valueLength = p.minus(valueStart).toInt(); } Pointer key = copyBytesAndNull(keyStart, keyLength); // Check if an existing VM property, in which case we ignore it. if (VMProperty.isVMProperty(key) != null) { continue; } keysArray.setWord(count, key); valuesArray.setWord(count, copyBytesAndNull(valueStart, valueLength)); count++; } } initialized = true; return count; } private static Pointer allocate(int length) { Pointer result = Memory.allocate(Size.fromInt(length)); FatalError.check(result.isNotZero(), "JVMTI system property initialization failure"); return result; } private static Pointer copyBytesAndNull(Pointer src, int length) { Pointer dst = allocate(length + 1); if (length > 0) { Memory.copyBytes(src, dst, Size.fromInt(length)); } dst.writeByte(length, (byte) 0); return dst; } } private static class FatalError { static void check(boolean value, String msg) { if (!value) { Log.println(msg); MaxineVM.native_exit(1); } } } }