docs/QuickReference.md
A cheat sheet for experienced users. For step-by-step instructions, see Getting Started. Back to README.
| Method | Command |
|---|---|
| JBang | jbang catalog add --name btraceio https://raw.githubusercontent.com/btraceio/jbang-catalog/main/jbang-catalog.json && jbang btrace@btraceio <PID> script.java |
| SDKMan | sdk install btrace |
| Docker | docker pull btrace/btrace |
| Manual | Download latest release |
Marks a class as a BTrace script.
@BTrace
public class MyTrace { }
Primary annotation for method instrumentation.
| Parameter | Type | Description | Example |
|---|---|---|---|
clazz | String | Target class(es) | "com.example.MyClass" |
method | String | Target method(s) | "processData" |
type | String | Method signature filter | "void (java.lang.String)" |
location | Location | Where to inject code | @Location(Kind.RETURN) |
enableAt | Level | Instrumentation level control | @Level(">=1") |
exactTypeMatch | boolean | Exact type matching | true |
Examples:
// Match specific class and method
@OnMethod(clazz = "com.example.MyClass", method = "myMethod")
// Match with regex
@OnMethod(clazz = "/com\\.example\\..*/", method = "/get.*/")
// Match by annotation
@OnMethod(clazz = "@javax.ws.rs.Path", method = "@javax.ws.rs.GET")
// Match by signature
@OnMethod(clazz = "com.example.MyClass", method = "calculate", type = "int (int, int)")
Execute periodically.
@OnTimer(5000) // Every 5 seconds
public static void periodic() { }
Handle custom events sent from BTrace console.
@OnEvent("myevent")
public static void onMyEvent() { }
Execute when traced process exits.
@OnExit
public static void onExit(int exitCode) { }
Execute when memory is low.
@OnLowMemory(pool = "Tenured Gen")
public static void onLowMemory() { }
Define a JFR (Java Flight Recorder) event factory for high-performance event recording.
Requirements: OpenJDK 8 (with backported JFR) or Java 11+
@Event(
name = "MyEvent",
label = "My Custom Event",
description = "Description of the event",
category = {"myapp", "performance"},
stacktrace = true,
fields = {
@Event.Field(type = Event.FieldType.STRING, name = "message"),
@Event.Field(type = Event.FieldType.LONG, name = "duration",
kind = @Event.Field.Kind(name = Event.FieldKind.TIMESPAN))
}
)
private static JfrEvent.Factory myEventFactory;
Field Types: BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, BOOLEAN, STRING, CLASS, THREAD
Field Kinds: TIMESTAMP, TIMESPAN, DATAAMOUNT, FREQUENCY, MEMORYADDRESS, PERCENTAGE, BOOLEANFLAG, UNSIGNED
See Getting Started: JFR Integration and Pattern #9 below.
Define a handler for periodic JFR events (OpenJDK 8 or Java 11+).
@PeriodicEvent(
name = "PeriodicStats",
label = "Periodic Statistics",
period = "10 s", // or "eachChunk", "beginChunk", "endChunk"
fields = @Event.Field(type = Event.FieldType.LONG, name = "count")
)
public static void emitStats(JfrEvent event) {
if (Jfr.shouldCommit(event)) {
Jfr.setEventField(event, "count", getCount());
Jfr.commit(event);
}
}
Specify where to inject code within a method using @Location(Kind.XXX).
| Kind | Description | When | Common Parameters |
|---|---|---|---|
ENTRY | Method entry | Default | Method args |
RETURN | Method return | Normal exit | @Return, @Duration |
ERROR | Exception thrown | Uncaught exception | Throwable, @Duration |
CALL | Method call | Before/after call | @TargetInstance, @TargetMethodOrField |
LINE | Source line | Specific line | Line number |
FIELD_GET | Field read | Getting field value | @TargetInstance, @TargetMethodOrField |
FIELD_SET | Field write | Setting field value | New value, @TargetInstance |
NEW | Object creation | After new | Object type name |
NEWARRAY | Array creation | After new[] | Array type, dimensions |
CATCH | Exception catch | Entering catch block | Throwable |
THROW | Throwing exception | Before throw | Throwable |
ARRAY_GET | Array read | Getting array element | Array, index |
ARRAY_SET | Array write | Setting array element | Array, index, value |
SYNC_ENTRY | Enter synchronized | Acquiring lock | Lock object |
SYNC_EXIT | Exit synchronized | Releasing lock | Lock object |
INSTANCEOF | instanceof check | Type check | Type name, @TargetInstance |
CHECKCAST | Type cast | Casting | Type name, @TargetInstance |
Location Modifiers:
@Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/") // Any call
@Location(value = Kind.CALL, clazz = "java.io.File", method = "delete") // Specific call
@Location(value = Kind.LINE, line = 42) // Specific line number
Inject context information into probe handler parameters.
| Annotation | Type | Description | Available In |
|---|---|---|---|
@Self | Object | Current instance (this) | All |
@Return | Method return type | Return value | RETURN, CALL (after) |
@Duration | long | Duration in nanoseconds | RETURN, ERROR, CALL (after) |
@ProbeClassName | String | Enclosing class name | All |
@ProbeMethodName | String | Enclosing method name | All |
@TargetInstance | Object | Target object | CALL, FIELD_GET/SET |
@TargetMethodOrField | String | Target name | CALL, FIELD_GET/SET |
Examples:
// Method entry with arguments and context
@OnMethod(clazz = "MyClass", method = "process")
public static void onEntry(@Self Object self,
@ProbeClassName String clazz,
@ProbeMethodName String method,
String arg1, int arg2) { }
// Method return with value and duration
@OnMethod(clazz = "MyClass", method = "calculate", location = @Location(Kind.RETURN))
public static void onReturn(@Return int result, @Duration long duration) { }
// Method call tracking
@OnMethod(clazz = "MyClass", method = "/.*/", location = @Location(Kind.CALL, clazz = "/.*/", method = "/.*/"))
public static void onCall(@TargetInstance Object target, @TargetMethodOrField String method) { }
Injects an extension/service instance into your script.
@Injected (no parameters). The invokedynamic injector auto-detects
whether the service needs a runtime context or a no-arg construction and wires it
accordingly.org.openjdk.btrace.core.annotations.Injectedimport org.openjdk.btrace.core.annotations.*;
import org.openjdk.btrace.metrics.MetricsService;
@BTrace
public class LatencyProbe {
@Injected
private static MetricsService metrics;
}
See also: Architecture → architecture/ExtensionInvokeDynamicBridge.md.
Control sampling rate.
@Sampled(kind = Sampled.Sampler.Const) // Constant sampling
@Sampled(kind = Sampled.Sampler.Adaptive) // Adaptive sampling
@OnMethod(...)
public static void sampledHandler() { }
Control handler activation by instrumentation level.
@OnMethod(clazz = "...", enableAt = @Level(">=1")) // Level 1 or higher
@OnMethod(clazz = "...", enableAt = @Level("2..5")) // Level 2-5 range
@OnMethod(clazz = "...", enableAt = @Level("3")) // Exactly level 3
Export data via JMX.
@Export
private static long counter;
Declare script properties.
@Property
public static String configValue = "default";
Per-thread state.
@TLS
private static long threadStartTime;
@TLS private static long startTime;
@OnMethod(clazz = "com.example.Service", method = "process")
public static void onEntry() {
startTime = timeNanos();
}
@OnMethod(clazz = "com.example.Service", method = "process", location = @Location(Kind.RETURN))
public static void onReturn() {
long duration = timeNanos() - startTime;
println("Duration: " + str(duration / 1000000) + " ms");
}
private static Map<String, AtomicInteger> counts = Collections.newHashMap();
@OnMethod(clazz = "com.example.Service", method = "/.*/")
public static void onMethod(@ProbeMethodName String method) {
AtomicInteger counter = Collections.get(counts, method);
if (counter == null) {
counter = Atomic.newAtomicInteger(0);
Collections.put(counts, method, counter);
}
Atomic.incrementAndGet(counter);
}
@OnTimer(5000)
public static void printStats() {
printMap(counts);
}
@OnMethod(clazz = "com.example.Service", method = "/.*/", location = @Location(Kind.ERROR))
public static void onError(@ProbeClassName String clazz,
@ProbeMethodName String method,
Throwable t) {
println("Exception in " + clazz + "." + method + ": " + str(t));
jstack();
}
@OnMethod(clazz = "com.example.State", method = "/.*/", location = @Location(Kind.FIELD_SET, clazz = "/.*/", field = "status"))
public static void onFieldSet(@TargetInstance Object target,
@TargetMethodOrField String field,
Object newValue) {
println("Field " + field + " set to: " + str(newValue));
}
@OnMethod(clazz = "java.sql.Statement", method = "execute.*")
public static void onSqlExecute(@Self Object stmt, String sql) {
println("SQL: " + sql);
}
@OnMethod(clazz = "+javax.servlet.http.HttpServlet", method = "service")
public static void onRequest(@Self Object servlet,
javax.servlet.http.HttpServletRequest req) {
println(str(req.getMethod()) + " " + str(req.getRequestURI()));
}
private static long objectCount;
@OnMethod(clazz = "com.example.HeavyObject", method = "<init>")
public static void onNew() {
objectCount++;
}
@OnTimer(5000)
public static void printCount() {
println("Objects created: " + str(objectCount));
}
private static Aggregation distrib = Aggregations.newAggregation(AggregationFunction.QUANTIZE);
@OnMethod(clazz = "com.example.Service", method = "process", location = @Location(Kind.RETURN))
public static void onReturn(@Duration long duration) {
Aggregations.addToAggregation(distrib, duration / 1000000); // Convert to ms
}
@OnTimer(10000)
public static void printDistribution() {
Aggregations.printAggregation("Duration distribution (ms)", distrib);
}
import org.openjdk.btrace.core.jfr.JfrEvent;
@BTrace
public class JfrMethodTrace {
@Event(
name = "MethodExecution",
label = "Method Execution Event",
category = {"myapp"},
fields = {
@Event.Field(type = Event.FieldType.STRING, name = "className"),
@Event.Field(type = Event.FieldType.STRING, name = "methodName"),
@Event.Field(type = Event.FieldType.LONG, name = "duration",
kind = @Event.Field.Kind(name = Event.FieldKind.TIMESPAN))
}
)
private static JfrEvent.Factory execEventFactory;
@TLS private static long startTime;
@OnMethod(clazz = "com.example.Service", method = "/.*/")
public static void onEntry() {
startTime = timeNanos();
}
@OnMethod(clazz = "com.example.Service", method = "/.*/",
location = @Location(Kind.RETURN))
public static void onReturn(@ProbeClassName String clazz,
@ProbeMethodName String method) {
JfrEvent event = Jfr.prepareEvent(execEventFactory);
if (Jfr.shouldCommit(event)) {
Jfr.setEventField(event, "className", clazz);
Jfr.setEventField(event, "methodName", method);
Jfr.setEventField(event, "duration", timeNanos() - startTime);
Jfr.commit(event);
}
}
}
Benefits: <1% overhead, native JVM recording, offline analysis with Mission Control.
Attach to running JVM and trace.
btrace [options] <PID> <script.java> [script-args]
Common Options:
--version - Show the version-v - Run in verbose mode-l - List all locally attachable JVMs-lp - List active probes in the given JVM (expects PID or app name)-r <probe-id> - Reconnect to an active disconnected probe-r help - Show help on remote commands-o <file> - Output to file (disables console output)-u - Run in trusted/unsafe mode-d <path> - Dump instrumented classes to specified path-pd <path> - Search path for probe XML descriptors-cp <path> / -classpath <path> - User class files and annotation processors path-I <path> - Include files path-p <port> - Port for btrace agent listener (default: 2020)-host <host> - Remote host (default: localhost)-statsd <host:port> - StatSD server configuration-x - Unattended mode (non-interactive)Examples:
# List all Java processes
btrace -l
# Basic attach
btrace 12345 MyTrace.java
# Verbose with output file
btrace -v -o trace.log 12345 MyTrace.java
# List active probes in a JVM
btrace -lp 12345
# Reconnect to an active probe
btrace -r myprobe-id 12345
# With script arguments
btrace 12345 MyTrace.java arg1 arg2
# Dump instrumented classes for debugging
btrace -d /tmp/instrumented 12345 MyTrace.java
# With StatSD integration
btrace -statsd localhost:8125 12345 MyTrace.java
Compile BTrace script.
btracec <script.java>
Examples:
# Compile script
btracec MyTrace.java
# Results in MyTrace.class
Launch Java app with BTrace agent.
btracer <script.class> <java-app-and-args>
Examples:
# First compile
btracec MyTrace.java
# Then launch
btracer MyTrace.class java -jar myapp.jar
# With JVM options
btracer MyTrace.class java -Xmx2g -jar myapp.jar
Start app with BTrace agent directly.
java -javaagent:/path/to/btrace-agent.jar=script=<script.class>[,arg=value]... YourApp
Agent Parameters:
script=<path> - BTrace script class filescriptdir=<dir> - Directory to load scripts fromport=<port> - Communication portnoServer=true - Don't start command serverbootClassPath=<path> - Additional boot classpathExamples:
# Basic agent mode
java -javaagent:btrace-agent.jar=script=MyTrace.class MyApp
# With custom port
java -javaagent:btrace-agent.jar=script=MyTrace.class,port=2020 MyApp
All functions from org.openjdk.btrace.core.BTraceUtils.
println(String) // Print line
print(String) // Print without newline
printArray(Object[]) // Print array
printMap(Map) // Print map
printf(String, ...) // Formatted print
str(Object) // Object to string
name(Class) // Class name
probeClass() // Get current probe class
classOf(Object) // Get object's class
field(Class, String) // Get field value
newAggregation(AggregationFunction) // Create aggregation
addToAggregation(Aggregation, long) // Add value
printAggregation(String, Aggregation) // Print distribution
timeMillis() // Current time in milliseconds
timeNanos() // Current time in nanoseconds
timestamp() // Formatted timestamp
jstack() // Print stack trace
jstack(int) // Print N frames
jstacks() // Print all thread stacks
threadId() // Current thread ID
threadName() // Current thread name
Collections.newHashMap() // Create HashMap
Collections.newArrayList() // Create ArrayList
Collections.put(Map, K, V) // Put in map
Collections.get(Map, K) // Get from map
Collections.size(Collection) // Get size
Atomic.newAtomicInteger(int) // Create AtomicInteger
Atomic.incrementAndGet(AtomicInteger) // Increment and get
Atomic.get(AtomicInteger) // Get value
exit(int) // Exit BTrace
sys(String) // System property
getenv(String) // Environment variable
getInstrumentationLevel() // Current level
sizeof(Object) // Object size
heapUsage() // Heap memory usage
nonHeapUsage() // Non-heap memory usage
// Create event from factory
Jfr.prepareEvent(JfrEvent.Factory) // Create new JFR event instance
// Set event field values (overloaded for all types)
Jfr.setEventField(JfrEvent, String, byte|boolean|char|short|int|float|long|double|String)
// Event lifecycle
Jfr.shouldCommit(JfrEvent) // Check if event should be recorded
Jfr.commit(JfrEvent) // Commit event to JFR
Jfr.begin(JfrEvent) // Start timing for timespan events
Jfr.end(JfrEvent) // End timing for timespan events
Note: JFR functions require OpenJDK 8 (with backported JFR) or Java 11+. Not available in Java 9-10 (graceful degradation).
BTrace scripts have safety restrictions:
Use -u (unsafe mode) to bypass restrictions, but only in controlled environments.