This project provides a JVMTI-based symbol resolver that maps JIT-compiled code addresses back to Java method names.
The system consists of a JVMTI Agent (agent.c): Loads into the JVM and builds a symbol table of JIT-compiled methods, mapping code address ranges to method names.
The JVMTI (Java Virtual Machine Tool Interface) agent is a shared library (libagent.so) that attaches to the JVM at startup. It:
- Captures JIT Compilation Events: Listens for
CompiledMethodLoadevents when the JVM compiles Java bytecode to native machine code. - Builds Symbol Table: Stores each compiled method's code address range (
[start, end)) and associated metadata (class name, method name, signature). - Provides Lookup Function: The
lookup_symbol(const void *addr)function searches the table to find which method contains the given address.
typedef struct {
const void *code_start; // Start of JIT-compiled code
const void *code_end; // End of JIT-compiled code
char *method_name; // e.g., "heavyCompute"
char *class_sig; // e.g., "Lspike_detector/TestJavaCpuSpike;"
char *method_sig; // e.g., "(I)J"
jmethodID method; // JVM internal ID
} Symbol;- Agent Loads: At JVM startup (
Agent_OnLoad), the agent registers event callbacks and enables JIT compilation notifications. - VM Initialization: After the VM is fully initialized (
VMInitcallback), the agent callsGenerateEvents()to retroactively capture all already-compiled methods. - Symbol Collection: For each
CompiledMethodLoadevent, the agent records the method's code range and metadata. - Lookup: When an address is provided (via stdin),
lookup_symbol()performs a linear search through the table to find the matching range.
- Linux kernel ≥ 4.15
- JDK (Java Development Kit)
- GCC
# Find Java installation
find /usr -name "java" -type f 2>/dev/null | head -5
# Edit sethome.sh with the correct path, e.g.:
# export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-arm64
# Source the script
source sethome.sh# Compile JVMTI agent
gcc -shared -fPIC agent.c -o libagent.so \
-I$JAVA_HOME/include \
-I$JAVA_HOME/include/linux \
-lpthread
# Compile Java test programs
cd spike_detector
javac TestJavaCpuSpike.java
cd ..Terminal 1: Start Java App with Agent
# Run Java test program with JVMTI agent
java -agentpath:./libagent.so <JAVA_APPLICATION>
# The agent will print:
# [agent] Loaded — waiting for VM init to generate retroactive events
# [agent] VM initialized — requesting retroactive CompiledMethodLoad events
# [agent] #0 Lspike_detector/TestJavaCpuSpike;::heavyCompute [0xffff... – 0xffff...] (128 bytes)
# [agent] #1 Lspike_detector/SpikeMath;::trigBurn [0xffff... – 0xffff...] (256 bytes)
# ...
# Enter address (hex):Terminal 1: Resolve Symbols
Paste hex addresses into Terminal 1's prompt:
Enter address (hex): 0000ffffabcd1234
Symbol for 0x0000ffffabcd1234: heavyCompute (symbol_count=12)
- Agent doesn't load: Check
JAVA_HOMEand library paths. - No symbols captured: Ensure the Java app is running CPU-intensive code to trigger JIT compilation.
- "UNKNOWN" for addresses: Verify the address is within a JIT-compiled method range (check agent output).
| File | Purpose |
|---|---|
agent.c |
JVMTI agent source — builds symbol table |
sethome.sh |
JAVA_HOME setup script |