I want to measure the startup time of a server without a considerable overhead.
What I actually want to measure is the time from server process execution to the time that the server starts listening to a well-known port.
For example, I want to measure the startup time of a simple Netty Server. i.e. the time from startup to the time it is ready to accept requests.
I developed a Java Agent using Byte-Buddy.
public class Agent {
public static void premain(String arg, Instrumentation instrumentation) {
new AgentBuilder.Default()
.type(ElementMatchers.named("io.netty.bootstrap.AbstractBootstrap"))
.transform((builder, typeDescription, classLoader, javaModule) ->
builder.visit(Advice.to(TimeAdvice.class)
.on(ElementMatchers.named("bind").and(ElementMatchers.takesArguments(SocketAddress.class)))))
.installOn(instrumentation);
}
}
Following is the source code for TimeAdvice
public class TimeAdvice {
@Advice.OnMethodExit
static void exit(@Advice.Origin String method) {
System.out.println(String.format("Server started. Current Time (ms): %d", System.currentTimeMillis()));
System.out.println(String.format("Server started. Current Uptime (ms): %d",
ManagementFactory.getRuntimeMXBean().getUptime()));
}
}
With this agent, the startup time is around 1400ms. However, when I measure the startup time by modifying the server code, the startup time of the server is around 650ms.
Therefore, it seems that there is a considerable overhead with byte-buddy Java agent when considering the startup time.
I also tried another Java Agent with Javassist.
public class Agent {
private static final String NETTY_CLASS = "io/netty/bootstrap/AbstractBootstrap";
public static void premain(String arg, Instrumentation instrumentation) {
instrumentation.addTransformer((classLoader, s, aClass, protectionDomain, bytes) -> {
if (NETTY_CLASS.equals(s)) {
System.out.println(aClass);
long start = System.nanoTime();
// Javassist
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("io.netty.bootstrap.AbstractBootstrap");
CtMethod m = cc.getDeclaredMethod("bind", new CtClass[]{cp.get("java.net.SocketAddress")});
m.insertAfter("{ System.out.println(\"Server started. Current Uptime (ms): \" + " +
"java.lang.management.ManagementFactory.getRuntimeMXBean().getUptime());}");
byte[] byteCode = cc.toBytecode();
cc.detach();
return byteCode;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
System.out.println(String.format("Agent - Transformation Time (ms): %d", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
}
}
return null;
});
}
}
With this agent, the startup time is around 800ms.
How can I minimize the overhead and measure the startup time? Is there a way to directly transform a specific class without going through all classes? If I can transform a class directly, I think I should be able to minimize the overhead as much as possible.