Skip to content
Snippets Groups Projects
java.cc 5.23 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright (C) 2013 Cloudius Systems, Ltd.
     *
     * This work is open source software, licensed under the terms of the
     * BSD license as described in the LICENSE file in the top-level directory.
     */
    
    
    #include <osv/elf.hh>
    
    #include <jni.h>
    #include <string.h>
    
    #include <osv/debug.hh>
    
    Glauber Costa's avatar
    Glauber Costa committed
    #include "jvm_balloon.hh"
    
    #include <osv/mempool.hh>
    
    // java.so is similar to the standard "java" command line launcher in Linux.
    //
    // This program does very little - basically it starts the JVM and asks it
    
    // to run a fixed class, RunJava, which parses the command line parameters,
    // sets up the class path, and runs the jar or class specified in these
    // parameters.
    
    #define JVM_PATH        "/usr/lib/jvm/jre/lib/amd64/server/libjvm.so"
    
    #define RUNJAVA_PATH    "/java/runjava.jar"
    #define RUNJAVA         "io/osv/RunJava"    // separated by slashes, not dots
    
        JavaVMOption opt = {};
    
    JavaVMOption mkoption(std::string str)
    {
        return mkoption(str.c_str());
    }
    
    template <typename... args>
    JavaVMOption mkoption(const char *fmt, args... as)
    {
        return mkoption(osv::sprintf(fmt, as...));
    }
    
    
    inline bool starts_with(const char *s, const char *prefix)
    {
        return !strncmp(s, prefix, strlen(prefix));
    }
    
    
    static bool is_jvm_option(const char *arg) {
        return starts_with(arg, "-verbose") ||
               starts_with(arg, "-D") ||
               starts_with(arg, "-X") ||
               starts_with(arg, "-javaagent") ||
               starts_with(arg, "-agentlib");
    }
    
    
    static void mark_heap_option(char **arg, int index, int &has_xms, int &has_xmx)
    {
        if (starts_with(arg[index], "-Xms") && !has_xms) {
            has_xms = index;
        }
    
        if (starts_with(arg[index], "-Xmx") && !has_xmx) {
            has_xmx = index;
        }
    
        if (starts_with(arg[index], "-mx")) {
            has_xmx = index;
        }
    
        if (starts_with(arg[index], "-ms")) {
            has_xms = index;
        }
    }
    
    
    extern "C"
    int main(int argc, char **argv)
    
    Nadav Har'El's avatar
    Nadav Har'El committed
        auto prog = elf::get_program();
        // The JVM library remains loaded as long as jvm_so is in scope.
        auto jvm_so = prog->get_library(JVM_PATH);
    
    
        auto JNI_GetDefaultJavaVMInitArgs
            = prog->lookup_function<void (void*)>("JNI_GetDefaultJavaVMInitArgs");
        JavaVMInitArgs vm_args = {};
        vm_args.version = JNI_VERSION_1_6;
        JNI_GetDefaultJavaVMInitArgs(&vm_args);
    
        options.push_back(mkoption("-Djava.class.path=" RUNJAVA_PATH));
    
        options.push_back(mkoption("-Djava.system.class.loader=io.osv.OsvSystemClassLoader"));
    
    
        int orig_argc = argc;
    
        int has_xms = 0, has_xmx = 0;
    
    
        for (int i = 1; i < orig_argc; i++) {
            // We are not supposed to look for verbose options after -jar
            // or class name. From that point on, they are user provided
    
            if (!strcmp(argv[i], "-jar") || !starts_with(argv[i], "-"))
    
            mark_heap_option(argv, i, has_xms, has_xmx);
    
    
            // Pass some options directly to the JVM
    
            if (is_jvm_option(argv[i])) {
    
                options.push_back(mkoption(argv[i]));
                argv[i] = NULL; // so we don't pass it to RunJava
                argc--;
            }
        }
    
    Pekka Enberg's avatar
    Pekka Enberg committed
    #if 0
    
        if (!has_xmx) {
            // FIXME: We should estimate how much memory the JVM itself is going to use
            auto heap_size = memory::stats::free() >> 20;
            options.push_back(mkoption("-Xmx%dM", heap_size));
            if (!has_xms) {
                options.push_back(mkoption("-Xms%dM", heap_size));
            }
    
            debugf("Autotuning JVM heap size to %d MB\n", heap_size);
    
    Pekka Enberg's avatar
    Pekka Enberg committed
    #endif
    
        vm_args.nOptions = options.size();
        vm_args.options = options.data();
    
    
        auto JNI_CreateJavaVM
            = prog->lookup_function<jint (JavaVM**, JNIEnv**, void*)>("JNI_CreateJavaVM");
    
        if (!JNI_CreateJavaVM) {
            debug("java.so: failed looking up JNI_CreateJavaVM()\n");
    
        JavaVM* jvm = nullptr;
        JNIEnv *env;
    
        if (JNI_CreateJavaVM(&jvm, &env, &vm_args) != 0) {
            debug("java.so: Can't create VM.\n");
    
        }
        auto mainclass = env->FindClass(RUNJAVA);
    
            debug("java.so: Can't find class %s in %s.\n", RUNJAVA, RUNJAVA_PATH);
    
        auto mainmethod = env->GetStaticMethodID(mainclass, "main", "([Ljava/lang/String;)V");
    
        if (!mainmethod) {
            debug("java.so: Can't find main() in class %s.\n", RUNJAVA);
    
        auto stringclass = env->FindClass("java/lang/String");
    
        auto args = env->NewObjectArray(argc-1, stringclass, nullptr);
    
    
        int index = 0;
        for (int i = 1; i < orig_argc; i++) {
            if (!argv[i])
                continue;
            env->SetObjectArrayElement(args, index++, env->NewStringUTF(argv[i]));
    
    Pekka Enberg's avatar
    Pekka Enberg committed
    #if 0
    
        // Manually setting the heap size is viewed as a declaration of intent. In
        // that case, we'll leave the user alone. This may be revisited in the
        // future, but it is certainly the safest option.
        std::unique_ptr<jvm_balloon_shrinker>
            balloon(has_xmx ? nullptr : new jvm_balloon_shrinker(jvm));
    
    Pekka Enberg's avatar
    Pekka Enberg committed
    #endif
    
        env->CallStaticVoidMethod(mainclass, mainmethod, args);
    
    
        // DestroyJavaVM() waits for all all non-daemon threads to end, and
        // only then destroys the JVM.
        jvm->DetachCurrentThread();
        jvm->DestroyJavaVM();