@@ -20,6 +20,7 @@ public class FlameGraph implements Comparator<Frame> {
2020 private static final String [] FRAME_SUFFIX = {"_[0]" , "_[j]" , "_[i]" , "" , "" , "_[k]" , "_[1]" };
2121 private static final byte HAS_SUFFIX = (byte ) 0x80 ;
2222 private static final int FLUSH_THRESHOLD = 15000 ;
23+ private static final long NEW_FRAME_DIFF = Long .MIN_VALUE ;
2324 private static final Pattern TID_FRAME_PATTERN = Pattern .compile ("\\ [(.* )?tid=\\ d+]" );
2425
2526 private final Arguments args ;
@@ -29,11 +30,14 @@ public class FlameGraph implements Comparator<Frame> {
2930
3031 private String title = "Flame Graph" ;
3132 private int [] order ;
33+ private int [] cpoolMap ;
3234 private int depth ;
3335 private int lastLevel ;
3436 private long lastX ;
3537 private long lastTotal ;
38+ private long lastDiff ;
3639 private long mintotal ;
40+ private long maxdiff = -1 ;
3741
3842 public FlameGraph (Arguments args ) {
3943 this .args = args ;
@@ -90,6 +94,8 @@ public void parseHtml(Reader in) throws IOException {
9094 while (!br .readLine ().isEmpty ()) ;
9195
9296 for (String line ; !(line = br .readLine ()).isEmpty (); ) {
97+ if (line .startsWith ("d=" )) continue ; // artifact of a differential flame graph
98+
9399 StringTokenizer st = new StringTokenizer (line .substring (2 , line .length () - 1 ), "," );
94100 int nameAndType = Integer .parseInt (st .nextToken ());
95101
@@ -109,12 +115,10 @@ public void parseHtml(Reader in) throws IOException {
109115
110116 int titleIndex = nameAndType >>> 3 ;
111117 byte type = (byte ) (nameAndType & 7 );
112- if (st .hasMoreTokens () && (type <= TYPE_INLINED || type >= TYPE_C1_COMPILED )) {
113- type = TYPE_JIT_COMPILED ;
114- }
118+ byte normalizedType = type <= TYPE_INLINED || type >= TYPE_C1_COMPILED ? TYPE_JIT_COMPILED : type ;
115119
116- Frame f = level > 0 || needRebuild ? new Frame (titleIndex , type ) : root ;
117- f . self = f . total = total ;
120+ Frame f = level > 0 || needRebuild ? new Frame (titleIndex , normalizedType ) : root ;
121+ fillFrameCounters ( f , type , total ) ;
118122 if (st .hasMoreTokens ()) f .inlined = Long .parseLong (st .nextToken ());
119123 if (st .hasMoreTokens ()) f .c1 = Long .parseLong (st .nextToken ());
120124 if (st .hasMoreTokens ()) f .interpreted = Long .parseLong (st .nextToken ());
@@ -177,6 +181,26 @@ public void addSample(CallStack stack, long ticks) {
177181 depth = Math .max (depth , stack .size );
178182 }
179183
184+ public void diff (FlameGraph base ) {
185+ // Build a map that translates this cpool keys to the base flamegraph's cpool keys
186+ cpoolMap = Arrays .stream (cpool .keys ()).mapToInt (title -> base .cpool .getOrDefault (title , -1 )).toArray ();
187+ diff (base .root , root );
188+ }
189+
190+ private void diff (Frame base , Frame current ) {
191+ current .diff = base == null ? NEW_FRAME_DIFF : current .self - base .self ;
192+ maxdiff = Math .max (maxdiff , Math .abs (current .diff ));
193+
194+ for (Frame child : current .values ()) {
195+ Frame baseChild = base == null ? null : base .get (translateKey (child .key ));
196+ diff (baseChild , child );
197+ }
198+ }
199+
200+ private int translateKey (int key ) {
201+ return cpoolMap [key & TITLE_MASK ] | (key & ~TITLE_MASK );
202+ }
203+
180204 public void dump (OutputStream out ) throws IOException {
181205 try (PrintStream ps = new PrintStream (out , false , "UTF-8" )) {
182206 dump (ps );
@@ -205,6 +229,9 @@ public void dump(PrintStream out) {
205229 tail = printTill (out , tail , "/*inverted:*/false" );
206230 out .print (args .reverse ^ args .inverted );
207231
232+ tail = printTill (out , tail , "/*maxdiff:*/-1" );
233+ out .print (maxdiff );
234+
208235 tail = printTill (out , tail , "/*depth:*/0" );
209236 out .print (depth );
210237
@@ -239,6 +266,15 @@ private void printCpool(PrintStream out) {
239266 }
240267
241268 private void printFrame (PrintStream out , Frame frame , int level , long x ) {
269+ StringBuilder sb = outbuf ;
270+ if (frame .diff != lastDiff ) {
271+ if (frame .diff == NEW_FRAME_DIFF ) {
272+ sb .append ("d=U\n " );
273+ } else {
274+ sb .append ("d=" ).append (frame .diff ).append ('\n' );
275+ }
276+ }
277+
242278 int nameAndType = order [frame .getTitleIndex ()] << 3 | frame .getType ();
243279 boolean hasExtraTypes = (frame .inlined | frame .c1 | frame .interpreted ) != 0 &&
244280 frame .inlined < frame .total && frame .interpreted < frame .total ;
@@ -250,7 +286,7 @@ private void printFrame(PrintStream out, Frame frame, int level, long x) {
250286 func = 'n' ;
251287 }
252288
253- StringBuilder sb = outbuf .append (func ).append ('(' ).append (nameAndType );
289+ sb .append (func ).append ('(' ).append (nameAndType );
254290 if (func == 'f' ) {
255291 sb .append (',' ).append (level ).append (',' ).append (x - lastX );
256292 }
@@ -270,6 +306,7 @@ private void printFrame(PrintStream out, Frame frame, int level, long x) {
270306 lastLevel = level ;
271307 lastX = x ;
272308 lastTotal = frame .total ;
309+ lastDiff = frame .diff ;
273310
274311 Frame [] children = frame .values ().toArray (EMPTY_FRAME_ARRAY );
275312 Arrays .sort (children , this );
@@ -291,6 +328,9 @@ private void printFrameCollapsed(PrintStream out, Frame frame, String[] strings)
291328 sb .append (strings [frame .getTitleIndex ()]).append (FRAME_SUFFIX [frame .getType ()]);
292329 if (frame .self > 0 ) {
293330 int tmpLength = sb .length ();
331+ if (maxdiff >= 0 ) {
332+ sb .append (' ' ).append (frame .diff == NEW_FRAME_DIFF ? 0 : frame .self - frame .diff );
333+ }
294334 out .print (sb .append (' ' ).append (frame .self ).append ('\n' ));
295335 sb .setLength (tmpLength );
296336 }
@@ -328,6 +368,21 @@ private boolean excludeStack(CallStack stack) {
328368 return include != null ;
329369 }
330370
371+ private static void fillFrameCounters (Frame frame , byte type , long ticks ) {
372+ frame .self = frame .total = ticks ;
373+ switch (type ) {
374+ case TYPE_INTERPRETED :
375+ frame .interpreted = ticks ;
376+ break ;
377+ case TYPE_INLINED :
378+ frame .inlined = ticks ;
379+ break ;
380+ case TYPE_C1_COMPILED :
381+ frame .c1 = ticks ;
382+ break ;
383+ }
384+ }
385+
331386 private Frame addChild (Frame frame , String title , byte type , long ticks ) {
332387 frame .total += ticks ;
333388
0 commit comments