From 2e1a7179f0691b17529b3b41c37f266c8e0210c1 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Sat, 11 May 2013 11:11:14 +0300 Subject: [PATCH] Allow instrumentation in preloader --- .../jet/preloading/ClassPreloadingUtils.java | 11 +- .../jetbrains/jet/preloading/Preloader.java | 105 +++++++++++++----- .../instrumentation/Instrumenter.java | 37 ++++++ 3 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 compiler/preloader/src/org/jetbrains/jet/preloading/instrumentation/Instrumenter.java diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java index 340bc1bcded..97adf0965b5 100644 --- a/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java @@ -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 { diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java b/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java index 5f5155fc6c4..640e9a41374 100644 --- a/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java @@ -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 files = new ArrayList(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 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 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 parseClassPath(String classpath) { + String[] paths = classpath.split("\\" + File.pathSeparator); + List files = new ArrayList(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 loader = ServiceLoader.load(Instrumenter.class, withInstrumenter); + Iterator 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
"); + System.out.println("Usage: Preloader
> "); System.exit(1); } } diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/instrumentation/Instrumenter.java b/compiler/preloader/src/org/jetbrains/jet/preloading/instrumentation/Instrumenter.java new file mode 100644 index 00000000000..a23cf2b5e3e --- /dev/null +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/instrumentation/Instrumenter.java @@ -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); +}