@@ -108,6 +108,113 @@ func (r *runner) run(analyzers []*analysis.Analyzer, initialPackages []*packages
108
108
return extractDiagnostics (roots )
109
109
}
110
110
111
+ type actKey struct {
112
+ * analysis.Analyzer
113
+ * packages.Package
114
+ }
115
+
116
+ func (r * runner ) markAllActions (a * analysis.Analyzer , pkg * packages.Package , markedActions map [actKey ]struct {}) {
117
+ k := actKey {a , pkg }
118
+ if _ , ok := markedActions [k ]; ok {
119
+ return
120
+ }
121
+
122
+ for _ , req := range a .Requires {
123
+ r .markAllActions (req , pkg , markedActions )
124
+ }
125
+
126
+ if len (a .FactTypes ) != 0 {
127
+ for path := range pkg .Imports {
128
+ r .markAllActions (a , pkg .Imports [path ], markedActions )
129
+ }
130
+ }
131
+
132
+ markedActions [k ] = struct {}{}
133
+ }
134
+
135
+ func (r * runner ) makeAction (a * analysis.Analyzer , pkg * packages.Package ,
136
+ initialPkgs map [* packages.Package ]bool , actions map [actKey ]* action , actAlloc * actionAllocator ) * action {
137
+ k := actKey {a , pkg }
138
+ act , ok := actions [k ]
139
+ if ok {
140
+ return act
141
+ }
142
+
143
+ act = actAlloc .alloc ()
144
+ act .a = a
145
+ act .pkg = pkg
146
+ act .log = r .log
147
+ act .prefix = r .prefix
148
+ act .pkgCache = r .pkgCache
149
+ act .isInitialPkg = initialPkgs [pkg ]
150
+ act .needAnalyzeSource = initialPkgs [pkg ]
151
+ act .analysisDoneCh = make (chan struct {})
152
+
153
+ depsCount := len (a .Requires )
154
+ if len (a .FactTypes ) > 0 {
155
+ depsCount += len (pkg .Imports )
156
+ }
157
+ act .deps = make ([]* action , 0 , depsCount )
158
+
159
+ // Add a dependency on each required analyzers.
160
+ for _ , req := range a .Requires {
161
+ act .deps = append (act .deps , r .makeAction (req , pkg , initialPkgs , actions , actAlloc ))
162
+ }
163
+
164
+ r .buildActionFactDeps (act , a , pkg , initialPkgs , actions , actAlloc )
165
+
166
+ actions [k ] = act
167
+ return act
168
+ }
169
+
170
+ func (r * runner ) buildActionFactDeps (act * action , a * analysis.Analyzer , pkg * packages.Package ,
171
+ initialPkgs map [* packages.Package ]bool , actions map [actKey ]* action , actAlloc * actionAllocator ) {
172
+ // An analysis that consumes/produces facts
173
+ // must run on the package's dependencies too.
174
+ if len (a .FactTypes ) == 0 {
175
+ return
176
+ }
177
+
178
+ act .objectFacts = make (map [objectFactKey ]analysis.Fact )
179
+ act .packageFacts = make (map [packageFactKey ]analysis.Fact )
180
+
181
+ paths := make ([]string , 0 , len (pkg .Imports ))
182
+ for path := range pkg .Imports {
183
+ paths = append (paths , path )
184
+ }
185
+ sort .Strings (paths ) // for determinism
186
+ for _ , path := range paths {
187
+ dep := r .makeAction (a , pkg .Imports [path ], initialPkgs , actions , actAlloc )
188
+ act .deps = append (act .deps , dep )
189
+ }
190
+
191
+ // Need to register fact types for pkgcache proper gob encoding.
192
+ for _ , f := range a .FactTypes {
193
+ gob .Register (f )
194
+ }
195
+ }
196
+
197
+ type actionAllocator struct {
198
+ allocatedActions []action
199
+ nextFreeIndex int
200
+ }
201
+
202
+ func newActionAllocator (maxCount int ) * actionAllocator {
203
+ return & actionAllocator {
204
+ allocatedActions : make ([]action , maxCount ),
205
+ nextFreeIndex : 0 ,
206
+ }
207
+ }
208
+
209
+ func (actAlloc * actionAllocator ) alloc () * action {
210
+ if actAlloc .nextFreeIndex == len (actAlloc .allocatedActions ) {
211
+ panic (fmt .Sprintf ("Made too many allocations of actions: %d allowed" , len (actAlloc .allocatedActions )))
212
+ }
213
+ act := & actAlloc .allocatedActions [actAlloc .nextFreeIndex ]
214
+ actAlloc .nextFreeIndex ++
215
+ return act
216
+ }
217
+
111
218
//nolint:gocritic
112
219
func (r * runner ) prepareAnalysis (pkgs []* packages.Package ,
113
220
analyzers []* analysis.Analyzer ) (map [* packages.Package ]bool , []* action , []* action ) {
@@ -116,70 +223,30 @@ func (r *runner) prepareAnalysis(pkgs []*packages.Package,
116
223
// Each graph node (action) is one unit of analysis.
117
224
// Edges express package-to-package (vertical) dependencies,
118
225
// and analysis-to-analysis (horizontal) dependencies.
119
- type key struct {
120
- * analysis.Analyzer
121
- * packages.Package
122
- }
123
- actions := make (map [key ]* action )
124
226
125
- initialPkgs := map [* packages.Package ]bool {}
126
- for _ , pkg := range pkgs {
127
- initialPkgs [pkg ] = true
227
+ // This place is memory-intensive: e.g. Istio project has 120k total actions.
228
+ // Therefore optimize it carefully.
229
+ markedActions := make (map [actKey ]struct {}, len (analyzers )* len (pkgs ))
230
+ for _ , a := range analyzers {
231
+ for _ , pkg := range pkgs {
232
+ r .markAllActions (a , pkg , markedActions )
233
+ }
128
234
}
235
+ totalActionsCount := len (markedActions )
129
236
130
- var mkAction func (a * analysis.Analyzer , pkg * packages.Package ) * action
131
- mkAction = func (a * analysis.Analyzer , pkg * packages.Package ) * action {
132
- k := key {a , pkg }
133
- act , ok := actions [k ]
134
- if ! ok {
135
- act = & action {
136
- a : a ,
137
- pkg : pkg ,
138
- log : r .log ,
139
- prefix : r .prefix ,
140
- pkgCache : r .pkgCache ,
141
- isInitialPkg : initialPkgs [pkg ],
142
- needAnalyzeSource : initialPkgs [pkg ],
143
- analysisDoneCh : make (chan struct {}),
144
- objectFacts : make (map [objectFactKey ]analysis.Fact ),
145
- packageFacts : make (map [packageFactKey ]analysis.Fact ),
146
- loadMode : r .loadMode ,
147
- }
148
-
149
- // Add a dependency on each required analyzers.
150
- for _ , req := range a .Requires {
151
- act .deps = append (act .deps , mkAction (req , pkg ))
152
- }
237
+ actions := make (map [actKey ]* action , totalActionsCount )
238
+ actAlloc := newActionAllocator (totalActionsCount )
153
239
154
- // An analysis that consumes/produces facts
155
- // must run on the package's dependencies too.
156
- if len (a .FactTypes ) > 0 {
157
- paths := make ([]string , 0 , len (pkg .Imports ))
158
- for path := range pkg .Imports {
159
- paths = append (paths , path )
160
- }
161
- sort .Strings (paths ) // for determinism
162
- for _ , path := range paths {
163
- dep := mkAction (a , pkg .Imports [path ])
164
- act .deps = append (act .deps , dep )
165
- }
166
-
167
- // Need to register fact types for pkgcache proper gob encoding.
168
- for _ , f := range a .FactTypes {
169
- gob .Register (f )
170
- }
171
- }
172
-
173
- actions [k ] = act
174
- }
175
- return act
240
+ initialPkgs := make (map [* packages.Package ]bool , len (pkgs ))
241
+ for _ , pkg := range pkgs {
242
+ initialPkgs [pkg ] = true
176
243
}
177
244
178
245
// Build nodes for initial packages.
179
- var roots []* action
246
+ roots := make ( []* action , 0 , len ( pkgs ) * len ( analyzers ))
180
247
for _ , a := range analyzers {
181
248
for _ , pkg := range pkgs {
182
- root := mkAction (a , pkg )
249
+ root := r . makeAction (a , pkg , initialPkgs , actions , actAlloc )
183
250
root .isroot = true
184
251
roots = append (roots , root )
185
252
}
@@ -190,6 +257,8 @@ func (r *runner) prepareAnalysis(pkgs []*packages.Package,
190
257
allActions = append (allActions , act )
191
258
}
192
259
260
+ debugf ("Built %d actions" , len (actions ))
261
+
193
262
return initialPkgs , allActions , roots
194
263
}
195
264
@@ -334,9 +403,6 @@ type action struct {
334
403
a * analysis.Analyzer
335
404
pkg * packages.Package
336
405
pass * analysis.Pass
337
- isroot bool
338
- isInitialPkg bool
339
- needAnalyzeSource bool
340
406
deps []* action
341
407
objectFacts map [objectFactKey ]analysis.Fact
342
408
packageFacts map [packageFactKey ]analysis.Fact
@@ -349,7 +415,9 @@ type action struct {
349
415
analysisDoneCh chan struct {}
350
416
loadCachedFactsDone bool
351
417
loadCachedFactsOk bool
352
- loadMode LoadMode
418
+ isroot bool
419
+ isInitialPkg bool
420
+ needAnalyzeSource bool
353
421
}
354
422
355
423
type objectFactKey struct {
0 commit comments