Skip to content

Commit 4663784

Browse files
Support include/exclude flag for JFR to heatmap conversion (async-profiler#1633)
1 parent d2172a6 commit 4663784

File tree

3 files changed

+93
-7
lines changed

3 files changed

+93
-7
lines changed

src/converter/Main.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ private static void usage() {
104104
"\n" +
105105
"Conversion options:\n" +
106106
" -o --output FORMAT Output format: html, collapsed, pprof, pb.gz, heatmap, otlp\n" +
107+
" -I --include REGEX Include only stacks with the specified frames\n" +
108+
" -X --exclude REGEX Exclude stacks with the specified frames\n" +
107109
"\n" +
108110
"JFR options:\n" +
109111
" --cpu CPU profile (ExecutionSample)\n" +
@@ -138,8 +140,6 @@ private static void usage() {
138140
" -r --reverse Reverse stack traces (defaults to icicle graph)\n" +
139141
" -i --inverted Toggles the layout for reversed stacktraces from icicle to flamegraph\n" +
140142
" and for default stacktraces from flamegraph to icicle\n" +
141-
" -I --include REGEX Include only stacks with the specified frames\n" +
142-
" -X --exclude REGEX Exclude stacks with the specified frames\n" +
143143
" --highlight REGEX Highlight frames matching the given pattern\n");
144144
}
145145
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright The async-profiler authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package one.convert;
7+
8+
import java.util.ArrayList;
9+
10+
public class BidirectionalIndex<T> extends Index<T> {
11+
private final ArrayList<T> reverseIndex;
12+
13+
public BidirectionalIndex(Class<T> cls, T empty) {
14+
this(cls, empty, 256);
15+
}
16+
17+
public BidirectionalIndex(Class<T> cls, T empty, int initialCapacity) {
18+
super(cls, empty, initialCapacity);
19+
this.reverseIndex = new ArrayList<>(initialCapacity);
20+
this.reverseIndex.add(empty);
21+
}
22+
23+
@Override
24+
public int index(T key) {
25+
assert super.size() == reverseIndex.size();
26+
int idx = super.index(key);
27+
if (idx < reverseIndex.size()) {
28+
// Key already exists
29+
return idx;
30+
}
31+
assert idx == reverseIndex.size();
32+
reverseIndex.add(key);
33+
return idx;
34+
}
35+
36+
public T getKey(int idx) {
37+
return reverseIndex.get(idx);
38+
}
39+
}

src/converter/one/heatmap/Heatmap.java

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.io.IOException;
99
import java.io.PrintStream;
1010
import java.util.*;
11+
import java.util.regex.Pattern;
1112

1213
import one.convert.*;
1314
import one.jfr.DictionaryInt;
@@ -37,12 +38,14 @@ public void addStack(long id, long[] methods, int[] locations, byte[] types, int
3738

3839
public void beforeChunk() {
3940
state.methodCache.clear();
41+
state.includeCache.clear();
4042
}
4143

4244
public void finish(long startMs) {
4345
this.startMs = startMs;
4446
state.methodCache.clear();
4547
state.stackTracesCache.clear();
48+
state.includeCache.clear();
4649
}
4750

4851
private EvaluationContext evaluate() {
@@ -394,8 +397,11 @@ private static class State {
394397
// Maps stack trace ID to prototype ID in stackTracesRemap
395398
final DictionaryInt stackTracesCache = new DictionaryInt();
396399
final Map<MethodKey, Integer> methodCache = new HashMap<>();
397-
final Index<Method> methods = new Index<>(Method.class, Method.EMPTY);
398-
final Index<String> symbolTable = new Index<>(String.class, "");
400+
final BidirectionalIndex<Method> methods = new BidirectionalIndex<>(Method.class, Method.EMPTY);
401+
final BidirectionalIndex<String> symbolTable = new BidirectionalIndex<>(String.class, "");
402+
403+
// Cache for exclude/include filter results per prototype ID
404+
final Map<Integer, Boolean> includeCache = new HashMap<>();
399405

400406
// reusable array to (temporary) store (potentially) new stack trace
401407
int[] cachedStackTrace = new int[4096];
@@ -406,14 +412,52 @@ private static class State {
406412
this.sampleList = new SampleList(blockDurationMs);
407413
}
408414

415+
private String resolveFrameName(Method method) {
416+
if (method.className == 0) {
417+
return symbolTable.getKey(method.methodName);
418+
}
419+
if (method.methodName == 0) {
420+
return symbolTable.getKey(method.className);
421+
}
422+
return symbolTable.getKey(method.className) + '.' + symbolTable.getKey(method.methodName);
423+
}
424+
425+
private boolean includeStack(int prototypeId) {
426+
if (args.include == null && args.exclude == null) {
427+
return true;
428+
}
429+
return includeCache.computeIfAbsent(prototypeId, stackId -> applyIncludeExcludeFilter(stackId));
430+
}
431+
432+
// Returns true if the stack should be included
433+
private boolean applyIncludeExcludeFilter(int stackId) {
434+
int[] stack = stackTracesRemap.get(stackId);
435+
Pattern include = args.include;
436+
Pattern exclude = args.exclude;
437+
for (int i = 0; i < stack.length; i++) {
438+
Method method = methods.getKey(stack[i]);
439+
String name = resolveFrameName(method);
440+
if (exclude != null && exclude.matcher(name).matches()) {
441+
return false;
442+
}
443+
if (include != null && include.matcher(name).matches()) {
444+
if (exclude == null) return true;
445+
include = null;
446+
}
447+
}
448+
return include == null;
449+
}
450+
409451
public void addEvent(int stackTraceId, int threadId, int classId, byte type, long timeMs) {
410-
if (sampleList.getRecordsCount() >= LIMIT) {
452+
if (sampleList.getRecordsCount() >= LIMIT || stackTraceId == 0) {
411453
return;
412454
}
413455

414456
int prototypeId = stackTracesCache.get(stackTraceId);
415457
if (classId == 0 && !args.threads) {
416-
sampleList.add(prototypeId, timeMs);
458+
if (includeStack(prototypeId)) {
459+
sampleList.add(prototypeId, timeMs);
460+
}
417461
return;
418462
}
419463

@@ -435,7 +479,10 @@ public void addEvent(int stackTraceId, int threadId, int classId, byte type, lon
435479
cachedStackTrace[stackSize - 1] = getMethodIndex(key);
436480
}
437481

438-
sampleList.add(stackTracesRemap.index(cachedStackTrace, stackSize), timeMs);
482+
int newStackId = stackTracesRemap.index(cachedStackTrace, stackSize);
483+
if (includeStack(newStackId)) {
484+
sampleList.add(newStackId, timeMs);
485+
}
439486
}
440487

441488
public void addStack(long id, long[] methods, int[] locations, byte[] types, int size) {

0 commit comments

Comments
 (0)