/*
* Copyright (c) 2007, 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;
import static com.sun.max.lang.Classes.*;
import static com.sun.max.platform.Platform.*;
import static com.sun.max.vm.VMConfiguration.*;
import static com.sun.max.vm.VMOptions.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import com.sun.max.annotate.*;
import com.sun.max.config.*;
import com.sun.max.lang.*;
import com.sun.max.platform.*;
import com.sun.max.program.*;
import com.sun.max.unsafe.*;
import com.sun.max.util.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.classfile.*;
import com.sun.max.vm.compiler.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.hosted.*;
import com.sun.max.vm.jdk.*;
import com.sun.max.vm.jni.*;
import com.sun.max.vm.log.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.ti.*;
import com.sun.max.vm.type.*;
/**
* The global VM context. There is a {@linkplain #vm() single VM context} in existence at any time.
* The {@linkplain VMConfiguration configuration} for a VM context can be accessed via the
* {@link #config} field although the preferred mechanism for accessing the configuration for the
* global VM context is {@linkplain VMConfiguration#vmConfig()}.
* <p>
* Other functionality encapsulated in this class includes:
* <li>The {@link #isHosted()} method that can be used to guard blocks of code that will be omitted from a boot image.</li>
* <li>The current execution {@linkplain #phase phase} of the VM, denoting what language and VM capabilities
* have been initialized and are available.</li>
* <li>Some global native methods used at runtime that don't particularly
* belong to other classes (e.g. {@link #native_currentTimeMillis()}, {@link #native_exit(int)}, etc).</li>
* <li>Methods for {@linkplain #registerCriticalMethod(CriticalMethod) registering} methods to be
* loaded & compiled into the boot image.</li>
*/
public final class MaxineVM {
public static final String NAME = "Maxine Virtual Machine";
public static final int MAJOR_VERSION = 1;
public static final int MINOR_VERSION = 0;
public static final String VERSION_STRING = Integer.toString(MAJOR_VERSION) + "." + Integer.toString(MINOR_VERSION);
public static final String HOME_URL = "http://labs.oracle.com/projects/maxine/";
public static final int HARD_EXIT_CODE = -2;
/**
* The set of packages denoting classes for which {@link #isBootImageClass(String)} will return true.
*/
@HOSTED_ONLY
private static final Map<String, BootImagePackage> BOOT_IMAGE_CODE_BASE_PACKAGES = new ConcurrentHashMap<String, BootImagePackage>();
@HOSTED_ONLY
private static final Map<Class, Boolean> HOSTED_CLASSES = new ConcurrentHashMap<Class, Boolean>();
@HOSTED_ONLY
private static final Set<String> KEEP_CLINIT_CLASSES = new HashSet<String>();
@HOSTED_ONLY
private static final String[] TEST_PACKAGES = {"test.", "jtt."};
private static final VMOption HELP_OPTION = register(new VMOption("-help", "Prints this help message.") {
@Override
protected boolean haltsVM() {
return true;
}
@Override
public boolean parseValue(Pointer optionValue) {
VMOptions.printUsage(Category.STANDARD);
return true;
}
}, MaxineVM.Phase.PRISTINE);
/**
* The current VM context.
*/
@INSPECTED
@CONSTANT
private static MaxineVM vm;
/**
* The exit code returned by the VM process.
*/
private static int exitCode = 0;
private static long startupTime;
private static long startupTimeNano;
/**
* Allows the Inspector access to the thread locals block for the primordial thread.
*/
@INSPECTED
private static Address primordialTLBlock;
@INSPECTED
private static int primordialTLBlockSize;
public enum Phase {
/**
* Running on a host VM in order to construct a target VM or to run tests.
*/
BOOTSTRAPPING,
/**
* Starting to compile while in {@link HOSTED_ONLY} mode (e.g. creating the compiled boot image).
*/
HOSTED_COMPILING,
/**
* Starting the serialization of a graph of host VM objects into the boot image.
*/
SERIALIZING_IMAGE,
/**
* Starting writing the serialized graph (with all address set a relative offset) into byte streams.
*/
WRITING_IMAGE,
/**
* Executing target VM code, but many features do not work yet.
*/
PRIMORDIAL,
/**
* Java thread synchronization initialized and available (but may not do anything yet).
*/
PRISTINE,
/**
* All pure Java language features work; now building the application sandbox.
*/
STARTING,
/**
* Executing application code.
*/
RUNNING,
/**
* VM about to terminate, all non-daemon threads terminated, shutdown hooks run, but {@link VmOperation} thread still live.
* Last chance to interpose, but be careful what you do. In particular, thread creation is not permitted.
*/
TERMINATING
}
/**
* An enum for the properties whose values must be obtained from the native environment at runtime. The enum
* constants in this class are used to read values from the native_properties_t struct defined in
* Native/substrate/maxine.c returned by {@link MaxineVM#native_properties()}.
*
*/
public enum NativeProperty {
USER_NAME,
USER_HOME,
USER_DIR;
/**
* Gets the value of this property from a given C struct.
*
* @param cStruct the value returned by a call to {@link MaxineVM#native_properties()}
* @return the value of this property in {@code cStruct} converted to a {@link String} value (which may be {@code null})
*/
public String value(Pointer cStruct) {
final Pointer cString = cStruct.readWord(ordinal() * Word.size()).asPointer();
if (cString.isZero()) {
return null;
}
try {
return CString.utf8ToJava(cString);
} catch (Utf8Exception utf8Exception) {
throw FatalError.unexpected("Could not convert C string value of " + this + " to a Java string");
}
}
}
/**
* Registers the complete set of packages that (potentially) comprise the boot image being constructed.
*/
@HOSTED_ONLY
public static void registerBootImagePackages(List<BootImagePackage> packages) {
for (BootImagePackage pkg : packages) {
BOOT_IMAGE_CODE_BASE_PACKAGES.put(pkg.name(), pkg);
}
}
@HOSTED_ONLY
public static void registerKeepClassInit(String className) {
KEEP_CLINIT_CLASSES.add(className);
}
/**
* Global variable determining whether class initializers are to be discarded
* or preserved by the {@link ClassfileReader}.
*/
@HOSTED_ONLY
public static boolean preserveClinitMethods = System.getProperty("max.loader.preserveClinitMethods") != null;
@HOSTED_ONLY
public static boolean keepClassInit(TypeDescriptor classDescriptor) {
final String className = classDescriptor.toJavaString();
final boolean result = preserveClinitMethods || KEEP_CLINIT_CLASSES.contains(className);
return result;
}
public static String name() {
return NAME;
}
public static String description() {
return "The " + NAME + " Ver. " + VERSION_STRING + " <" + HOME_URL + ">";
}
/**
* Gets the current VM context.
*/
@INLINE
public static MaxineVM vm() {
return vm;
}
/**
* Initializes or changes the current VM context.
* This also {@linkplain Platform#set(Platform) sets} the current platform context
* to {@code vm.configuration.platform}. That is,
* changing the VM context also changes the platform context.
*
* @param vm the new VM context (must not be {@code null})
* @return the previous VM context
*/
@HOSTED_ONLY
public static MaxineVM set(MaxineVM vm) {
MaxineVM old = MaxineVM.vm;
Platform.set(platform());
MaxineVM.vm = vm;
return old;
}
/**
* Determines if the current execution environment is hosted on another JVM.
*
* @return {@code true} if being executed on another JVM, {@code false} if executing the bootstrapped/target VM.
*/
public static boolean isHosted() {
return true;
}
@LOCAL_SUBSTITUTION
@FOLD
public static boolean isHosted_() {
return false;
}
/**
* Determines if this is a {@link BuildLevel#DEBUG debug} build of the VM.
* @return {@code true} if this is a debug build
*/
@FOLD
public static boolean isDebug() {
return vm().config.debugging();
}
/**
* Determines if a given constructor, field or method exists only for hosted execution.
*
* @param member the member to check
* @return {@code true} if the member is only valid while executing hosted
*/
@HOSTED_ONLY
public static boolean isHostedOnly(AccessibleObject member) {
return member.getAnnotation(HOSTED_ONLY.class) != null ||
!Platform.platform().isAcceptedBy(member.getAnnotation(PLATFORM.class)) ||
!JDK.thisVersionOrNewer(member.getAnnotation(JDK_VERSION.class)) ||
isHostedOnly(Classes.getDeclaringClass(member));
}
/**
* Determines if a given class exists only for hosted execution and should not be part of a generated target image.
* <p>
* A <i>direct hosted-only class</i> is defined as a class that is:
* <ul>
* <li>or annotated with {@link HOSTED_ONLY}
* <li>or in a package that ends with {@code ".hosted"}
* <li>or annotated with {@link PLATFORM} that does not match the target platform
* <li>or annotated with {@link JDK_VERSION} that is not at least the JDK version used for building the boot image
* <li>
* </ul>
* <p>
* A <i>hosted-only</i> class is defined as:
* <ul>
* <li>a direct hosted-only class
* <li>or a subclass of a hosted-only class
* <li>or a nested class in a hosted-only class
* <li>or an array of {@code T} where {@code T} is a hosted-only class
* </ul>
*
* @param javaClass the class to check
* @return {@code true} if the class is hosted-only
*/
@HOSTED_ONLY
public static boolean isHostedOnly(Class< ? > javaClass) {
// We keep a cache of what we know
final Boolean value = HOSTED_CLASSES.get(javaClass);
if (value != null) {
return value;
}
boolean result = false; // default assumption
final String pkgName = getPackageName(javaClass);
// Direct part of definition
if (javaClass.getAnnotation(HOSTED_ONLY.class) != null) {
result = true;
} else if (pkgName.endsWith(".hosted")) {
// May want to replace this 'magic' interpretation of ".hosted"
// with a sentinel class (e.g. HOSTED_ONLY_PACKAGE).
result = true;
} else if (!Platform.platform().isAcceptedBy(javaClass.getAnnotation(PLATFORM.class))) {
result = true;
} else if (!JDK.thisVersionOrNewer(javaClass.getAnnotation(JDK_VERSION.class))) {
result = true;
} else {
// Indirect part of definition, cover all the possible cases
if (javaClass.isArray()) {
final Class< ? > componentClass = javaClass.getComponentType();
result = isHostedOnly(componentClass);
} else {
final Class superClass = javaClass.getSuperclass();
if (superClass != null && isHostedOnly(superClass)) {
result = true;
} else {
final Class< ? > enclosingClass = getEnclosingClass(javaClass);
if (enclosingClass != null && isHostedOnly(enclosingClass)) {
result = true;
}
}
}
}
HOSTED_CLASSES.put(javaClass, result);
//Trace.line(1, "setHostedOnly: " + javaClass.getName() + " " + result);
return result;
}
private static Class<?> getEnclosingClass(Class<?> javaClass) {
try {
final Class< ? > enclosingClass = javaClass.getEnclosingClass();
return enclosingClass;
} catch (LinkageError linkageError) {
ProgramWarning.message("Error trying to get the enclosing class for " + javaClass + ": " + linkageError);
}
return null;
}
public static boolean isPrimordial() {
return vm().phase == Phase.PRIMORDIAL;
}
public static boolean isPristine() {
return vm().phase == Phase.PRISTINE;
}
public static boolean isStarting() {
return vm().phase == Phase.STARTING;
}
public static boolean isPrimordialOrPristine() {
final Phase phase = vm().phase;
return phase == Phase.PRIMORDIAL || phase == Phase.PRISTINE;
}
public static boolean isRunning() {
return vm().phase == Phase.RUNNING;
}
public static long getStartupTime() {
return startupTime;
}
public static long getStartupTimeNano() {
return startupTimeNano;
}
/**
* Determines if a given class name denotes a class that is specified as part of the boot image.
* This cannot be based solely on the package name as the package may enumerate
* a specific set of classes.
*/
public static boolean isBootImageClass(String className) {
BootImagePackage pkg = BOOT_IMAGE_CODE_BASE_PACKAGES.get(getPackageName(className));
if (pkg != null) {
// check for explicit class
return pkg.isBootImageClass(className);
}
return false;
}
public static void setExitCode(int code) {
exitCode = code;
}
/**
* VM initialization point called by the substrate.
*
* ATTENTION: this signature must match 'VMRunMethod' in "com.oracle.max.vm.native/substrate/maxine.c"
*
* VM startup, initialization and exit code reporting routine running in the VM startup native thread.
*
* This must work without having established a valid Java 'Thread' or 'VmThread'. Hence, no JNI callbacks are
* supported in this routine.
*
* Also, there is no heap at first. In this early phase, we cannot allocate any objects.
*
* @return 0 indicating initialization succeeded, non-0 if not
*/
@VM_ENTRY_POINT
public static int run(Pointer tlBlock, int tlBlockSize, Pointer bootHeapRegionStart, Word dlopen, Word dlsym, Word dlerror, Pointer vmInterface, Pointer jniEnv, Pointer jmmInterface, Pointer jvmtiInterface, int argc, Pointer argv) {
primordialTLBlock = tlBlock;
primordialTLBlockSize = tlBlockSize;
Pointer etla = tlBlock.plus(platform().pageSize - Address.size() + VmThreadLocal.tlaSize().toInt());
SafepointPoll.setLatchRegister(etla);
// This one field was not marked by the data prototype for relocation
// to avoid confusion between "offset zero" and "null".
// Fix it manually:
Heap.bootHeapRegion.setStart(bootHeapRegionStart);
VMLog.vmLog().initialize(MaxineVM.Phase.PRIMORDIAL);
// The dynamic linker must be initialized before linking critical native methods
DynamicLinker.initialize(dlopen, dlsym, dlerror);
// Link the critical native methods:
CriticalNativeMethod.linkAll();
DynamicLinker.markCriticalLinked();
// Initialize the trap system:
Trap.initialize();
ImmortalHeap.initialize();
NativeInterfaces.initialize(vmInterface, jniEnv, jmmInterface);
// Perhaps this should be later, after VM has initialized
startupTime = System.currentTimeMillis();
startupTimeNano = System.nanoTime();
MaxineVM vm = vm();
vmConfig().initializeSchemes(MaxineVM.Phase.PRIMORDIAL);
vm().stubs.intialize();
vm.phase = Phase.PRISTINE;
VMOptions.parsePristine(argc, argv);
return exitCode;
}
public static String getExecutablePath() {
try {
return CString.utf8ToJava(native_executablePath());
} catch (Utf8Exception e) {
throw FatalError.unexpected("Could not convert C string value of executable path to a Java string");
}
}
/**
* Request the given method to be statically compiled in the boot image.
*/
@HOSTED_ONLY
public static ClassMethodActor registerImageMethod(ClassMethodActor method) {
CompiledPrototype.registerVMEntryPoint(method);
return method;
}
/**
* Request the given method to have its invocation stub be compiled in the boot image.
*/
@HOSTED_ONLY
public static MethodActor registerImageInvocationStub(MethodActor method) {
CompiledPrototype.registerImageInvocationStub(method);
return method;
}
@HOSTED_ONLY
public static void registerCriticalMethod(CriticalMethod criticalEntryPoint) {
registerImageMethod(criticalEntryPoint.classMethodActor);
}
/*
* Global native functions: these functions implement a thin layer over basic native
* services that are needed to implement higher-level Java VM services. Note that
* these native functions *ONLY* work on the target VM, not in bootstrapping or
* inspecting modes.
*
* These service methods cannot block, and cannot use object references.
*/
@C_FUNCTION
public static native long native_nanoTime();
@C_FUNCTION
public static native long native_currentTimeMillis();
@C_FUNCTION
public static native Pointer native_executablePath();
@C_FUNCTION
public static native Pointer native_environment();
/**
* Gets a pointer to a C struct whose fields are NULL terminated C char arrays. The fields of this struct are read
* and converted to {@link String} values by {@link NativeProperty#value(Pointer)}. The {@code native_properties_t}
* struct declaration is in Native/substrate/maxine.c.
*/
@C_FUNCTION
public static native Pointer native_properties();
@C_FUNCTION
public static native float native_parseFloat(Pointer pointer, float nan);
@C_FUNCTION
public static native void native_exit(int code);
@C_FUNCTION
public static native void native_trap_exit(int code, Address address);
/**
* Generate a core file of the vm process.
* Note that this doesn't exit the vm which can progress normally after the core generation.
*/
@C_FUNCTION
public static native void core_dump();
@INSPECTED
public final VMConfiguration config;
public Phase phase = Phase.BOOTSTRAPPING;
public final RegisterConfigs registerConfigs;
public final Stubs stubs;
public final CompilationBroker compilationBroker;
public final SafepointPoll safepointPoll;
public final TrapFrameAccess trapFrameAccess;
public MaxineVM(VMConfiguration configuration) {
this.config = configuration;
this.registerConfigs = RegisterConfigs.create();
this.stubs = new Stubs(registerConfigs);
this.safepointPoll = SafepointPoll.create();
this.trapFrameAccess = TrapFrameAccess.create();
this.compilationBroker = CompilationBroker.create();
}
public static void reportPristineMemoryFailure(String memoryAreaName, String operation, Size numberOfBytes) {
Log.println("Error occurred during initialization of VM");
Log.print("Failed to ");
Log.print(operation);
Log.print(' ');
Log.print(numberOfBytes.toLong());
Log.print(" bytes (");
Log.printlnToPowerOfTwoUnits(numberOfBytes);
Log.print(") of memory for ");
Log.println(memoryAreaName);
MaxineVM.native_exit(1);
}
/**
* Low level VM exit. This method does not run any shutdown hooks or finalizers.
* This is where {@link Runtime#exit(int)} and {@link Runtime#halt(int)} bottom out.
*
* @param code exit code for the VM process
*/
public static void exit(int code) {
VMOptions.beforeExit();
// This prevents further thread creation
VmThreadMap.ACTIVE.setVMTerminating();
SignalDispatcher.terminate();
try {
VMTI.handler().vmDeath();
} catch (Throwable throwable) {
System.err.println("Exception thrown by VMTI handler: " + throwable);
}
// TODO: need to revisit this. Likely, we would want to bring all
// threads to a safepoint before running the terminating phase.
VMLog.vmLog().initialize(MaxineVM.Phase.TERMINATING);
vmConfig().initializeSchemes(MaxineVM.Phase.TERMINATING);
VmOperationThread.terminate();
// Drop back to PRIMORDIAL
MaxineVM vm = vm();
vm.phase = MaxineVM.Phase.PRIMORDIAL;
native_exit(code);
}
}