4
4
5
5
import { Protocol as Cdp } from 'devtools-protocol' ;
6
6
import { INode } from '../common/model' ;
7
+ import { IAnnotationLocation , IJsDebugAnnotations } from '../common/types' ;
8
+ import { getBestLocation } from '../getBestLocation' ;
7
9
import { ISourceLocation } from '../location-mapping' ;
8
10
import { maybeFileUrlToPath } from '../path' ;
9
11
@@ -18,24 +20,16 @@ export interface ITreeNode extends IHeapProfileNode {
18
20
parent ?: ITreeNode ;
19
21
}
20
22
21
- /**
22
- * Extra annotations added by js-debug.
23
- */
24
- export interface IJsDebugAnnotations {
25
- /**
26
- * Workspace root path, if set.
27
- */
28
- rootPath ?: string ;
29
- }
30
-
31
23
export interface IHeapProfileRaw extends Cdp . HeapProfiler . SamplingHeapProfile {
32
24
$vscode ?: IJsDebugAnnotations ;
25
+ head : IProfileModelNode ;
33
26
}
34
27
35
28
export interface IProfileModelNode
36
29
extends Omit < Cdp . HeapProfiler . SamplingHeapProfileNode , 'children' > {
37
30
src ?: ISourceLocation ;
38
31
children : IProfileModelNode [ ] ;
32
+ locationId ?: number ;
39
33
}
40
34
41
35
/**
@@ -48,25 +42,84 @@ export type IProfileModel = {
48
42
} ;
49
43
50
44
/**
51
- * Computes the model for the given profile.
45
+ * Ensures that all profile nodes have a location ID, setting them if they
46
+ * aren't provided by default.
52
47
*/
53
- export const buildModel = ( profile : IHeapProfileRaw ) : IProfileModel => {
54
- let nodes = [ profile . head ] ;
48
+ const ensureSourceLocations = ( profile : IHeapProfileRaw ) : ReadonlyArray < IAnnotationLocation > => {
49
+ if ( profile . $vscode ) {
50
+ return profile . $vscode . locations ; // profiles we generate are already good
51
+ }
55
52
56
- while ( nodes . length ) {
57
- const node = nodes . pop ( ) ;
53
+ let locationIdCounter = 0 ;
54
+ const locationsByRef = new Map <
55
+ string ,
56
+ { id : number ; callFrame : Cdp . Runtime . CallFrame ; location : ISourceLocation }
57
+ > ( ) ;
58
58
59
- if ( node ) {
60
- const { callFrame } = node ;
61
- ( node as unknown as IHeapProfileNode ) . src = {
59
+ const getLocationIdFor = ( callFrame : Cdp . Runtime . CallFrame ) => {
60
+ const ref = [
61
+ callFrame . functionName ,
62
+ callFrame . url ,
63
+ callFrame . scriptId ,
64
+ callFrame . lineNumber ,
65
+ callFrame . columnNumber ,
66
+ ] . join ( ':' ) ;
67
+
68
+ const existing = locationsByRef . get ( ref ) ;
69
+ if ( existing ) {
70
+ return existing . id ;
71
+ }
72
+ const id = locationIdCounter ++ ;
73
+ locationsByRef . set ( ref , {
74
+ id,
75
+ callFrame,
76
+ location : {
62
77
lineNumber : callFrame . lineNumber ,
63
78
columnNumber : callFrame . columnNumber ,
64
79
source : {
65
80
name : maybeFileUrlToPath ( callFrame . url ) ,
66
81
path : maybeFileUrlToPath ( callFrame . url ) ,
67
82
sourceReference : 0 ,
68
83
} ,
69
- } ;
84
+ } ,
85
+ } ) ;
86
+
87
+ return id ;
88
+ } ;
89
+
90
+ let nodes = [ profile . head ] ;
91
+
92
+ while ( nodes . length ) {
93
+ const node = nodes . pop ( ) ;
94
+
95
+ if ( node ) {
96
+ const { callFrame } = node ;
97
+ node . locationId = getLocationIdFor ( callFrame ) ;
98
+
99
+ nodes = nodes . concat ( node . children ) ;
100
+ }
101
+ }
102
+
103
+ return [ ...locationsByRef . values ( ) ]
104
+ . sort ( ( a , b ) => a . id - b . id )
105
+ . map ( l => ( { locations : [ l . location ] , callFrame : l . callFrame } ) ) ;
106
+ } ;
107
+
108
+ /**
109
+ * Computes the model for the given profile.
110
+ */
111
+ export const buildModel = ( profile : IHeapProfileRaw ) : IProfileModel => {
112
+ let nodes = [ profile . head ] ;
113
+
114
+ const sourceLocations = ensureSourceLocations ( profile ) ;
115
+
116
+ while ( nodes . length ) {
117
+ const node = nodes . pop ( ) ;
118
+
119
+ if ( node ) {
120
+ if ( node . locationId ) {
121
+ node . src = getBestLocation ( profile , sourceLocations [ node . locationId ] . locations ) ;
122
+ }
70
123
nodes = nodes . concat ( node . children ) ;
71
124
}
72
125
}
0 commit comments