001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.scheduler; 014 015 import java.lang.instrument.Instrumentation; 016 import java.lang.reflect.InvocationTargetException; 017 import java.lang.reflect.Method; 018 import java.util.jar.JarFile; 019 import java.util.jar.Manifest; 020 021 import org.jikesrvm.Callbacks; 022 import org.jikesrvm.CommandLineArgs; 023 import org.jikesrvm.VM; 024 import org.jikesrvm.classloader.Atom; 025 import org.jikesrvm.classloader.RVMClass; 026 import org.jikesrvm.classloader.RVMClassLoader; 027 import org.jikesrvm.classloader.RVMMethod; 028 import org.jikesrvm.classloader.TypeReference; 029 import org.jikesrvm.runtime.Reflection; 030 import org.vmmagic.pragma.Entrypoint; 031 032 /** 033 * Thread in which user's "main" program runs. 034 */ 035 public final class MainThread extends Thread { 036 private final String[] args; 037 private final String[] agents; 038 private RVMMethod mainMethod; 039 protected boolean launched = false; 040 041 private static final boolean dbg = false; 042 043 /** 044 * Create "main" thread. 045 * @param args {@code args[0]}: name of class containing "main" method; 046 * {@code args[1..N]}: parameters to pass to "main" method 047 */ 048 public MainThread(String[] args) { 049 super("MainThread"); 050 setDaemon(false); // NB otherwise we inherit the boot threads daemon status 051 this.agents = CommandLineArgs.getJavaAgentArgs(); 052 this.args = args; 053 if (dbg) { 054 VM.sysWriteln("MainThread(args.length == ", args.length, "): constructor done"); 055 } 056 } 057 058 private void runAgents(ClassLoader cl) { 059 if (agents.length > 0) { 060 Instrumentation instrumenter = null; 061 if (VM.BuildForGnuClasspath) { 062 try { 063 instrumenter = (Instrumentation)Class.forName("gnu.java.lang.JikesRVMSupport") 064 .getMethod("createInstrumentation").invoke(null); 065 java.lang.JikesRVMSupport.initializeInstrumentation(instrumenter); 066 } catch (Exception _) { 067 } 068 } 069 for (String agent : agents) { 070 /* 071 * Parse agent string according to the form 072 * given in the java.lang.instrumentation package 073 * documentation: 074 * jarpath[=options] 075 * 076 * (The -javaagent: part of the agent options has 077 * already been stripped) 078 */ 079 int equalsIndex = agent.indexOf('='); 080 String agentJar; 081 String agentOptions; 082 if (equalsIndex != -1) { 083 agentJar = agent.substring(0, equalsIndex); 084 agentOptions = agent.substring(equalsIndex + 1); 085 } else { 086 agentJar = agent; 087 agentOptions = ""; 088 } 089 runAgent(instrumenter, cl, agentJar, agentOptions); 090 } 091 } 092 } 093 094 private static void runAgent(Instrumentation instrumenter, ClassLoader cl, String agentJar, String agentOptions) { 095 Manifest mf = null; 096 try { 097 JarFile jf = new JarFile(agentJar); 098 mf = jf.getManifest(); 099 } catch (Exception e) { 100 VM.sysWriteln("vm: IO Exception opening JAR file ", agentJar, ": ", e.getMessage()); 101 VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG); 102 } 103 if (mf == null) { 104 VM.sysWriteln("The jar file is missing the manifest: ", agentJar); 105 VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG); 106 } 107 String agentClassName = mf.getMainAttributes().getValue("Premain-Class"); 108 if (agentClassName == null) { 109 VM.sysWriteln("The jar file is missing the Premain-Class manifest entry for the agent class: ", agentJar); 110 VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG); 111 } 112 //TODO: By this stage all agent jars and classes they reference via their manifest 113 try { 114 Class<?> agentClass = cl.loadClass(agentClassName); 115 Method agentPremainMethod = agentClass.getMethod("premain", new Class<?>[]{String.class, Instrumentation.class}); 116 agentPremainMethod.invoke(null, new Object[]{agentOptions, instrumenter}); 117 } catch (InvocationTargetException e) { 118 // According to the spec, exceptions from premain() can be ignored 119 } catch (Throwable e) { 120 VM.sysWriteln("Failed to run the agent's premain: " + e.getMessage()); 121 e.printStackTrace(); 122 System.exit(0); 123 } 124 } 125 126 RVMMethod getMainMethod() { 127 return mainMethod; 128 } 129 130 /** 131 * Run "main" thread. 132 * <p> 133 * This code could be made a little shorter by relying on Reflection 134 * to do the classloading and compilation. We intentionally do it here 135 * to give us a chance to provide error messages that are specific to 136 * not being able to find the main class the user wants to run. 137 * This may be a little silly, since it results in code duplication 138 * just to provide debug messages in a place where very little is actually 139 * likely to go wrong, but there you have it.... 140 */ 141 @Override 142 @Entrypoint 143 public void run() { 144 launched = true; 145 146 if (dbg) VM.sysWriteln("MainThread.run() starting "); 147 148 // Set up application class loader 149 ClassLoader cl = RVMClassLoader.getApplicationClassLoader(); 150 setContextClassLoader(cl); 151 152 runAgents(cl); 153 154 if (dbg) VM.sysWrite("[MainThread.run() loading class to run... "); 155 // find method to run 156 // load class specified by args[0] 157 RVMClass cls = null; 158 try { 159 Atom mainAtom = Atom.findOrCreateUnicodeAtom(args[0].replace('.', '/')); 160 TypeReference mainClass = TypeReference.findOrCreate(cl, mainAtom.descriptorFromClassName()); 161 cls = mainClass.resolve().asClass(); 162 cls.resolve(); 163 cls.instantiate(); 164 cls.initialize(); 165 } catch (NoClassDefFoundError e) { 166 if (dbg) VM.sysWrite("failed.]"); 167 // no such class 168 VM.sysWrite(e + "\n"); 169 return; 170 } 171 if (dbg) VM.sysWriteln("loaded.]"); 172 173 // find "main" method 174 // 175 mainMethod = cls.findMainMethod(); 176 if (mainMethod == null) { 177 // no such method 178 VM.sysWrite(cls + " doesn't have a \"public static void main(String[])\" method to execute\n"); 179 return; 180 } 181 182 if (dbg) VM.sysWrite("[MainThread.run() making arg list... "); 183 // create "main" argument list 184 // 185 String[] mainArgs = new String[args.length - 1]; 186 for (int i = 0, n = mainArgs.length; i < n; ++i) { 187 mainArgs[i] = args[i + 1]; 188 } 189 if (dbg) VM.sysWriteln("made.]"); 190 191 if (dbg) VM.sysWrite("[MainThread.run() compiling main(String[])... "); 192 mainMethod.compile(); 193 if (dbg) VM.sysWriteln("compiled.]"); 194 195 // Notify other clients that the startup is complete. 196 // 197 Callbacks.notifyStartup(); 198 199 if (dbg) VM.sysWriteln("[MainThread.run() invoking \"main\" method... "); 200 // invoke "main" method with argument list 201 Reflection.invoke(mainMethod, null, null, new Object[]{mainArgs}, true); 202 if (dbg) VM.sysWriteln(" MainThread.run(): \"main\" method completed.]"); 203 } 204 }