Allow instrumentation in preloader

This commit is contained in:
Andrey Breslav
2013-05-11 11:11:14 +03:00
parent df557faa09
commit 2e1a7179f0
3 changed files with 126 additions and 27 deletions
@@ -30,6 +30,10 @@ import java.util.zip.ZipInputStream;
public class ClassPreloadingUtils {
public static abstract class ClassHandler {
public byte[] instrument(String resourceName, byte[] data) {
return data;
}
public void beforeDefineClass(String name, int sizeInBytes) {}
public void afterDefineClass(String name) {}
@@ -152,7 +156,12 @@ public class ClassPreloadingUtils {
bytes.write(buffer, 0, count);
}
resources.put(name, new ResourceData(jarFile, name, bytes.toByteArray()));
byte[] data = bytes.toByteArray();
if (handler != null) {
data = handler.instrument(name, data);
}
resources.put(name, new ResourceData(jarFile, name, data));
}
}
finally {
@@ -1,31 +1,25 @@
package org.jetbrains.jet.preloading;
import org.jetbrains.jet.preloading.instrumentation.Instrumenter;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
public class Preloader {
public static final int PRELOADER_ARG_COUNT = 4;
private static final String PROFILE = "profile=";
public static void main(String[] args) throws Exception {
if (args.length < PRELOADER_ARG_COUNT) {
printUsageAndExit();
}
String classpath = args[0];
String[] paths = classpath.split("\\" + File.pathSeparator);
List<File> files = new ArrayList<File>(paths.length);
for (String path : paths) {
File file = new File(path);
if (!file.exists()) {
System.out.println("File does not exist: " + file);
printUsageAndExit();
}
files.add(file);
}
List<File> files = parseClassPath(args[0]);
String mainClassCanonicalName = args[1];
@@ -39,14 +33,18 @@ public class Preloader {
return;
}
boolean printTime = parseMeasureTime(args[3]);
String profilingModeStr = args[3];
ProfilingMode profilingMode = parseMeasureTime(profilingModeStr);
URL[] instrumentersClasspath = parseInstrumentersClasspath(profilingMode, profilingModeStr);
long startTime = System.nanoTime();
ClassLoader parent = Preloader.class.getClassLoader();
ClassPreloadingUtils.ClassHandler handler = getHandler(printTime);
ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(files, classNumber, parent, handler);
handler.done();
ClassLoader withInstrumenter = instrumentersClasspath.length > 0 ? new URLClassLoader(instrumentersClasspath, parent) : parent;
ClassPreloadingUtils.ClassHandler handler = getHandler(profilingMode, withInstrumenter);
ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(files, classNumber, withInstrumenter, handler);
Class<?> mainClass = preloaded.loadClass(mainClassCanonicalName);
Method mainMethod = mainClass.getMethod("main", String[].class);
@@ -55,15 +53,55 @@ public class Preloader {
mainMethod.invoke(0, new Object[] {Arrays.copyOfRange(args, PRELOADER_ARG_COUNT, args.length)});
}
finally {
if (printTime) {
if (profilingMode != ProfilingMode.NO_TIME) {
long dt = System.nanoTime() - startTime;
System.out.format("Total time: %.3fs\n", dt / 1e9);
}
handler.done();
}
}
private static ClassPreloadingUtils.ClassHandler getHandler(boolean printTime) {
if (printTime) return new ClassPreloadingUtils.ClassHandler() {};
private static URL[] parseInstrumentersClasspath(ProfilingMode profilingMode, String profilingModeStr)
throws MalformedURLException {
URL[] instrumentersClasspath;
if (profilingMode == ProfilingMode.PROFILE) {
List<File> instrumentersClassPathFiles = parseClassPath(getClassPath(profilingModeStr));
instrumentersClasspath = new URL[instrumentersClassPathFiles.size()];
for (int i = 0; i < instrumentersClassPathFiles.size(); i++) {
File file = instrumentersClassPathFiles.get(i);
instrumentersClasspath[i] = file.toURI().toURL();
}
}
else {
instrumentersClasspath = new URL[0];
}
return instrumentersClasspath;
}
private static String getClassPath(String profilingModeStr) {
return profilingModeStr.substring(PROFILE.length());
}
private static List<File> parseClassPath(String classpath) {
String[] paths = classpath.split("\\" + File.pathSeparator);
List<File> files = new ArrayList<File>(paths.length);
for (String path : paths) {
File file = new File(path);
if (!file.exists()) {
System.out.println("File does not exist: " + file);
printUsageAndExit();
}
files.add(file);
}
return files;
}
private static ClassPreloadingUtils.ClassHandler getHandler(ProfilingMode profilingMode, ClassLoader withInstrumenter) {
if (profilingMode == ProfilingMode.NO_TIME) return new ClassPreloadingUtils.ClassHandler() {};
ServiceLoader<Instrumenter> loader = ServiceLoader.load(Instrumenter.class, withInstrumenter);
Iterator<Instrumenter> instrumenters = loader.iterator();
final Instrumenter instrumenter = instrumenters.hasNext() ? instrumenters.next() : Instrumenter.DO_NOTHING;
final int[] counter = new int[1];
final int[] size = new int[1];
@@ -78,20 +116,35 @@ public class Preloader {
public void done() {
System.out.println("Loaded classes: " + counter[0]);
System.out.println("Loaded classes size: " + size[0]);
instrumenter.dump(System.out);
}
@Override
public byte[] instrument(String resourceName, byte[] data) {
return instrumenter.instrument(resourceName, data);
}
};
}
private static boolean parseMeasureTime(String arg) {
if ("time".equals(arg)) return true;
if ("notime".equals(arg)) return false;
private enum ProfilingMode {
NO_TIME,
TIME,
PROFILE
}
private static ProfilingMode parseMeasureTime(String arg) {
if ("time".equals(arg)) return ProfilingMode.TIME;
if ("notime".equals(arg)) return ProfilingMode.NO_TIME;
if (arg.startsWith(PROFILE)) return ProfilingMode.PROFILE;
System.out.println("Unrecognized argument: " + arg);
printUsageAndExit();
return false;
return ProfilingMode.NO_TIME;
}
private static void printUsageAndExit() {
System.out.println("Usage: Preloader <paths to jars> <main class> <class number estimate> <time|notime> <parameters to pass to the main class>");
System.out.println("Usage: Preloader <paths to jars> <main class> <class number estimate> <time|notime|profile=<profiling class path>> <parameters to pass to the main class>");
System.exit(1);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.preloading.instrumentation;
import java.io.PrintStream;
public interface Instrumenter {
Instrumenter DO_NOTHING = new Instrumenter() {
@Override
public byte[] instrument(String resourceName, byte[] data) {
return data;
}
@Override
public void dump(PrintStream out) {
// Do nothing
}
};
byte[] instrument(String resourceName, byte[] data);
void dump(PrintStream out);
}