Some docs and example

This commit is contained in:
Andrey Breslav
2013-05-13 17:07:46 +04:00
parent 9f7a4ffe56
commit 2ed217bb4a
3 changed files with 149 additions and 0 deletions
@@ -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 \
<compiler's command-line args>
```
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```.
@@ -0,0 +1 @@
org.jetbrains.jet.preloading.ProfilingInstrumenterExample
@@ -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<Object> arguments = new ArrayList<Object>(30000);
public void enter(Object arg) {
arguments.add(arg);
}
public void dump(PrintStream out) {
System.out.println("Different values: " + new HashSet<Object>(arguments).size());
}
}
}