From 2ed217bb4ae3795f3156386066aaaf28c46a9d5c Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Mon, 13 May 2013 17:07:46 +0400 Subject: [PATCH] Some docs and example --- compiler/preloader/instrumentation/ReadMe.md | 52 ++++++++++ ...et.preloading.instrumentation.Instrumenter | 1 + .../ProfilingInstrumenterExample.java | 96 +++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 compiler/preloader/instrumentation/ReadMe.md create mode 100644 compiler/preloader/instrumentation/src/META-INF/services/org.jetbrains.jet.preloading.instrumentation.Instrumenter create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/ProfilingInstrumenterExample.java diff --git a/compiler/preloader/instrumentation/ReadMe.md b/compiler/preloader/instrumentation/ReadMe.md new file mode 100644 index 00000000000..632eb225b77 --- /dev/null +++ b/compiler/preloader/instrumentation/ReadMe.md @@ -0,0 +1,52 @@ +# Instrumentation in Kotlin Preloader + +The main purpose of Preloader is to speed up class loading at compiler's startup. +But as a side effect, we got a chance to support instrumenting the compiler's code, which is mainly useful for profiling. + +## How to quickly set up instrumentation + +To run Preloader with instrumentation, pass ```instrument=...``` on the command line: + +``` +org.jetbrains.jet.preloading.Preloader \ + dist/kotlinc/lib/kotlin-compiler.jar \ + org.jetbrains.jet.cli.jvm.K2JVMCompiler \ + 5000 \ + instrument=out/artifacts/instrumentation_jar/instrumentation.jar \ + +``` + +This example uses an artifact already configured in our project. +In this artifact, what to instrument is configured in the ```org.jetbrains.jet.preloading.ProfilingInstrumenterExample``` class. +This is determined by the ```src/META-INF/services/org.jetbrains.jet.preloading.instrumentation.Instrumenter``` file (see JavaDoc for ```java.util.ServiceLoader```). + +## More structured description + +**Instrumenter** is any implementation of ```org.jetbrains.jet.preloading.instrumentation.Instrumenter``` interface. + +Preloader loads the **first** instrumenter service found on the class path. +Services are provided through the [standard JDK mechanism](http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html). + +Every preloaded class is run through the instrumenter. Before exiting the program instrumenter's dupm() method is called. +**Note** JDK classes and everything in the Preloader's own class path are not preloaded, thus not instrumented. + +The ```instrumentation``` module provides a convenient way to define useful instrumenters: + +* Derive your class from ```org.jetbrains.jet.preloading.instrumentation.InterceptionInstrumenterAdaptor``` +* In this class define public static fields with ```@MethodInterceptor``` annotation + +Whatever the type of the field, if it has methods named by the convention defined below, they will be called as follows: +* ```enter.*``` - upon entering the instrumented method +* ```normalReturn.*``` - upon returning normally from the instrumented method (not throwing an exception) +* ```exception.*``` - upon explicitly throwing an exception from the instrumented method +* ```exit.*``` - upon exiting the instrumented method (either return or throw) +* ```dump.*``` - upon program termination, useful to display the results + +If any of the methods above, except for ```dump.*```, have parameters, they are treated as follows: +* *no annotation* - this parameter receives the respective parameter of the instrumented method +* ```@This``` - this parameter receives the ```this``` of the instrumented method, or ```null``` if there's no ```this``` +* ```@MethodName``` - this parameter receives the name of the instrumented method, must be a ```String``` +* ```@MethodDesc``` - this parameter receives the JVM descriptor of the instrumented method, like ```(ILjava/lang/Object;)V```, must be a ```String``` +* ```@AllArgs``` - this parameter receives an array of all arguments of the instrumented method, must be of type ```Object[]``` + +See ```org.jetbrains.jet.preloading.ProfilingInstrumenterExample```. diff --git a/compiler/preloader/instrumentation/src/META-INF/services/org.jetbrains.jet.preloading.instrumentation.Instrumenter b/compiler/preloader/instrumentation/src/META-INF/services/org.jetbrains.jet.preloading.instrumentation.Instrumenter new file mode 100644 index 00000000000..4778763239c --- /dev/null +++ b/compiler/preloader/instrumentation/src/META-INF/services/org.jetbrains.jet.preloading.instrumentation.Instrumenter @@ -0,0 +1 @@ +org.jetbrains.jet.preloading.ProfilingInstrumenterExample \ No newline at end of file diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/ProfilingInstrumenterExample.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/ProfilingInstrumenterExample.java new file mode 100644 index 00000000000..8695d63f058 --- /dev/null +++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/ProfilingInstrumenterExample.java @@ -0,0 +1,96 @@ +/* + * 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; + +import org.jetbrains.jet.preloading.instrumentation.InterceptionInstrumenterAdaptor; +import org.jetbrains.jet.preloading.instrumentation.annotations.MethodInterceptor; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +@SuppressWarnings("UnusedDeclaration") +public class ProfilingInstrumenterExample extends InterceptionInstrumenterAdaptor { + + // How many times are visit* methods of visitors called? + @MethodInterceptor(className = ".*Visitor.*", methodName = "visit.*", methodDesc = ".*", allowMultipleMatches = true) + public static final Object a = new InvocationCount(); + + public static class InvocationCount { + private int count = 0; + + public void enter() { + // This method is called upon entering a visit* method + count++; + } + + public void dump(PrintStream out) { + // This method is called upon program termination + out.println("Invocation count: " + count); + } + } + + // How much time do we spend in equals() methods of all classes inside package org + // NOTE: this works only on methods actually DECLARED in these classes + // This also logs names of actually instrumented methods to console + @MethodInterceptor(className = "org/.*", methodName = "equals", methodDesc = "\\(Ljava/lang/Object;\\)Z", logInterceptions = true) + public static final Object b = new TotalTime(); + + public static class TotalTime { + private long time = 0; + private long start = 0; + private boolean started = false; + + public void enter() { + if (!started) { + start = System.nanoTime(); + started = true; + } + } + + public void exit() { + if (started) { + time += System.nanoTime() - start; + started = false; + } + } + + public void dump(PrintStream out) { + out.printf("Total time: %.3fs\n", (time / 1e9)); + } + } + + // Collect all strings that were capitalized using StringUtil + @MethodInterceptor(className = "com/intellij/openapi/util/text/StringUtil", + methodName = "capitalize", + methodDesc = "\\(Ljava/lang/String;\\).*") + public static Object c = new CollectFirstArguments(); + + public static class CollectFirstArguments { + + private final List arguments = new ArrayList(30000); + + public void enter(Object arg) { + arguments.add(arg); + } + + public void dump(PrintStream out) { + System.out.println("Different values: " + new HashSet(arguments).size()); + } + } +}