This project was created by Antoine Gicquel in order to illustrate StackOverflow question #68715127.
Alexander Kriegisch forked and restructured it,
- building with Maven instead of Make,
- splitting the agent module into a springboard agent and a small set of classes needed by the Byte Buddy advice, extracting injecting it from its uber JAR and injecting it into the boot classpath.
- I also added a little example application to attach the agent to during runtime, utilising the launcher module - attacher would actually be a better name.
What the advice code and its dependency classes actually do according to the original author, is to track the complete
call stack of each thread, including arguments of called methods. (I did not analyse if this has to be done the way it is done, but simply kept
the functionality as-is, only factored out the map keeping the stats out from the agent class into a separate class,
because the agent is not on the boot classloader.) The whole JVM is then instrumented using Frida to call the
callStackToJSON
method, and this part works great. The Frida part is not contained in this repository.
mvn clean package
I am assuming that you have a UNIX-like shell with grep
and sed
available, e.g. Git Bash for Windows:
This application simply prints stuff to the console in an endless loop. The log output should change, as soon as the launcher in console 2 attaches the agent and the latter transforms the application class and some bootstrap classes using Byte Buddy, logging its activity to the console.
# Run application as executable JAR
java -jar application/target/application-1.0-SNAPSHOT-jar-with-dependencies.jar
# Run application from IDE
java -cp ... org.acme.app.Application
Here we start the launcher, telling it which agent (agent-1.0-SNAPSHOT.jar) to attach to which process ID. The latter
is determined by using JDK command line tool jps
, filtering its output via jps | grep -E 'application-.*\.jar' | sed -E 's/^([0-9]+).*/\1/'
in order to get the PID.
Instead of determining the PID by yourself, the launcher can also call jps
on your behalf, if instead of a numerical
PID you simply specify a regex matching the main class or main JAR in the jps
output. So you could call either of
# Run both application and launcher as executable JARs, determine PID manually (tedious)
java -jar launcher/target/launcher-1.0-SNAPSHOT-jar-with-dependencies.jar agent/target/agent-1.0-SNAPSHOT.jar $(jps | grep -E 'application-.*\.jar' | sed -E 's/^([0-9]+).*/\1/')
# Run both application and launcher as executable JARs, use regex to match application JAR name
java -jar launcher/target/launcher-1.0-SNAPSHOT-jar-with-dependencies.jar agent/target/agent-1.0-SNAPSHOT.jar application-.*\.jar
# Run both application and launcher from IDE, use regex to match application main class name
java -cp ... org.acme.agent.Launcher agent/target/agent-1.0-SNAPSHOT.jar Application