SlideShare a Scribd company logo
1 of 55
The definitive guide
to Java agents
agent fundamentals
code instrumentation
class loading
miscellaneous
attach
thread
main
thread
premain()premain() main()
agentmain()agentmain()
Main-Class: main.EntryPremain-Class: agent.PreEntry
Agent-Class: agent.AgentEntry
What are Java agents?
java -cp:. -javaagent:/home/user/myagent.jar=MyAgent main.AppEntry
Running a static Java agent
class PreEntry {
public static void premain(String arg) {
System.out.println("Hello from " + arg);
}
}
java -cp:. main.AppEntry
Running a dynamic Java agent
class AgentEntry {
public static void agentmain(String arg) {
System.out.println("Hello from " + arg);
}
}
VirtualMachine vm = VirtualMachine.attach(pid);
try {
vm.loadAgent("/home/user/myagent.jar", "MyAgent");
} finally {
vm.detach();
}
PID a
PID b
Ifownedbysameuser
java -cp:. -javaagent:/home/user/myagent.jar=MyAgent main.AppEntry
Transforming a class
class PreEntry {
public static void premain(String arg, Instrumentation inst) {
inst.addTransformer(
(classLoader, className, classIfLoaded, protectionDomain, classFile) -> {
if (shouldTransform(className)) {
return transform(classFile);
} else {
return null;
}
});
}
}
java -cp:. main.AppEntry
Retransforming a class
class AgentEntry {
public static void agentmain(String arg, Instrumentation inst) {
inst.addTransformer(
(classLoader, className, classIfLoaded, protectionDomain, classFile) -> {
if (shouldTransform(classIfLoaded)) {
return transform(classFile);
} else {
return null;
}
}, true);
inst.retransformClasses(getClassesToRetransform());
}
}
Retransforming a class: limitations
class Sample {
String m() {
return "foo";
}
}
class Sample {
String m() {
return "bar";
}
}
class Sample {
String m() {
return m2();
}
private static String m2() {
return "bar";
}
}
class Sample {
String m() {
return m2();
}
private String m2() {
return "bar";
}
}
Possible future extension: JEP 159 or Dynamic code evolution VM
But currently this does not seem to be a goal.
JNIEXPORT jint JNICALL Agent_OnLoad(
JavaVM *jvm, char *options, void *reserved) {
jvmtiEnv *jvmti; jint res; jvmtiError error;
jvmtiCapabilities capabilities; jvmtiEventCallbacks callbacks;
res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);
(void) memset(&capabilities, 0, sizeof(jvmtiCapabilities));
capabilities.can_retransform_classes = 1;
error = jvmti->AddCapabilities(&capabilities);
callbacks.ClassFileLoadHook = &myClassFileLoadHook;
error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint) sizeof(callbacks));
return JNI_OK;
}
Running a native agent
java -agentlib:/home/user/myagent.so=MyAgent main.AppEntry
class Foo {
String bar() {
return "bar";
}
How can classes be manipulated at runtime?
class Foo
Constant pool:
#1 = String #4
#2 = Class #6
#3 = Utf8 Code
#4 = Utf8 bar
#5 = Utf8 ()Ljava/lang/String;
#6 = Utf8 Foo
#7 = Utf8 java/lang/Object
java.lang.String bar();
descriptor: ()Ljava/lang/String;
Code:
0: ldc #2
2: areturn
ca fe ba be 00 00 00
34 00 10 0a 00 04 00
0d 08 00 09 07 00 0e
07 00 0f 01 00 06 3c
69 6e 69 74 3e 01 00
03 28 29 56 01 00 04
43 6f 64 65 01 00 0f
4c 69 6e 65 4e 75 6d
62 65 72 54 61 62 6c
65 01 00 03 62 61 72
01 00 14 28 29 4c 6a
61 76 61 ...
Foo.java
Foo.class (javap)
byte[]
class Foo {
void bar() {
// mock...
}
}
Example application: Mockito inline-mock maker
attaches to itself
runs in JVM
class Foo {
void bar() {
// run...
}
}
Example application: APM
agent v1
PID scan
traces / general information
agent v2
agent fundamentals
code instrumentation
class loading
miscellaneous
static byte[] transform(byte[] classFile) {
ClassReader reader = new ClassReader(classFile);
ClassWriter writer = new ClassWriter(0);
reader.accept(new ClassVisitor(ASM_7, writer) {
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
super.visit(version, access | PUBLIC, name,
signature, superName, interfaces);
}
}, 0);
return writer.toByteArray();
}
static byte[] transform(byte[] classFile) {
ClassReader reader = new ClassReader(classFile);
ClassWriter writer = new ClassWriter(0);
reader.accept(writer, 0);
return writer.toByteArray();
}
Transforming classes using ASM
static byte[] transform(byte[] classFile) {
ClassReader reader = new ClassReader(classFile);
ClassWriter writer = new ClassWriter(0);
reader.accept(new ClassVisitor(ASM_7, writer) {
@Override public MethodVisitor visitMethod(
int access, String name, String desc, String sig, String[] e) {
MethodVisitor visitor = super.visitMethod(access, name, desc, sig, e);
return new MethodVisitor(ASM_7, visitor) {
@Override public void visitCode() {
super.visitCode();
visitFieldInsn(GET_STATIC, "java/lang/System",
"out", "Ljava/lang/PrintStream;");
visitLdc("Hello World");
visitMethodInsn(INVOKEINTERFACE, "Ljava/lang/PrintStream;",
"println", "(Ljava/lang/String;)V", true);
};
}
}
}, 0);
return writer.toByteArray();
}
static boolean shouldTransform(String className) {
return "my/target/UserType".equals(className);
}
static boolean shouldTransform(String className, byte[] classFile, ClassLoader cl) {
if ("my/target/UserType".equals(className)) return true;
String[] nextType = new String[1];
while (true) {
new ClassReader(classFile, 0).accept(new ClassVisitor(ASM_7) {
@Override
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
nextType[0] = superName;
}
}, 0);
if (nextType[0] == null) return false;
else if ("my/target/UserType".equals(className)) return true;
else {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cl.getResourceAsStream(nextType[0] + ".class").writeTo(bos);
classFile = bos.toByteArray();
}
} throw new IllegalStateException();
}
static boolean shouldTransform(String className, byte[] classFile) {
if ("my/target/UserType".equals(className)) {
return true;
}
ClassReader reader = new ClassReader(classFile, 0);
boolean[] hasSuperType = new boolean[1];
reader.accept(new ClassVisitor(ASM_7) {
@Override
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
hasSuperType[0] = "my/target/UserType".equals(superName);
}
}, 0);
return hasSuperType[0];
}
Matching classes using ASM
Transforming and matching classes using Byte Buddy
class PreEntry {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(hasSuperType(named("my.target.UserType")))
.transform((builder, type, classLoader, module) -> builder
.method(any())
.intercept(MethodCall.invoke(
PrintStream.class.getMethod("println", String.class))
.onField(System.class.getField("out"))
.with("Hello world")
.andThen(SuperMethodCall.INSTANCE)))
.installOn(inst);
}
}
class UserType {
String foo() {
return "foo"
}
}
How is a transformation applied by Byte Buddy?
class UserType {
String foo() {
System.out.println("Hello World!");
return foo$original();
}
String foo$original() {
return "foo";
}
public String toString() {
System.out.println("Hello World!");
return super.toString();
}
}
class UserType {
String foo() {
System.out.println("Hello world!");
return "foo";
}
}
class AgentEntry {
public static void agentmain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORM)
.type(hasSuperType(named("my.target.UserType")))
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(HelloWorldAdvice.class).on(isMethod()))
.installOn(inst);
}
}
Decorating and retransforming classes using Byte Buddy
class AgentEntry {
public static void agentmain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(hasSuperType(named("my.target.UserType")))
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(HelloWorldAdvice.class).on(isMethod()))
.installOn(inst);
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter() {
System.out.println("Hello world!");
}
}
class UserType {
String foo() {
return "foo";
}
}
class UserType {
String foo() {
return "foo";
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter() {
System.out.println("Method enter");
}
@Advice.OnMethodExit
static void onExit() {
System.out.println("Method exit");
}
}
Advice: exit instrumentation
class UserType {
String foo() {
System.out.println("Method entry");
return "foo";
}
}
class UserType {
String foo() {
System.out.println("Method enter");
String $return = "foo";
System.out.println("Method exit");
return $return;
}
}
class UserType {
String foo() {
System.out.println("Method enter");
String $return;
if (System.nanoTime() % 2 == 0) {
$return = "bar";
goto end;
}
doSomething();
$return = "baz";
end:
System.out.println("Method exit");
return $return;
}
}
class UserType {
String foo() {
if (System.nanoTime() % 2 == 0) {
return "bar";
}
doSomething();
return "qux";
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter() {
System.out.println("Method enter");
}
@Advice.OnMethodExit
static void onExit() {
System.out.println("Method exit");
}
}
Advice: multiple returns
class UserType {
String foo(String val) {
return "foo" + val;
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter(
@Advice.Argument(0) String val) {
System.out.println("Val: " + val);
}
}
Advice: method arguments
class UserType {
String foo(String val) {
System.out.println("Val: " + val);
return "foo" + val;
}
}
class UserType {
String foo(String val) {
return "foo" + val;
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter(
@Advice.Argument(
value = 0
readOnly = false) String val) {
val = val + "bar";
}
}
Advice: changing method arguments
class UserType {
String foo(String val) {
val = val + "bar";
return "foo" + val;
}
}
class UserType {
String foo() {
String $return = "foo";
$return = "bar";
return $return;
}
}
class HelloWorldAdvice {
@Advice.OnMethodExit
static void onExit(
@Advice.Return(
readOnly = false) String ret) {
ret = "bar";
}
}
Advice: changing the return value
class UserType {
String foo() {
return "foo";
}
}
class UserType {
String foo(String val) {
val = "foo" + val
return val;
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter(
@Advice.Argument(0) String val) {
System.out.println("Enter: " + val);
}
@Advice.OnMethodExit
static void onExit(
@Advice.Argument(0) String val) {
System.out.println("Exit: " + val);
}
}
Advice: method arguments under alteration
class UserType {
String foo(String val) {
return "foo" + val;
}
}
class UserType {
String foo(String val) {
System.out.println("Enter: " + val);
String $val = val;
$val = "foo" + $val;
String $return = $val;
System.out.println("Exit: " + val);
return $return;
}
}
class UserType {
String foo(String val) {
System.out.println("Enter: " + val);
val = "foo" + val;
String $return = val;
System.out.println("Exit: " + val);
return $return;
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter(
@Advice.Argument(0) String val) {
System.out.println("Enter: " + val);
}
@Advice.OnMethodExit(
backupArguments = false)
static void onExit(
@Advice.Argument(0) String val) {
System.out.println("Exit: " + val);
}
}
Advice: retaining method argument changes
class UserType {
String foo() throws Exception {
System.out.println("Method enter");
String $return = null;
IOException $exception = null;
try {
if (System.nanoTime() % 2 == 0) {
throw new IOException();
} else {
$return = "qux";
}
} catch (IOException e) {
$exception = e;
}
System.out.println("Method exit");
if ($exception == null) {
return $return;
} else throw $exception;
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static void onEnter() {
System.out.println("Method enter");
}
@Advice.OnMethodExit(
onThrowable = IOException.class)
static void onExit() {
System.out.println("Method exit");
}
}
Advice: exits on exceptions
class UserType {
String foo() throws Exception {
if (System.nanoTime() % 2 == 0) {
throw new IOException();
} else {
return "qux";
}
}
}
class HelloWorldAdvice {
@Advice.OnMethodExit(
onThrowable = IOException.class)
static void onExit(
@Advice.Return(
readOnly = false) String ret
@Advice.Thrown(
readOnly = false) Throwable t) {
ret = "bar";
t = null;
}
}
Advice: handling exceptions
class UserType {
String foo() throws Exception {
String $return = null;
IOException $exception = null;
try {
if (System.nanoTime() % 2 == 0) {
throw new IOException();
} else {
$return = "qux";
}
} catch (IOException e) {
$exception = e;
}
$return = "bar";
$exception = null;
if ($exception == null) {
return $return;
} else throw $exception;
}
}
class UserType {
String foo() {
doSomething();
return "foo";
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter(
skipOn = String.class)
static String onEnter() {
if (System.nanoTime() % 2 == 0) {
return "bar";
} else {
return null;
}
}
}
Advice: skipping a method
class UserType {
String foo() {
String $enter;
if (System.nanoTime() % 2 == 0) {
$enter = "bar";
} else {
$enter = null;
}
String $return;
if ($enter == null) {
doSomething();
$return = "foo";
} else {
$return = null;
}
return $return;
}
}
class UserType {
String foo() {
String $enter;
if (System.nanoTime() % 2 == 0) {
$enter = "bar";
} else {
$enter = null;
}
String $return;
if ($enter == null) {
doSomething();
$return = "foo";
} else {
$return = null;
}
$return = $enter;
return $return;
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter(
skipOn = String.class)
static String onEnter() {
if (System.nanoTime() % 2 == 0) {
return "bar";
} else {
return null;
}
}
@Advice.OnMethodExit
static void onExit(
@Advice.Enter String enter
@Advice.Return(
readOnly = false) String ret) {
ret = enter;
}
}
Advice: skipping a method and determining its return value
class UserType {
String foo() {
int $exit = 0; $exit = 3;
String $return = null;
IOException $exception = null;
do {
try {
if (System.nanoTime() % 2 == 0) {
throw new IOException();
} else $return = "qux";
} catch (IOException e) {
$exception = e;
}
$exit--;
} while ($exit != 0);
if ($exception == null) {
return $return;
} else throw $exception;
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static int onEnter(
@Advice.Exit (
readOnly = false) int exit) {
exit = 3
}
@Advice.OnMethodExit(
onThrowable = IOException.class
repeatOn =
Advice.OnNonDefaultValue.class)
static int onExit(
@Advice.Exit (
readOnly = false) int exit) {
exit--;
}
}
Advice: repeating a method
class UserType {
String foo() {
if (System.nanoTime() % 2 == 0) {
throw new IOException();
} else return "qux";
}
}
class HelloWorldAdvice {
@Advice.OnMethodEnter
static int onEnter(
@Advice.Local("val") String val) {
val = "Hello!";
}
@Advice.OnMethodExit
static int onExit(
@Advice.Local("val") String val) {
System.out.println(val);
}
}
Advice: stack allocation
class UserType {
String foo() {
String $val = null;
$val = "Hello!";
String $return = "foo";
System.out.println($val);
return $return;
}
}
class UserType {
String foo() {
return "foo";
}
}
Advice: annotation overview
@Advice.Enter
@Advice.Exit
@Advice.FieldValue
@Advice.Argument
@Advice.AllArguments
@Advice.This
@Advice.Origin
@Advice.Return
@Advice.Thrown
@Advice.Local
Returns value produced by enter advice. Allows for conditional method skipping.
Returns value produced by exit advice. Allows for conditional method repeating.
Allows reading and writing of instance field value.
Allows accessing method argument. Argument retention can be set on method level.
Allows accessing all method arguments.
Allows accessing instrumented instance.
Allows accessing method context.
Allows accessing method return value (if any).
Allows accessing thrown instance by method (if any).
Allows sharing stack-allocated value between enter and exit advice.
Stack map frames
static void foo(String val) {
if (val == null) {
return;
}
System.out.println(val);
}
0: ALOAD 0
1: IFNONNULL 3
2: RETURN
3: GETSTATIC java/lang/System.out
4: ALOAD 0
5: INVOKEVIRTUAL java/io/PrintStream.println
6: RETURN
Stack map frames: (since Java 6)
3: [String]
MethodVisitor methodVisitor = ...
Label label = new Label();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitJumpInsn(IFNONNULL, label);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitLabel(label);
methodVisitor.visitStackMapFrame(F_SAME, ...);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", ...);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", ...);
methodVisitor.visitInsn(RETURN);
Computing stack map frames in ASM
static void foo(String val) {
if (val == null) {
return;
}
System.out.println(val);
}
ClassWriter classWriter = new ClassWriter(COMPUTE_FRAMES);
Class<?> loaded1 = ClassWriter.class
.getClassLoader()
.loadClass(type1.replace("/", "."));
[String,Object]
6: ALOAD 1
7: ASTORE 2
8: ICONST_0
9: IFNE 5
10: RETURN
[String,Object,Object]
11: RETURN
Fusing stack map frames in Byte Buddy advice
@Advice.OnMethodEnter
static void onEnter(
@Advice.Argument(0) String val) {
String copy = val;
if (false) return;
}
[String]
0: ALOAD 0
1: ASTORE 1
2: ICONST_0
3: IFNE 5
4: RETURN
[String,String]
5: RETURN
[String,Object]
0: ALOAD 1
1: ASTORE 2
2: ICONST_0
3: IFNE 5
4: RETURN
[String,Object,Object]
5: RETURN
static void foo(String val, Object other) {
Object copy = other;
if (false) return;
}
[String,Object]
0: ALOAD 0
1: ASTORE 1
2: ICONST_0
3: IFNE 5
4: RETURN
[String,String]
5: RETURN
[String,Object]
0: ALOAD 0
1: ASTORE 2
2: ICONST_0
3: IFNE 5
4: RETURN
[String,Object,String]
5: RETURN
[String,Object]
0: ALOAD 0
1: ASTORE 2
2: ICONST_0
3: IFNE 5
4: GOTO 6
[String,Object,String]
5: GOTO 6
[String,Object]
class FaultyAdvice {
static String value;
@Advice.OnMethodEnter
static void onEnter() {
helper(value);
}
private static void helper(String value) {
// ...
}
}
Limitations of advice templates
class FaultyAdvice {
static String value;
@Advice.OnMethodEnter
static void onEnter(@MyConstantValue String value) {
helper(value);
}
private static void helper(String value) {
// ...
}
}
// #BB-613: since Java 8, a copy would be possible
class UserType {
void foo() {
int random = ThreadLocalRandom.current().nextInt();
System.out.println(random);
}
}
class UserType {
void foo() {
int random = ThreadLocalRandom.current().nextInt();
}
}
class UserType {
void foo() {
int random = ThreadLocalRandom.current().nextInt();
System.out.print(random);
}
}
class PreEntry {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(hasSuperType(named("my.target.UserType")))
.transform((builder, type, classLoader, module) -> builder
.visit(MemberSubstitution.strict()
.method(named("println"))
.stub()
.on(named("foo")))
.installOn(inst);
}
}
class PreEntry {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(hasSuperType(named("my.target.UserType")))
.transform((builder, type, classLoader, module) -> builder
.visit(MemberSubstitution.strict()
.method(named("println"))
.replaceWithMethod(named("print").and(takesArguments(String.class))
.on(named("foo")))
.installOn(inst);
}
}
In-method code substitution
agent fundamentals
code instrumentation
class loading
miscellaneous
boot
Class loader hierarchies
extension
system
webapp 2 webapp 3
OSGi
webapp 1
module 1 custom
module 2
module 3
platform
class PreEntry
class AgentEntry
class HttpClientAdvice {
@Advice.OnMethodEnter
static long onEnter() {
return System.currentTimeMillis();
}
@Advice.OnMethodExit
static void onExit(
@Advice.Argument(0) HttpRequest request,
@Advice.Return HttpResponse response,
@Advice.Enter long time) {
Tracing.record(request.getUrl(),
response.getStatus(),
System.currentTimeMillis() - time);
}
}
Supporting unknown class loader hierarchies
class PreEntry {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(named("some.HttpClient"))
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(HttpClientAdvice.class).on(named("send")))
.installOn(inst);
}
}
Supporting unknown class loader hierarchies
class PreEntry {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(named("some.HttpClient"))
.transform((builder, type, classLoader, module) -> builder
.visit(new AgentBuilder.Transformer.ForAdvice()
.include(PreEntry.class.getClassLoader())
.advice(named("send"), "my.HttpClientAdvice")))
.installOn(inst);
}
}
boot
Supporting unknown class loader hierarchies
extension
system
custom
platform
TypePool
TypeDescription
("my.HttpClientAdvice")
class PreEntry {
public static void premain(String arg, Instrumentation inst) {
inst.appendToBootstrapClassLoaderSearch(new JarFile("trace.jar"));
Recording.dispatcher = initializeRecording();
new AgentBuilder.Default()
.type(named("some.HttpClient"))
.transform((builder, type, classLoader, module) -> builder
.visit(new AgentBuilder.Transformer.ForAdvice()
.include(PreEntry.class.getClassLoader())
.advice(named("send"), "my.HttpClientAdvice")))
.installOn(inst);
}
}
Supporting unknown class loader hierarchies
public abstract class Recording {
public static Recording dispatcher;
public static void record(String url, int status, long time) {
tracing.doRecord(url, status, time);
}
protected abstract void doRecord(String url, int status, long time);
}
boot
Supporting unknown class loader hierarchies
extension
system
custom
platform
TypePool
TypeDescription
("my.HttpClientAdvice")
class Recording
class java.lang.Recording boot
Supporting unknown module hierarchies
extension
system
custom
platform
TypePool
TypeDescription
("my.HttpClientAdvice")
class Recording module some.UserModule
interface Instrumentation {
void redefineModule(...);
}
class [sun|jdk.internal].misc.Unsafe {
Class<?> defineClass(...);
}
module
java.base
class FilteringClassLoader
extends ClassLoader {
@Override
public Class<?> loadClass(String name) {
if (name.startsWith("java.lang.")
return Class.forName(name,
false, null);
if (!knownClasses.contains(named)) {
throw new IllegalStateException();
}
// ...
}
}
class FilteringClassLoader
extends ClassLoader {
@Override
public Class<?> loadClass(String name) {
if (!knownClasses.contains(named)) {
throw new IllegalStateException();
}
// ...
}
}
boot
Supporting class loaders with explicit filters
extension
system
custom
platform
class AgentEntry {
static ClassLoader previous;
public static void agentmain(String arg, Instrumentation inst)
throws Exception {
ClassLoader loader = new URLClassLoader(new URL[] {arg});
loader.loadClass("agent.EntryPoint")
.getMethod("init", Instrumentation.class, ClassLoader.class)
.invoke(inst, previous);
previous = loader;
}
}
Making agents updatable
boot
Making agents updatable
extension
system
custom
platform
TypePool
TypeDescription
("my.HttpClientAdvice")
class Recording
agent v1
agent v2
agent fundamentals
code instrumentation
class loading
miscellaneous
long pid = ProcessHandle.current().pid();
VMHelper.runInNewVm(() -> {
VirtualMachine vm = VirtualMachine.attach(String.valueOf(pid));
try {
vm.loadAgent(MyAttachHelper.class.getProtectionDomain()
.getCodeSource()
.getURL()
.toString(), null);
} finally {
vm.detach();
}
});
Instrumentation inst = MyAttachHelper.inst;
Self-attaching for testing a Java agent
long pid = ProcessHandle.current().pid();
VirtualMachine vm = VirtualMachine.attach(String.valueOf(pid));
try {
vm.loadAgent(MyAttachHelper.class.getProtectionDomain()
.getCodeSource()
.getURL()
.toString(), null);
} finally {
vm.detach();
}
class MyAttachHelper {
Instrumentation inst;
public static void agentmain(String arg, Instrumentation inst) {
MyAttachHelper.inst = inst;
}
}
Attachment using Byte Buddy
Instrumentation inst = ByteBuddyAgent.install();
interface AttachmentProvider
1. jdk.attach (JDK only) / IBM-namespace-aware
2. jdk.attach via helper process (JDK only) / IBM-namespace-aware
3. Attachment emulation (via JNA/JNI), no self-attachment constraint (POSIX, Solaris, Windows / HotSpot, OpenJ9)
ByteBuddyAgent.attach(new File("myagent.jar"), "123");
Instance-bound state without adding fields
public class StateHandler { // injected into boot loader
public static final Map<Object, Object> state =
Collections.synchronizedMap(new WeakHashMap<>());
}
public class StateHandler { // injected into boot loader
public static final WeakConcurrentMap<Object, Object> state =
new WeakConcurrentMap.WithInlinedExpunction<>();
}
class UserClass {
void foo() {
// ...
}
}
class UserClass {
static Dispatcher dispatcher;
static {
Class.forName(
"net.bytebuddy.Nexus", false, ClassLoader.getSystemClassLoader())
.getMethod("init", Class.class)
.invoke(null, UserClass.class);
}
void foo() {
dispatcher.run();
// ...
}
}
class AgentEntry {
public static void agentmain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORM)
.with(BatchAllocator.Partitioning.of(4))
.with(DiscoveryStrategy.Reiterating.INSTANCE)
.type(aLotOfTypes())
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(SomeAdvice.class).on(isMethod()))
.installOn(inst);
}
}
class AgentEntry {
public static void agentmain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORM)
.with(BatchAllocator.Partitioning.of(4))
.with(DiscoveryStrategy.Reiterating.INSTANCE)
.with(new ResubmissionScheduler.WithFixedDelay(
Executors.newSingleThreadScheduledExecutor(), 1, TimeUnit.SECOND))
.type(aLotOfTypes())
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(SomeAdvice.class).on(isMethod()))
.installOn(inst);
}
}
class AgentEntry {
public static void agentmain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORM)
.type(aLotOfTypes())
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(SomeAdvice.class).on(isMethod()))
.installOn(inst);
}
}
Increasing retransformation resilience
class AgentEntry {
public static void agentmain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORM)
.with(BatchAllocator.Partitioning.of(4))
.type(aLotOfTypes())
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(SomeAdvice.class).on(isMethod()))
.installOn(inst);
}
}
Balancing performance: JIT and GC inference
-XX:MaxInlineLevel // maximum nested calls
-XX:MaxInlineSize // maximum byte code size
-XX:FreqInlineSize // maximum byte code size (frequently called)
-XX:MaxTrivialSize // maximum byte code size (trivial, e.g. getter/setter)
-XX:MinInliningThreshold // minimum amount of calls before inlining
-XX:LiveNodeCountInliningCutoff // max number of live nodes
object life time
main
agent
object life time: young generation, tenured generation
http://rafael.codes
@rafaelcodes
http://documents4j.com
https://github.com/documents4j/documents4j
http://bytebuddy.net
https://github.com/raphw/byte-buddy

More Related Content

What's hot

Hexagonal architecture for java applications
Hexagonal architecture for java applicationsHexagonal architecture for java applications
Hexagonal architecture for java applicationsFabricio Epaminondas
 
Integration testing with spring @snow one
Integration testing with spring @snow oneIntegration testing with spring @snow one
Integration testing with spring @snow oneVictor Rentea
 
Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...
Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...
Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...Edureka!
 
Clean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a MonolithClean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a MonolithVictor Rentea
 
GraalVM: Run Programs Faster Everywhere
GraalVM: Run Programs Faster EverywhereGraalVM: Run Programs Faster Everywhere
GraalVM: Run Programs Faster EverywhereJ On The Beach
 
Domain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDeclan Whelan
 
Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8Victor Rentea
 
php-src の歩き方
php-src の歩き方php-src の歩き方
php-src の歩き方do_aki
 
Asynchronous JavaScript Programming
Asynchronous JavaScript ProgrammingAsynchronous JavaScript Programming
Asynchronous JavaScript ProgrammingHaim Michael
 
Working With Legacy Code
Working With Legacy CodeWorking With Legacy Code
Working With Legacy CodeAndrea Polci
 
プログラミング言語Clojureのニャンパスでの活用事例
プログラミング言語Clojureのニャンパスでの活用事例プログラミング言語Clojureのニャンパスでの活用事例
プログラミング言語Clojureのニャンパスでの活用事例sohta
 
Clean architecture
Clean architectureClean architecture
Clean architecture.NET Crowd
 
Rust system programming language
Rust system programming languageRust system programming language
Rust system programming languagerobin_sy
 
1 java programming- introduction
1  java programming- introduction1  java programming- introduction
1 java programming- introductionjyoti_lakhani
 
Deep drive into rust programming language
Deep drive into rust programming languageDeep drive into rust programming language
Deep drive into rust programming languageVigneshwer Dhinakaran
 
Functional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User GroupFunctional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User GroupVictor Rentea
 
Polymorphism in java, method overloading and method overriding
Polymorphism in java,  method overloading and method overridingPolymorphism in java,  method overloading and method overriding
Polymorphism in java, method overloading and method overridingJavaTportal
 
JBoss Enterprise Application Platform 6 Troubleshooting
JBoss Enterprise Application Platform 6 TroubleshootingJBoss Enterprise Application Platform 6 Troubleshooting
JBoss Enterprise Application Platform 6 TroubleshootingAlexandre Cavalcanti
 
Asynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & PromisesAsynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & PromisesHùng Nguyễn Huy
 

What's hot (20)

Hexagonal architecture for java applications
Hexagonal architecture for java applicationsHexagonal architecture for java applications
Hexagonal architecture for java applications
 
Integration testing with spring @snow one
Integration testing with spring @snow oneIntegration testing with spring @snow one
Integration testing with spring @snow one
 
Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...
Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...
Java Classes | Java Tutorial for Beginners | Java Classes and Objects | Java ...
 
Clean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a MonolithClean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a Monolith
 
GraalVM: Run Programs Faster Everywhere
GraalVM: Run Programs Faster EverywhereGraalVM: Run Programs Faster Everywhere
GraalVM: Run Programs Faster Everywhere
 
Lombok ハンズオン
Lombok ハンズオンLombok ハンズオン
Lombok ハンズオン
 
Domain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with Rails
 
Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8
 
php-src の歩き方
php-src の歩き方php-src の歩き方
php-src の歩き方
 
Asynchronous JavaScript Programming
Asynchronous JavaScript ProgrammingAsynchronous JavaScript Programming
Asynchronous JavaScript Programming
 
Working With Legacy Code
Working With Legacy CodeWorking With Legacy Code
Working With Legacy Code
 
プログラミング言語Clojureのニャンパスでの活用事例
プログラミング言語Clojureのニャンパスでの活用事例プログラミング言語Clojureのニャンパスでの活用事例
プログラミング言語Clojureのニャンパスでの活用事例
 
Clean architecture
Clean architectureClean architecture
Clean architecture
 
Rust system programming language
Rust system programming languageRust system programming language
Rust system programming language
 
1 java programming- introduction
1  java programming- introduction1  java programming- introduction
1 java programming- introduction
 
Deep drive into rust programming language
Deep drive into rust programming languageDeep drive into rust programming language
Deep drive into rust programming language
 
Functional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User GroupFunctional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User Group
 
Polymorphism in java, method overloading and method overriding
Polymorphism in java,  method overloading and method overridingPolymorphism in java,  method overloading and method overriding
Polymorphism in java, method overloading and method overriding
 
JBoss Enterprise Application Platform 6 Troubleshooting
JBoss Enterprise Application Platform 6 TroubleshootingJBoss Enterprise Application Platform 6 Troubleshooting
JBoss Enterprise Application Platform 6 Troubleshooting
 
Asynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & PromisesAsynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & Promises
 

Similar to The definitive guide to java agents

Making Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMMaking Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMRafael Winterhalter
 
Code generation for alternative languages
Code generation for alternative languagesCode generation for alternative languages
Code generation for alternative languagesRafael Winterhalter
 
Greach, GroovyFx Workshop
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx WorkshopDierk König
 
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistJEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistAnton Arhipov
 
SystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features SummarySystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features SummaryAmal Khailtash
 
Learning Java 1 – Introduction
Learning Java 1 – IntroductionLearning Java 1 – Introduction
Learning Java 1 – Introductioncaswenson
 
Testing in android
Testing in androidTesting in android
Testing in androidjtrindade
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
No dark magic - Byte code engineering in the real world
No dark magic - Byte code engineering in the real worldNo dark magic - Byte code engineering in the real world
No dark magic - Byte code engineering in the real worldtcurdt
 
Ast transformations
Ast transformationsAst transformations
Ast transformationsHamletDRC
 
05 pig user defined functions (udfs)
05 pig user defined functions (udfs)05 pig user defined functions (udfs)
05 pig user defined functions (udfs)Subhas Kumar Ghosh
 
Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...
Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...
Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...Ayes Chinmay
 

Similar to The definitive guide to java agents (20)

Making Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMMaking Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVM
 
Java byte code in practice
Java byte code in practiceJava byte code in practice
Java byte code in practice
 
Mastering Java ByteCode
Mastering Java ByteCodeMastering Java ByteCode
Mastering Java ByteCode
 
Griffon @ Svwjug
Griffon @ SvwjugGriffon @ Svwjug
Griffon @ Svwjug
 
Code generation for alternative languages
Code generation for alternative languagesCode generation for alternative languages
Code generation for alternative languages
 
Greach, GroovyFx Workshop
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx Workshop
 
Server1
Server1Server1
Server1
 
java input & output statements
 java input & output statements java input & output statements
java input & output statements
 
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistJEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
 
SystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features SummarySystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features Summary
 
Learning Java 1 – Introduction
Learning Java 1 – IntroductionLearning Java 1 – Introduction
Learning Java 1 – Introduction
 
Testing in android
Testing in androidTesting in android
Testing in android
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
No dark magic - Byte code engineering in the real world
No dark magic - Byte code engineering in the real worldNo dark magic - Byte code engineering in the real world
No dark magic - Byte code engineering in the real world
 
Coding Ajax
Coding AjaxCoding Ajax
Coding Ajax
 
Jvm a brief introduction
Jvm  a brief introductionJvm  a brief introduction
Jvm a brief introduction
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 
Taking User Input in Java
Taking User Input in JavaTaking User Input in Java
Taking User Input in Java
 
05 pig user defined functions (udfs)
05 pig user defined functions (udfs)05 pig user defined functions (udfs)
05 pig user defined functions (udfs)
 
Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...
Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...
Internet and Web Technology (CLASS-16) [Basic Elements of Java Program] | NIC...
 

More from Rafael Winterhalter

Java and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystemJava and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystemRafael Winterhalter
 
Event-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVMEvent-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVMRafael Winterhalter
 
Getting started with Java 9 modules
Getting started with Java 9 modulesGetting started with Java 9 modules
Getting started with Java 9 modulesRafael Winterhalter
 
Monitoring distributed (micro-)services
Monitoring distributed (micro-)servicesMonitoring distributed (micro-)services
Monitoring distributed (micro-)servicesRafael Winterhalter
 
An introduction to JVM performance
An introduction to JVM performanceAn introduction to JVM performance
An introduction to JVM performanceRafael Winterhalter
 
Understanding Java byte code and the class file format
Understanding Java byte code and the class file formatUnderstanding Java byte code and the class file format
Understanding Java byte code and the class file formatRafael Winterhalter
 
A topology of memory leaks on the JVM
A topology of memory leaks on the JVMA topology of memory leaks on the JVM
A topology of memory leaks on the JVMRafael Winterhalter
 

More from Rafael Winterhalter (13)

Java and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystemJava and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystem
 
Byte code field report
Byte code field reportByte code field report
Byte code field report
 
Event-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVMEvent-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVM
 
Java 10, Java 11 and beyond
Java 10, Java 11 and beyondJava 10, Java 11 and beyond
Java 10, Java 11 and beyond
 
Getting started with Java 9 modules
Getting started with Java 9 modulesGetting started with Java 9 modules
Getting started with Java 9 modules
 
Monitoring distributed (micro-)services
Monitoring distributed (micro-)servicesMonitoring distributed (micro-)services
Monitoring distributed (micro-)services
 
An Overview of Project Jigsaw
An Overview of Project JigsawAn Overview of Project Jigsaw
An Overview of Project Jigsaw
 
Migrating to JUnit 5
Migrating to JUnit 5Migrating to JUnit 5
Migrating to JUnit 5
 
The Java memory model made easy
The Java memory model made easyThe Java memory model made easy
The Java memory model made easy
 
An introduction to JVM performance
An introduction to JVM performanceAn introduction to JVM performance
An introduction to JVM performance
 
Unit testing concurrent code
Unit testing concurrent codeUnit testing concurrent code
Unit testing concurrent code
 
Understanding Java byte code and the class file format
Understanding Java byte code and the class file formatUnderstanding Java byte code and the class file format
Understanding Java byte code and the class file format
 
A topology of memory leaks on the JVM
A topology of memory leaks on the JVMA topology of memory leaks on the JVM
A topology of memory leaks on the JVM
 

Recently uploaded

GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZABSYZ Inc
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Rob Geurden
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxAndreas Kunz
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
How To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROHow To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROmotivationalword821
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identityteam-WIBU
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 

Recently uploaded (20)

GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZ
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
How To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROHow To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTRO
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identity
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 

The definitive guide to java agents

  • 4. java -cp:. -javaagent:/home/user/myagent.jar=MyAgent main.AppEntry Running a static Java agent class PreEntry { public static void premain(String arg) { System.out.println("Hello from " + arg); } }
  • 5. java -cp:. main.AppEntry Running a dynamic Java agent class AgentEntry { public static void agentmain(String arg) { System.out.println("Hello from " + arg); } } VirtualMachine vm = VirtualMachine.attach(pid); try { vm.loadAgent("/home/user/myagent.jar", "MyAgent"); } finally { vm.detach(); } PID a PID b Ifownedbysameuser
  • 6. java -cp:. -javaagent:/home/user/myagent.jar=MyAgent main.AppEntry Transforming a class class PreEntry { public static void premain(String arg, Instrumentation inst) { inst.addTransformer( (classLoader, className, classIfLoaded, protectionDomain, classFile) -> { if (shouldTransform(className)) { return transform(classFile); } else { return null; } }); } }
  • 7. java -cp:. main.AppEntry Retransforming a class class AgentEntry { public static void agentmain(String arg, Instrumentation inst) { inst.addTransformer( (classLoader, className, classIfLoaded, protectionDomain, classFile) -> { if (shouldTransform(classIfLoaded)) { return transform(classFile); } else { return null; } }, true); inst.retransformClasses(getClassesToRetransform()); } }
  • 8. Retransforming a class: limitations class Sample { String m() { return "foo"; } } class Sample { String m() { return "bar"; } } class Sample { String m() { return m2(); } private static String m2() { return "bar"; } } class Sample { String m() { return m2(); } private String m2() { return "bar"; } } Possible future extension: JEP 159 or Dynamic code evolution VM But currently this does not seem to be a goal.
  • 9. JNIEXPORT jint JNICALL Agent_OnLoad( JavaVM *jvm, char *options, void *reserved) { jvmtiEnv *jvmti; jint res; jvmtiError error; jvmtiCapabilities capabilities; jvmtiEventCallbacks callbacks; res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1); (void) memset(&capabilities, 0, sizeof(jvmtiCapabilities)); capabilities.can_retransform_classes = 1; error = jvmti->AddCapabilities(&capabilities); callbacks.ClassFileLoadHook = &myClassFileLoadHook; error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint) sizeof(callbacks)); return JNI_OK; } Running a native agent java -agentlib:/home/user/myagent.so=MyAgent main.AppEntry
  • 10. class Foo { String bar() { return "bar"; } How can classes be manipulated at runtime? class Foo Constant pool: #1 = String #4 #2 = Class #6 #3 = Utf8 Code #4 = Utf8 bar #5 = Utf8 ()Ljava/lang/String; #6 = Utf8 Foo #7 = Utf8 java/lang/Object java.lang.String bar(); descriptor: ()Ljava/lang/String; Code: 0: ldc #2 2: areturn ca fe ba be 00 00 00 34 00 10 0a 00 04 00 0d 08 00 09 07 00 0e 07 00 0f 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 03 62 61 72 01 00 14 28 29 4c 6a 61 76 61 ... Foo.java Foo.class (javap) byte[]
  • 11. class Foo { void bar() { // mock... } } Example application: Mockito inline-mock maker attaches to itself runs in JVM class Foo { void bar() { // run... } }
  • 12. Example application: APM agent v1 PID scan traces / general information agent v2
  • 14. static byte[] transform(byte[] classFile) { ClassReader reader = new ClassReader(classFile); ClassWriter writer = new ClassWriter(0); reader.accept(new ClassVisitor(ASM_7, writer) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access | PUBLIC, name, signature, superName, interfaces); } }, 0); return writer.toByteArray(); } static byte[] transform(byte[] classFile) { ClassReader reader = new ClassReader(classFile); ClassWriter writer = new ClassWriter(0); reader.accept(writer, 0); return writer.toByteArray(); } Transforming classes using ASM static byte[] transform(byte[] classFile) { ClassReader reader = new ClassReader(classFile); ClassWriter writer = new ClassWriter(0); reader.accept(new ClassVisitor(ASM_7, writer) { @Override public MethodVisitor visitMethod( int access, String name, String desc, String sig, String[] e) { MethodVisitor visitor = super.visitMethod(access, name, desc, sig, e); return new MethodVisitor(ASM_7, visitor) { @Override public void visitCode() { super.visitCode(); visitFieldInsn(GET_STATIC, "java/lang/System", "out", "Ljava/lang/PrintStream;"); visitLdc("Hello World"); visitMethodInsn(INVOKEINTERFACE, "Ljava/lang/PrintStream;", "println", "(Ljava/lang/String;)V", true); }; } } }, 0); return writer.toByteArray(); }
  • 15. static boolean shouldTransform(String className) { return "my/target/UserType".equals(className); } static boolean shouldTransform(String className, byte[] classFile, ClassLoader cl) { if ("my/target/UserType".equals(className)) return true; String[] nextType = new String[1]; while (true) { new ClassReader(classFile, 0).accept(new ClassVisitor(ASM_7) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { nextType[0] = superName; } }, 0); if (nextType[0] == null) return false; else if ("my/target/UserType".equals(className)) return true; else { ByteArrayOutputStream bos = new ByteArrayOutputStream(); cl.getResourceAsStream(nextType[0] + ".class").writeTo(bos); classFile = bos.toByteArray(); } } throw new IllegalStateException(); } static boolean shouldTransform(String className, byte[] classFile) { if ("my/target/UserType".equals(className)) { return true; } ClassReader reader = new ClassReader(classFile, 0); boolean[] hasSuperType = new boolean[1]; reader.accept(new ClassVisitor(ASM_7) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { hasSuperType[0] = "my/target/UserType".equals(superName); } }, 0); return hasSuperType[0]; } Matching classes using ASM
  • 16. Transforming and matching classes using Byte Buddy class PreEntry { public static void premain(String arg, Instrumentation inst) { new AgentBuilder.Default() .type(hasSuperType(named("my.target.UserType"))) .transform((builder, type, classLoader, module) -> builder .method(any()) .intercept(MethodCall.invoke( PrintStream.class.getMethod("println", String.class)) .onField(System.class.getField("out")) .with("Hello world") .andThen(SuperMethodCall.INSTANCE))) .installOn(inst); } }
  • 17. class UserType { String foo() { return "foo" } } How is a transformation applied by Byte Buddy? class UserType { String foo() { System.out.println("Hello World!"); return foo$original(); } String foo$original() { return "foo"; } public String toString() { System.out.println("Hello World!"); return super.toString(); } }
  • 18. class UserType { String foo() { System.out.println("Hello world!"); return "foo"; } } class AgentEntry { public static void agentmain(String arg, Instrumentation inst) { new AgentBuilder.Default() .disableClassFormatChanges() .with(RedefinitionStrategy.RETRANSFORM) .type(hasSuperType(named("my.target.UserType"))) .transform((builder, type, classLoader, module) -> builder .visit(Advice.to(HelloWorldAdvice.class).on(isMethod())) .installOn(inst); } } Decorating and retransforming classes using Byte Buddy class AgentEntry { public static void agentmain(String arg, Instrumentation inst) { new AgentBuilder.Default() .type(hasSuperType(named("my.target.UserType"))) .transform((builder, type, classLoader, module) -> builder .visit(Advice.to(HelloWorldAdvice.class).on(isMethod())) .installOn(inst); } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter() { System.out.println("Hello world!"); } } class UserType { String foo() { return "foo"; } }
  • 19. class UserType { String foo() { return "foo"; } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter() { System.out.println("Method enter"); } @Advice.OnMethodExit static void onExit() { System.out.println("Method exit"); } } Advice: exit instrumentation class UserType { String foo() { System.out.println("Method entry"); return "foo"; } } class UserType { String foo() { System.out.println("Method enter"); String $return = "foo"; System.out.println("Method exit"); return $return; } }
  • 20. class UserType { String foo() { System.out.println("Method enter"); String $return; if (System.nanoTime() % 2 == 0) { $return = "bar"; goto end; } doSomething(); $return = "baz"; end: System.out.println("Method exit"); return $return; } } class UserType { String foo() { if (System.nanoTime() % 2 == 0) { return "bar"; } doSomething(); return "qux"; } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter() { System.out.println("Method enter"); } @Advice.OnMethodExit static void onExit() { System.out.println("Method exit"); } } Advice: multiple returns
  • 21. class UserType { String foo(String val) { return "foo" + val; } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter( @Advice.Argument(0) String val) { System.out.println("Val: " + val); } } Advice: method arguments class UserType { String foo(String val) { System.out.println("Val: " + val); return "foo" + val; } }
  • 22. class UserType { String foo(String val) { return "foo" + val; } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter( @Advice.Argument( value = 0 readOnly = false) String val) { val = val + "bar"; } } Advice: changing method arguments class UserType { String foo(String val) { val = val + "bar"; return "foo" + val; } }
  • 23. class UserType { String foo() { String $return = "foo"; $return = "bar"; return $return; } } class HelloWorldAdvice { @Advice.OnMethodExit static void onExit( @Advice.Return( readOnly = false) String ret) { ret = "bar"; } } Advice: changing the return value class UserType { String foo() { return "foo"; } }
  • 24. class UserType { String foo(String val) { val = "foo" + val return val; } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter( @Advice.Argument(0) String val) { System.out.println("Enter: " + val); } @Advice.OnMethodExit static void onExit( @Advice.Argument(0) String val) { System.out.println("Exit: " + val); } } Advice: method arguments under alteration class UserType { String foo(String val) { return "foo" + val; } } class UserType { String foo(String val) { System.out.println("Enter: " + val); String $val = val; $val = "foo" + $val; String $return = $val; System.out.println("Exit: " + val); return $return; } }
  • 25. class UserType { String foo(String val) { System.out.println("Enter: " + val); val = "foo" + val; String $return = val; System.out.println("Exit: " + val); return $return; } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter( @Advice.Argument(0) String val) { System.out.println("Enter: " + val); } @Advice.OnMethodExit( backupArguments = false) static void onExit( @Advice.Argument(0) String val) { System.out.println("Exit: " + val); } } Advice: retaining method argument changes
  • 26. class UserType { String foo() throws Exception { System.out.println("Method enter"); String $return = null; IOException $exception = null; try { if (System.nanoTime() % 2 == 0) { throw new IOException(); } else { $return = "qux"; } } catch (IOException e) { $exception = e; } System.out.println("Method exit"); if ($exception == null) { return $return; } else throw $exception; } } class HelloWorldAdvice { @Advice.OnMethodEnter static void onEnter() { System.out.println("Method enter"); } @Advice.OnMethodExit( onThrowable = IOException.class) static void onExit() { System.out.println("Method exit"); } } Advice: exits on exceptions class UserType { String foo() throws Exception { if (System.nanoTime() % 2 == 0) { throw new IOException(); } else { return "qux"; } } }
  • 27. class HelloWorldAdvice { @Advice.OnMethodExit( onThrowable = IOException.class) static void onExit( @Advice.Return( readOnly = false) String ret @Advice.Thrown( readOnly = false) Throwable t) { ret = "bar"; t = null; } } Advice: handling exceptions class UserType { String foo() throws Exception { String $return = null; IOException $exception = null; try { if (System.nanoTime() % 2 == 0) { throw new IOException(); } else { $return = "qux"; } } catch (IOException e) { $exception = e; } $return = "bar"; $exception = null; if ($exception == null) { return $return; } else throw $exception; } }
  • 28. class UserType { String foo() { doSomething(); return "foo"; } } class HelloWorldAdvice { @Advice.OnMethodEnter( skipOn = String.class) static String onEnter() { if (System.nanoTime() % 2 == 0) { return "bar"; } else { return null; } } } Advice: skipping a method class UserType { String foo() { String $enter; if (System.nanoTime() % 2 == 0) { $enter = "bar"; } else { $enter = null; } String $return; if ($enter == null) { doSomething(); $return = "foo"; } else { $return = null; } return $return; } }
  • 29. class UserType { String foo() { String $enter; if (System.nanoTime() % 2 == 0) { $enter = "bar"; } else { $enter = null; } String $return; if ($enter == null) { doSomething(); $return = "foo"; } else { $return = null; } $return = $enter; return $return; } } class HelloWorldAdvice { @Advice.OnMethodEnter( skipOn = String.class) static String onEnter() { if (System.nanoTime() % 2 == 0) { return "bar"; } else { return null; } } @Advice.OnMethodExit static void onExit( @Advice.Enter String enter @Advice.Return( readOnly = false) String ret) { ret = enter; } } Advice: skipping a method and determining its return value
  • 30. class UserType { String foo() { int $exit = 0; $exit = 3; String $return = null; IOException $exception = null; do { try { if (System.nanoTime() % 2 == 0) { throw new IOException(); } else $return = "qux"; } catch (IOException e) { $exception = e; } $exit--; } while ($exit != 0); if ($exception == null) { return $return; } else throw $exception; } } class HelloWorldAdvice { @Advice.OnMethodEnter static int onEnter( @Advice.Exit ( readOnly = false) int exit) { exit = 3 } @Advice.OnMethodExit( onThrowable = IOException.class repeatOn = Advice.OnNonDefaultValue.class) static int onExit( @Advice.Exit ( readOnly = false) int exit) { exit--; } } Advice: repeating a method class UserType { String foo() { if (System.nanoTime() % 2 == 0) { throw new IOException(); } else return "qux"; } }
  • 31. class HelloWorldAdvice { @Advice.OnMethodEnter static int onEnter( @Advice.Local("val") String val) { val = "Hello!"; } @Advice.OnMethodExit static int onExit( @Advice.Local("val") String val) { System.out.println(val); } } Advice: stack allocation class UserType { String foo() { String $val = null; $val = "Hello!"; String $return = "foo"; System.out.println($val); return $return; } } class UserType { String foo() { return "foo"; } }
  • 32. Advice: annotation overview @Advice.Enter @Advice.Exit @Advice.FieldValue @Advice.Argument @Advice.AllArguments @Advice.This @Advice.Origin @Advice.Return @Advice.Thrown @Advice.Local Returns value produced by enter advice. Allows for conditional method skipping. Returns value produced by exit advice. Allows for conditional method repeating. Allows reading and writing of instance field value. Allows accessing method argument. Argument retention can be set on method level. Allows accessing all method arguments. Allows accessing instrumented instance. Allows accessing method context. Allows accessing method return value (if any). Allows accessing thrown instance by method (if any). Allows sharing stack-allocated value between enter and exit advice.
  • 33. Stack map frames static void foo(String val) { if (val == null) { return; } System.out.println(val); } 0: ALOAD 0 1: IFNONNULL 3 2: RETURN 3: GETSTATIC java/lang/System.out 4: ALOAD 0 5: INVOKEVIRTUAL java/io/PrintStream.println 6: RETURN Stack map frames: (since Java 6) 3: [String]
  • 34. MethodVisitor methodVisitor = ... Label label = new Label(); methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitJumpInsn(IFNONNULL, label); methodVisitor.visitInsn(RETURN); methodVisitor.visitLabel(label); methodVisitor.visitStackMapFrame(F_SAME, ...); methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", ...); methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", ...); methodVisitor.visitInsn(RETURN); Computing stack map frames in ASM static void foo(String val) { if (val == null) { return; } System.out.println(val); } ClassWriter classWriter = new ClassWriter(COMPUTE_FRAMES); Class<?> loaded1 = ClassWriter.class .getClassLoader() .loadClass(type1.replace("/", "."));
  • 35. [String,Object] 6: ALOAD 1 7: ASTORE 2 8: ICONST_0 9: IFNE 5 10: RETURN [String,Object,Object] 11: RETURN Fusing stack map frames in Byte Buddy advice @Advice.OnMethodEnter static void onEnter( @Advice.Argument(0) String val) { String copy = val; if (false) return; } [String] 0: ALOAD 0 1: ASTORE 1 2: ICONST_0 3: IFNE 5 4: RETURN [String,String] 5: RETURN [String,Object] 0: ALOAD 1 1: ASTORE 2 2: ICONST_0 3: IFNE 5 4: RETURN [String,Object,Object] 5: RETURN static void foo(String val, Object other) { Object copy = other; if (false) return; } [String,Object] 0: ALOAD 0 1: ASTORE 1 2: ICONST_0 3: IFNE 5 4: RETURN [String,String] 5: RETURN [String,Object] 0: ALOAD 0 1: ASTORE 2 2: ICONST_0 3: IFNE 5 4: RETURN [String,Object,String] 5: RETURN [String,Object] 0: ALOAD 0 1: ASTORE 2 2: ICONST_0 3: IFNE 5 4: GOTO 6 [String,Object,String] 5: GOTO 6 [String,Object]
  • 36. class FaultyAdvice { static String value; @Advice.OnMethodEnter static void onEnter() { helper(value); } private static void helper(String value) { // ... } } Limitations of advice templates class FaultyAdvice { static String value; @Advice.OnMethodEnter static void onEnter(@MyConstantValue String value) { helper(value); } private static void helper(String value) { // ... } } // #BB-613: since Java 8, a copy would be possible
  • 37. class UserType { void foo() { int random = ThreadLocalRandom.current().nextInt(); System.out.println(random); } } class UserType { void foo() { int random = ThreadLocalRandom.current().nextInt(); } } class UserType { void foo() { int random = ThreadLocalRandom.current().nextInt(); System.out.print(random); } } class PreEntry { public static void premain(String arg, Instrumentation inst) { new AgentBuilder.Default() .type(hasSuperType(named("my.target.UserType"))) .transform((builder, type, classLoader, module) -> builder .visit(MemberSubstitution.strict() .method(named("println")) .stub() .on(named("foo"))) .installOn(inst); } } class PreEntry { public static void premain(String arg, Instrumentation inst) { new AgentBuilder.Default() .type(hasSuperType(named("my.target.UserType"))) .transform((builder, type, classLoader, module) -> builder .visit(MemberSubstitution.strict() .method(named("println")) .replaceWithMethod(named("print").and(takesArguments(String.class)) .on(named("foo"))) .installOn(inst); } } In-method code substitution
  • 39. boot Class loader hierarchies extension system webapp 2 webapp 3 OSGi webapp 1 module 1 custom module 2 module 3 platform class PreEntry class AgentEntry
  • 40. class HttpClientAdvice { @Advice.OnMethodEnter static long onEnter() { return System.currentTimeMillis(); } @Advice.OnMethodExit static void onExit( @Advice.Argument(0) HttpRequest request, @Advice.Return HttpResponse response, @Advice.Enter long time) { Tracing.record(request.getUrl(), response.getStatus(), System.currentTimeMillis() - time); } } Supporting unknown class loader hierarchies
  • 41. class PreEntry { public static void premain(String arg, Instrumentation inst) { new AgentBuilder.Default() .type(named("some.HttpClient")) .transform((builder, type, classLoader, module) -> builder .visit(Advice.to(HttpClientAdvice.class).on(named("send"))) .installOn(inst); } } Supporting unknown class loader hierarchies class PreEntry { public static void premain(String arg, Instrumentation inst) { new AgentBuilder.Default() .type(named("some.HttpClient")) .transform((builder, type, classLoader, module) -> builder .visit(new AgentBuilder.Transformer.ForAdvice() .include(PreEntry.class.getClassLoader()) .advice(named("send"), "my.HttpClientAdvice"))) .installOn(inst); } }
  • 42. boot Supporting unknown class loader hierarchies extension system custom platform TypePool TypeDescription ("my.HttpClientAdvice")
  • 43. class PreEntry { public static void premain(String arg, Instrumentation inst) { inst.appendToBootstrapClassLoaderSearch(new JarFile("trace.jar")); Recording.dispatcher = initializeRecording(); new AgentBuilder.Default() .type(named("some.HttpClient")) .transform((builder, type, classLoader, module) -> builder .visit(new AgentBuilder.Transformer.ForAdvice() .include(PreEntry.class.getClassLoader()) .advice(named("send"), "my.HttpClientAdvice"))) .installOn(inst); } } Supporting unknown class loader hierarchies public abstract class Recording { public static Recording dispatcher; public static void record(String url, int status, long time) { tracing.doRecord(url, status, time); } protected abstract void doRecord(String url, int status, long time); }
  • 44. boot Supporting unknown class loader hierarchies extension system custom platform TypePool TypeDescription ("my.HttpClientAdvice") class Recording
  • 45. class java.lang.Recording boot Supporting unknown module hierarchies extension system custom platform TypePool TypeDescription ("my.HttpClientAdvice") class Recording module some.UserModule interface Instrumentation { void redefineModule(...); } class [sun|jdk.internal].misc.Unsafe { Class<?> defineClass(...); } module java.base
  • 46. class FilteringClassLoader extends ClassLoader { @Override public Class<?> loadClass(String name) { if (name.startsWith("java.lang.") return Class.forName(name, false, null); if (!knownClasses.contains(named)) { throw new IllegalStateException(); } // ... } } class FilteringClassLoader extends ClassLoader { @Override public Class<?> loadClass(String name) { if (!knownClasses.contains(named)) { throw new IllegalStateException(); } // ... } } boot Supporting class loaders with explicit filters extension system custom platform
  • 47. class AgentEntry { static ClassLoader previous; public static void agentmain(String arg, Instrumentation inst) throws Exception { ClassLoader loader = new URLClassLoader(new URL[] {arg}); loader.loadClass("agent.EntryPoint") .getMethod("init", Instrumentation.class, ClassLoader.class) .invoke(inst, previous); previous = loader; } } Making agents updatable
  • 50. long pid = ProcessHandle.current().pid(); VMHelper.runInNewVm(() -> { VirtualMachine vm = VirtualMachine.attach(String.valueOf(pid)); try { vm.loadAgent(MyAttachHelper.class.getProtectionDomain() .getCodeSource() .getURL() .toString(), null); } finally { vm.detach(); } }); Instrumentation inst = MyAttachHelper.inst; Self-attaching for testing a Java agent long pid = ProcessHandle.current().pid(); VirtualMachine vm = VirtualMachine.attach(String.valueOf(pid)); try { vm.loadAgent(MyAttachHelper.class.getProtectionDomain() .getCodeSource() .getURL() .toString(), null); } finally { vm.detach(); } class MyAttachHelper { Instrumentation inst; public static void agentmain(String arg, Instrumentation inst) { MyAttachHelper.inst = inst; } }
  • 51. Attachment using Byte Buddy Instrumentation inst = ByteBuddyAgent.install(); interface AttachmentProvider 1. jdk.attach (JDK only) / IBM-namespace-aware 2. jdk.attach via helper process (JDK only) / IBM-namespace-aware 3. Attachment emulation (via JNA/JNI), no self-attachment constraint (POSIX, Solaris, Windows / HotSpot, OpenJ9) ByteBuddyAgent.attach(new File("myagent.jar"), "123");
  • 52. Instance-bound state without adding fields public class StateHandler { // injected into boot loader public static final Map<Object, Object> state = Collections.synchronizedMap(new WeakHashMap<>()); } public class StateHandler { // injected into boot loader public static final WeakConcurrentMap<Object, Object> state = new WeakConcurrentMap.WithInlinedExpunction<>(); } class UserClass { void foo() { // ... } } class UserClass { static Dispatcher dispatcher; static { Class.forName( "net.bytebuddy.Nexus", false, ClassLoader.getSystemClassLoader()) .getMethod("init", Class.class) .invoke(null, UserClass.class); } void foo() { dispatcher.run(); // ... } }
  • 53. class AgentEntry { public static void agentmain(String arg, Instrumentation inst) { new AgentBuilder.Default() .disableClassFormatChanges() .with(RedefinitionStrategy.RETRANSFORM) .with(BatchAllocator.Partitioning.of(4)) .with(DiscoveryStrategy.Reiterating.INSTANCE) .type(aLotOfTypes()) .transform((builder, type, classLoader, module) -> builder .visit(Advice.to(SomeAdvice.class).on(isMethod())) .installOn(inst); } } class AgentEntry { public static void agentmain(String arg, Instrumentation inst) { new AgentBuilder.Default() .disableClassFormatChanges() .with(RedefinitionStrategy.RETRANSFORM) .with(BatchAllocator.Partitioning.of(4)) .with(DiscoveryStrategy.Reiterating.INSTANCE) .with(new ResubmissionScheduler.WithFixedDelay( Executors.newSingleThreadScheduledExecutor(), 1, TimeUnit.SECOND)) .type(aLotOfTypes()) .transform((builder, type, classLoader, module) -> builder .visit(Advice.to(SomeAdvice.class).on(isMethod())) .installOn(inst); } } class AgentEntry { public static void agentmain(String arg, Instrumentation inst) { new AgentBuilder.Default() .disableClassFormatChanges() .with(RedefinitionStrategy.RETRANSFORM) .type(aLotOfTypes()) .transform((builder, type, classLoader, module) -> builder .visit(Advice.to(SomeAdvice.class).on(isMethod())) .installOn(inst); } } Increasing retransformation resilience class AgentEntry { public static void agentmain(String arg, Instrumentation inst) { new AgentBuilder.Default() .disableClassFormatChanges() .with(RedefinitionStrategy.RETRANSFORM) .with(BatchAllocator.Partitioning.of(4)) .type(aLotOfTypes()) .transform((builder, type, classLoader, module) -> builder .visit(Advice.to(SomeAdvice.class).on(isMethod())) .installOn(inst); } }
  • 54. Balancing performance: JIT and GC inference -XX:MaxInlineLevel // maximum nested calls -XX:MaxInlineSize // maximum byte code size -XX:FreqInlineSize // maximum byte code size (frequently called) -XX:MaxTrivialSize // maximum byte code size (trivial, e.g. getter/setter) -XX:MinInliningThreshold // minimum amount of calls before inlining -XX:LiveNodeCountInliningCutoff // max number of live nodes object life time main agent object life time: young generation, tenured generation