@@ -19,6 +19,10 @@ let getClassAndMethodName = function(fqdn) {
19
19
return tokens . slice ( tokens . length - 2 ) . join ( "." ) ;
20
20
} ;
21
21
22
+ // Return a vector (0.0 -> 1.0) that is a hash of the input string.
23
+ // The hash is computed to favor early characters over later ones, so
24
+ // that strings with similar starts have similar vectors. Only the first
25
+ // 6 characters are considered.
22
26
let hash = function ( name ) {
23
27
let i , j , maxHash , mod , ref , ref1 , result , weight ;
24
28
ref = [ 0 , 0 , 1 , 10 ] , result = ref [ 0 ] , maxHash = ref [ 1 ] , weight = ref [ 2 ] , mod = ref [ 3 ] ;
@@ -36,10 +40,13 @@ let hash = function(name) {
36
40
} ;
37
41
38
42
const FlameGraphUtils = {
43
+ // augments each node in the tree with the maximum distance
44
+ // it is from a terminal node, the list of parents linking
45
+ // it to the root and filler nodes that balance the representation
39
46
augment ( node , location ) {
40
- let childSum ;
41
- let children ;
42
- children = node . children ;
47
+ let children = node . children ;
48
+ // d3.partition adds the reverse (depth), here we store the distance
49
+ // between a node and its furthest leaf
43
50
if ( node . augmented ) {
44
51
return node ;
45
52
}
@@ -51,7 +58,7 @@ const FlameGraphUtils = {
51
58
node . augmented = true ;
52
59
return node ;
53
60
}
54
- childSum = children . reduce ( ( function ( sum , child ) {
61
+ let childSum = children . reduce ( ( function ( sum , child ) {
55
62
return sum + child . value ;
56
63
} ) , 0 ) ;
57
64
if ( childSum < node . value ) {
@@ -72,41 +79,36 @@ const FlameGraphUtils = {
72
79
. sum ( d => d . data ? d . data . value : d . value )
73
80
. sort ( ( a , b ) => {
74
81
if ( a . filler ) {
75
- return 1 ;
82
+ return 1 ; // move fillers to the right
76
83
}
77
84
if ( b . filler ) {
78
- return - 1 ;
85
+ return - 1 ; // move fillers to the right
79
86
}
80
87
return a . data . name . localeCompare ( b . data . name ) ;
81
88
} ) ;
82
89
return d3partition ( root ) . descendants ( ) ;
83
90
} ,
84
91
hide ( nodes , unhide ) {
85
- let process ;
86
- let processChildren ;
87
- let processParents ;
88
- let remove ;
89
- let sum ;
90
92
if ( unhide === null ) {
91
93
unhide = false ;
92
94
}
93
- sum = arr => arr . reduce ( ( ( acc , val ) => acc + val ) , 0 ) ;
94
- remove = ( arr , val ) => {
95
- let pos ;
96
- pos = arr . indexOf ( val ) ;
95
+ let sum = arr => arr . reduce ( ( ( acc , val ) => acc + val ) , 0 ) ;
96
+ let remove = ( arr , val ) => {
97
+ // we need to remove precisely one occurrence of initial value
98
+ let pos = arr . indexOf ( val ) ;
97
99
if ( pos >= 0 ) {
98
100
return arr . splice ( pos , 1 ) ;
99
101
}
100
102
} ;
101
- process = ( node , val ) => {
103
+ let process = ( node , val ) => {
102
104
if ( unhide ) {
103
105
remove ( node . hidden , val ) ;
104
106
} else {
105
107
node . hidden . push ( val ) ;
106
108
}
107
109
return node . value = Math . max ( node . originalValue - sum ( node . hidden ) , 0 ) ;
108
110
} ;
109
- processChildren = ( node , val ) => {
111
+ let processChildren = ( node , val ) => {
110
112
if ( ! node . children ) {
111
113
return ;
112
114
}
@@ -115,18 +117,16 @@ const FlameGraphUtils = {
115
117
return processChildren ( child , val ) ;
116
118
} ) ;
117
119
} ;
118
- processParents = ( node , val ) => {
119
- let results ;
120
- results = [ ] ;
120
+ let processParents = ( node , val ) => {
121
+ let results = [ ] ;
121
122
while ( node . parent ) {
122
123
process ( node . parent , val ) ;
123
124
results . push ( node = node . parent ) ;
124
125
}
125
126
return results ;
126
127
} ;
127
128
return nodes . forEach ( node => {
128
- let val ;
129
- val = node . originalValue ;
129
+ let val = node . originalValue ;
130
130
processParents ( node , val ) ;
131
131
process ( node , val ) ;
132
132
return processChildren ( node , val ) ;
@@ -141,6 +141,8 @@ class FlameGraph {
141
141
if ( debug == null ) {
142
142
debug = false ;
143
143
}
144
+
145
+ // enable logging only if explicitly specified
144
146
if ( debug ) {
145
147
this . console = window . console ;
146
148
} else {
@@ -150,6 +152,8 @@ class FlameGraph {
150
152
timeEnd ( ) { }
151
153
} ;
152
154
}
155
+
156
+ // defaults
153
157
this . _size = [ 1200 , 800 ] ;
154
158
this . _cellHeight = 20 ;
155
159
this . _margin = {
@@ -171,6 +175,8 @@ class FlameGraph {
171
175
if ( this . _tooltipEnabled && d3Tip ) {
172
176
this . _tooltipPlugin = d3Tip ( ) ;
173
177
}
178
+
179
+ // initial processing of data
174
180
this . console . time ( 'augment' ) ;
175
181
this . original = FlameGraphUtils . augment ( root , '0' ) ;
176
182
this . console . timeEnd ( 'augment' ) ;
@@ -256,45 +262,42 @@ class FlameGraph {
256
262
if ( onlyVisible ) {
257
263
return this . container . selectAll ( '.node' ) . filter ( predicate ) ;
258
264
} else {
265
+ // re-partition the data prior to rendering
259
266
result = FlameGraphUtils . partition ( this . original ) . filter ( predicate ) ;
260
267
return result ;
261
268
}
262
269
}
263
270
264
271
render ( ) {
265
- let data ;
266
- let existingContainers ;
267
- let maxLevels ;
268
- let newContainers ;
269
- let ref ;
270
- let renderNode ;
271
- let visibleCells ;
272
272
if ( ! this . _selector ) {
273
273
throw new Error ( "No DOM element provided" ) ;
274
274
}
275
275
this . console . time ( 'render' ) ;
276
276
if ( ! this . container ) {
277
277
this . _createContainer ( ) ;
278
278
}
279
+
280
+ // reset size and scales
279
281
this . fontSize = ( this . cellHeight ( ) / 10 ) * 0.4 ;
280
282
281
283
this . x = scaleLinear ( ) . domain ( [ 0 , max ( this . _data , d => d . x1 ) ] ) . range ( [ 0 , this . width ( ) ] ) ;
282
284
283
- visibleCells = Math . floor ( this . height ( ) / this . cellHeight ( ) ) ;
284
- maxLevels = this . _root . level ;
285
+ let visibleCells = Math . floor ( this . height ( ) / this . cellHeight ( ) ) ;
286
+ let maxLevels = this . _root . level ;
285
287
286
288
this . y = scaleQuantize ( ) . domain ( [ min ( this . _data , d => d . y0 ) , max ( this . _data , d => d . y0 ) ] ) . range ( range ( maxLevels ) . map ( ( function ( _this ) {
287
289
return function ( cell ) {
288
290
return ( visibleCells - 1 - cell - _this . _ancestors . length ) * _this . cellHeight ( ) ;
289
291
} ;
290
292
} ) ( this ) ) ) ;
291
293
292
- data = this . _data . filter ( ( function ( _this ) {
294
+ // JOIN
295
+ let data = this . _data . filter ( ( function ( _this ) {
293
296
return function ( d ) {
294
297
return _this . x ( d . x1 - d . x0 ) > 0.4 && _this . y ( d . y0 ) >= 0 && ! d . data . filler ;
295
298
} ;
296
299
} ) ( this ) ) ;
297
- renderNode = {
300
+ let renderNode = {
298
301
x : ( function ( _this ) {
299
302
return function ( d ) {
300
303
return _this . x ( d . x0 ) ;
@@ -324,10 +327,16 @@ class FlameGraph {
324
327
} ;
325
328
} ) ( this )
326
329
} ;
327
- existingContainers = this . container . selectAll ( '.node' ) . data ( data , d => d . data . location ) . attr ( 'class' , 'node' ) ;
330
+ let existingContainers = this . container . selectAll ( '.node' ) . data ( data , d => d . data . location ) . attr ( 'class' , 'node' ) ;
331
+
332
+ // UPDATE
328
333
this . _renderNodes ( existingContainers , renderNode , false , data ) ;
329
- newContainers = existingContainers . enter ( ) . append ( 'g' ) . attr ( 'class' , 'node' ) ;
334
+
335
+ // ENTER
336
+ let newContainers = existingContainers . enter ( ) . append ( 'g' ) . attr ( 'class' , 'node' ) ;
330
337
this . _renderNodes ( newContainers , renderNode , true , data ) ;
338
+
339
+ // EXIT
331
340
existingContainers . exit ( ) . remove ( ) ;
332
341
if ( this . zoomEnabled ( ) ) {
333
342
this . _renderAncestors ( ) . _enableNavigation ( ) ;
@@ -341,13 +350,23 @@ class FlameGraph {
341
350
}
342
351
343
352
_createContainer ( ) {
344
- let offset ;
345
- let svg ;
353
+ // remove any previously existing svg
346
354
select ( this . _selector ) . select ( 'svg' ) . remove ( ) ;
347
- svg = select ( this . _selector ) . append ( 'svg' ) . attr ( 'class' , 'flame-graph' ) . attr ( 'width' , this . _size [ 0 ] ) . attr ( 'height' , this . _size [ 1 ] ) ;
348
- offset = `translate(${ this . margin ( ) . left } , ${ this . margin ( ) . top } )` ;
355
+ // create main svg container
356
+ let svg = select ( this . _selector ) . append ( 'svg' ) . attr ( 'class' , 'flame-graph' ) . attr ( 'width' , this . _size [ 0 ] ) . attr ( 'height' , this . _size [ 1 ] ) ;
357
+ // we set an offset based on the margin
358
+ let offset = `translate(${ this . margin ( ) . left } , ${ this . margin ( ) . top } )` ;
359
+ // this.container will hold all our nodes
349
360
this . container = svg . append ( 'g' ) . attr ( 'transform' , offset ) ;
350
- return svg . append ( 'rect' ) . attr ( 'width' , this . _size [ 0 ] - ( this . _margin . left + this . _margin . right ) ) . attr ( 'height' , this . _size [ 1 ] - ( this . _margin . top + this . _margin . bottom ) ) . attr ( 'transform' , offset ) . attr ( 'class' , 'border-rect' ) ;
361
+
362
+ // this rectangle draws the border around the flame graph
363
+ // has to be appended after the container so that the border is visible
364
+ // we also need to apply the same translation
365
+ return svg . append ( 'rect' )
366
+ . attr ( 'width' , this . _size [ 0 ] - ( this . _margin . left + this . _margin . right ) )
367
+ . attr ( 'height' , this . _size [ 1 ] - ( this . _margin . top + this . _margin . bottom ) )
368
+ . attr ( 'transform' , offset )
369
+ . attr ( 'class' , 'border-rect' ) ;
351
370
}
352
371
353
372
_renderNodes ( containers , attrs , enter , data ) {
@@ -403,12 +422,9 @@ class FlameGraph {
403
422
}
404
423
return 's' ;
405
424
} ) ) ( this ) ) . offset ( ( ( _this => d => {
406
- let x ;
407
- let xOffset ;
408
- let yOffset ;
409
- x = _this . x ( d . x0 ) + _this . x ( d . x1 - d . x0 ) / 2 ;
410
- xOffset = Math . max ( Math . ceil ( _this . x ( d . x1 - d . x0 ) / 2 ) , 5 ) ;
411
- yOffset = Math . ceil ( _this . cellHeight ( ) / 2 ) ;
425
+ let x = _this . x ( d . x0 ) + _this . x ( d . x1 - d . x0 ) / 2 ;
426
+ let xOffset = Math . max ( Math . ceil ( _this . x ( d . x1 - d . x0 ) / 2 ) , 5 ) ;
427
+ let yOffset = Math . ceil ( _this . cellHeight ( ) / 2 ) ;
412
428
if ( _this . width ( ) - 100 < x ) {
413
429
return [ 0 , - xOffset ] ;
414
430
}
@@ -431,33 +447,32 @@ class FlameGraph {
431
447
}
432
448
433
449
_renderAncestors ( ) {
434
- let ancestor ;
435
- let ancestorData ;
436
- let ancestors ;
437
- let idx ;
450
+ let i ;
438
451
let j ;
452
+ let idx ;
439
453
let len ;
440
- let newAncestors ;
441
- let prev ;
442
- let renderAncestor ;
454
+ let ancestor ;
455
+ let ancestors ;
443
456
if ( ! this . _ancestors . length ) {
444
457
ancestors = this . container . selectAll ( '.ancestor' ) . remove ( ) ;
445
458
return this ;
446
459
}
447
- ancestorData = this . _ancestors . map ( ( ancestor , idx ) => ( {
460
+ let ancestorData = this . _ancestors . map ( ( ancestor , idx ) => ( {
448
461
name : ancestor . name ,
449
462
value : idx + 1 ,
450
463
location : ancestor . location ,
451
464
isAncestor : true
452
465
} ) ) ;
453
466
for ( idx = j = 0 , len = ancestorData . length ; j < len ; idx = ++ j ) {
454
467
ancestor = ancestorData [ idx ] ;
455
- prev = ancestorData [ idx - 1 ] ;
468
+ let prev = ancestorData [ idx - 1 ] ;
456
469
if ( prev ) {
457
470
prev . children = [ ancestor ] ;
458
471
}
459
472
}
460
- renderAncestor = {
473
+
474
+ // FIXME: this is pretty ugly, but we need to add links between ancestors
475
+ let renderAncestor = {
461
476
x : ( function ( _this ) {
462
477
return function ( d ) {
463
478
return 0 ;
@@ -477,20 +492,23 @@ class FlameGraph {
477
492
} ) ( this )
478
493
} ;
479
494
495
+ // JOIN
480
496
ancestors = this . container . selectAll ( '.ancestor' ) . data (
481
497
FlameGraphUtils . partition ( ancestorData [ 0 ] ) , d => d . location
482
498
) ;
483
499
500
+ // UPDATE
484
501
this . _renderNodes ( ancestors , renderAncestor , false , ancestorData ) ;
485
- newAncestors = ancestors . enter ( ) . append ( 'g' ) . attr ( 'class' , 'ancestor' ) ;
502
+ // ENTER
503
+ let newAncestors = ancestors . enter ( ) . append ( 'g' ) . attr ( 'class' , 'ancestor' ) ;
486
504
this . _renderNodes ( newAncestors , renderAncestor , true , ancestorData ) ;
505
+ // EXIT
487
506
ancestors . exit ( ) . remove ( ) ;
488
507
return this ;
489
508
}
490
509
491
510
_enableNavigation ( ) {
492
- let clickable ;
493
- clickable = ( ( _this => d => {
511
+ let clickable = ( ( _this => d => {
494
512
let ref ;
495
513
return Math . round ( _this . width ( ) - _this . x ( d . x1 - d . x0 ) ) > 0 && ( ( ref = d . children ) != null ? ref . length : void 0 ) ;
496
514
} ) ) ( this ) ;
@@ -514,10 +532,8 @@ class FlameGraph {
514
532
515
533
_generateAccessors ( accessors ) {
516
534
let accessor ;
517
- let j ;
518
- let len ;
519
535
let results = [ ] ;
520
- for ( j = 0 , len = accessors . length ; j < len ; j ++ ) {
536
+ for ( let j = 0 , len = accessors . length ; j < len ; j ++ ) {
521
537
accessor = accessors [ j ] ;
522
538
results . push ( this [ accessor ] = ( ( accessor => function ( newValue ) {
523
539
if ( ! arguments . length ) {
0 commit comments