Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1195)

Side by Side Diff: pkg/analysis_server/lib/src/domain_diagnostic.dart

Issue 1463923003: Rolling average work queue diagnostic (#24933). (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: nits Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library src.domain_diagnostic; 5 library src.domain_diagnostic;
6 6
7 import 'dart:async';
7 import 'dart:collection'; 8 import 'dart:collection';
8 import 'dart:core' hide Resource; 9 import 'dart:core' hide Resource;
9 10
10 import 'package:analysis_server/plugin/protocol/protocol.dart'; 11 import 'package:analysis_server/plugin/protocol/protocol.dart';
11 import 'package:analysis_server/src/analysis_server.dart'; 12 import 'package:analysis_server/src/analysis_server.dart';
12 import 'package:analyzer/file_system/file_system.dart'; 13 import 'package:analyzer/file_system/file_system.dart';
13 import 'package:analyzer/src/context/cache.dart'; 14 import 'package:analyzer/src/context/cache.dart';
14 import 'package:analyzer/src/context/context.dart'; 15 import 'package:analyzer/src/context/context.dart';
15 import 'package:analyzer/src/generated/engine.dart' 16 import 'package:analyzer/src/generated/engine.dart'
16 hide AnalysisCache, AnalysisContextImpl; 17 hide AnalysisCache, AnalysisContextImpl;
17 import 'package:analyzer/src/generated/source.dart'; 18 import 'package:analyzer/src/generated/source.dart';
18 import 'package:analyzer/src/generated/utilities_collection.dart'; 19 import 'package:analyzer/src/generated/utilities_collection.dart';
19 import 'package:analyzer/src/task/driver.dart'; 20 import 'package:analyzer/src/task/driver.dart';
20 import 'package:analyzer/task/model.dart'; 21 import 'package:analyzer/task/model.dart';
21 22
22 /// Extract context data from the given [context]. 23 int _workItemCount(AnalysisContextImpl context) {
23 ContextData extractData(AnalysisContext context) { 24 AnalysisDriver driver = context.driver;
24 int explicitFiles = 0; 25 List<WorkItem> items = driver.currentWorkOrder?.workItems;
25 int implicitFiles = 0; 26 return items?.length ?? 0;
26 int workItems = 0;
27 Set<String> exceptions = new HashSet<String>();
28 if (context is AnalysisContextImpl) {
29 // Work Item count.
30 AnalysisDriver driver = context.driver;
31 List<WorkItem> items = driver.currentWorkOrder?.workItems;
32 workItems ??= items?.length;
33 var cache = context.analysisCache;
34 if (cache is AnalysisCache) {
35 Set<AnalysisTarget> countedTargets = new HashSet<AnalysisTarget>();
36 MapIterator<AnalysisTarget, CacheEntry> iterator = cache.iterator();
37 while (iterator.moveNext()) {
38 AnalysisTarget target = iterator.key;
39 if (countedTargets.add(target)) {
40 CacheEntry cacheEntry = iterator.value;
41 if (target is Source) {
42 if (cacheEntry.explicitlyAdded) {
43 explicitFiles++;
44 } else {
45 implicitFiles++;
46 }
47 }
48 // Caught exceptions.
49 if (cacheEntry.exception != null) {
50 exceptions.add(cacheEntry.exception.toString());
51 }
52 }
53 }
54 }
55 }
56 return new ContextData(context.name, explicitFiles, implicitFiles, workItems,
57 exceptions.toList());
58 } 27 }
59 28
60 /// Instances of the class [DiagnosticDomainHandler] implement a 29 /// Instances of the class [DiagnosticDomainHandler] implement a
61 /// [RequestHandler] that handles requests in the `diagnostic` domain. 30 /// [RequestHandler] that handles requests in the `diagnostic` domain.
62 class DiagnosticDomainHandler implements RequestHandler { 31 class DiagnosticDomainHandler implements RequestHandler {
63 /// The name of the request used to get diagnostic information. 32 /// The name of the request used to get diagnostic information.
64 static const String DIAGNOSTICS = 'diagnostic.getDiagnostics'; 33 static const String DIAGNOSTICS = 'diagnostic.getDiagnostics';
65 34
66 /// The analysis server that is using this handler to process requests. 35 /// The analysis server that is using this handler to process requests.
67 final AnalysisServer server; 36 final AnalysisServer server;
68 37
38 /// The sampler tracking rolling work queue length averages.
39 Sampler sampler;
40
69 /// Initialize a newly created handler to handle requests for the given 41 /// Initialize a newly created handler to handle requests for the given
70 /// [server]. 42 /// [server].
71 DiagnosticDomainHandler(this.server); 43 DiagnosticDomainHandler(this.server);
72 44
73 /// Answer the `diagnostic.diagnostics` request. 45 /// Answer the `diagnostic.diagnostics` request.
74 Response computeDiagnostics(Request request) { 46 Response computeDiagnostics(Request request) {
47 // Initialize sampler if needed.
48 if (sampler == null) {
49 sampler = new Sampler(server);
50 }
51
75 List<ContextData> infos = <ContextData>[]; 52 List<ContextData> infos = <ContextData>[];
76 server.folderMap.forEach((Folder folder, AnalysisContext context) { 53 server.folderMap.forEach((Folder folder, AnalysisContext context) {
77 infos.add(extractData(context)); 54 infos.add(extractData(folder, context));
78 }); 55 });
79 56
80 return new DiagnosticGetDiagnosticsResult(infos).toResponse(request.id); 57 return new DiagnosticGetDiagnosticsResult(infos).toResponse(request.id);
81 } 58 }
82 59
60 /// Extract context data from the given [context].
61 ContextData extractData(Folder folder, AnalysisContext context) {
62 int explicitFiles = 0;
63 int implicitFiles = 0;
64 int workItems = 0;
65 String workItemAverage = '-1';
66 Set<String> exceptions = new HashSet<String>();
67 if (context is AnalysisContextImpl) {
68 workItems = _workItemCount(context);
69 workItemAverage = sampler.getAverage(folder)?.toString() ?? '-1';
70 var cache = context.analysisCache;
71 if (cache is AnalysisCache) {
72 Set<AnalysisTarget> countedTargets = new HashSet<AnalysisTarget>();
73 MapIterator<AnalysisTarget, CacheEntry> iterator = cache.iterator();
74 while (iterator.moveNext()) {
75 AnalysisTarget target = iterator.key;
76 if (countedTargets.add(target)) {
77 CacheEntry cacheEntry = iterator.value;
78 if (target is Source) {
79 if (cacheEntry.explicitlyAdded) {
80 explicitFiles++;
81 } else {
82 implicitFiles++;
83 }
84 }
85 // Caught exceptions.
86 if (cacheEntry.exception != null) {
87 exceptions.add(cacheEntry.exception.toString());
88 }
89 }
90 }
91 }
92 }
93 return new ContextData(context.name, explicitFiles, implicitFiles,
94 workItems, workItemAverage, exceptions.toList());
95 }
96
83 @override 97 @override
84 Response handleRequest(Request request) { 98 Response handleRequest(Request request) {
85 try { 99 try {
86 String requestName = request.method; 100 String requestName = request.method;
87 if (requestName == DIAGNOSTICS) { 101 if (requestName == DIAGNOSTICS) {
88 return computeDiagnostics(request); 102 return computeDiagnostics(request);
89 } 103 }
90 } on RequestFailure catch (exception) { 104 } on RequestFailure catch (exception) {
91 return exception.response; 105 return exception.response;
92 } 106 }
93 return null; 107 return null;
94 } 108 }
95 } 109 }
110
111 /// Keeps track of a moving average of work item queue lengths mapped to
112 /// contexts.
113 ///
114 /// Sampling terminates after [maxSampleCount], if no one expresses interest
115 /// by calling [resetTimerCountdown].
116 class Sampler {
117 /// Timer interval.
118 static const Duration duration = const Duration(seconds: 1);
119
120 /// Maximum number of samples taken between calls to [reset].
121 static const int maxSampleCount = 30;
122
123 /// Current sample count.
124 int sampleCount = 0;
125
126 /// The shared timer.
127 Timer timer;
128
129 /// Map of contexts (tracked as folders to avoid leaks) to averages.
130 /// TOOD(pq): consider adding GC to remove mappings for deleted folders
131 Map<Folder, _Average> averages = new HashMap<Folder, _Average>();
132
133 final AnalysisServer server;
134 Sampler(this.server) {
135 start();
136 _sample();
137 }
138
139 /// Get the average for the context associated with the given [folder].
140 int getAverage(Folder folder) {
141 resetTimerCountdown();
142 return averages[folder].value;
143 }
144
145 /// Check if we're currently sampling.
146 bool isSampling() => timer?.isActive ?? false;
147
148 /// Reset counter.
149 void resetTimerCountdown() {
150 sampleCount = 0;
151 }
152
153 /// Start sampling.
154 void start() {
155 // No need to (re)start if already sampling.
156 if (isSampling()) {
157 return;
158 }
159 timer = new Timer.periodic(duration, (Timer timer) {
160 _sample();
161 if (sampleCount++ >= maxSampleCount) {
162 timer.cancel();
163 }
164 });
165 }
166
167 /// Stop sampling.
168 void stop() {
169 timer.cancel();
170 }
171
172 /// Take a sample.
173 void _sample() {
174 try {
175 server.folderMap.forEach((Folder folder, AnalysisContext context) {
176 if (context is AnalysisContextImpl) {
177 _Average average = averages[folder];
178 if (average == null) {
179 average = new _Average();
180 averages[folder] = average;
181 }
182 average.addSample(_workItemCount(context));
183 }
184 });
185 } on Exception {
186 stop();
187 }
188 }
189 }
190
191 /// Simple rolling average sample counter.
192 class _Average {
193 num _val;
194
195 final int sampleCount;
196 _Average([this.sampleCount = 20]);
197
198 num get value => _val ?? 0;
199
200 void addSample(num sample) {
201 if (_val == null) {
202 _val = sample;
203 } else {
204 _val =
205 _val * ((sampleCount - 1) / sampleCount) + sample * (1 / sampleCount);
206 }
207 }
208
209 @override
210 String toString() => 'average: ${value}';
211 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/lib/plugin/protocol/generated_protocol.dart ('k') | pkg/analysis_server/test/domain_diagnostic_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698