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