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

Side by Side Diff: pkg/analysis_server/lib/src/status/diagnostics.dart

Issue 2933753002: Run the sorter to reduce code churn (Closed)
Patch Set: Created 3 years, 6 months 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) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, 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 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:convert'; 6 import 'dart:convert';
7 import 'dart:io'; 7 import 'dart:io';
8 8
9 import 'package:analysis_server/protocol/protocol_generated.dart'; 9 import 'package:analysis_server/protocol/protocol_generated.dart';
10 import 'package:analysis_server/src/analysis_server.dart'; 10 import 'package:analysis_server/src/analysis_server.dart';
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 line-height: 1.75; 124 line-height: 1.75;
125 color: #7a7a7a; 125 color: #7a7a7a;
126 border-top: 1px solid #eee; 126 border-top: 1px solid #eee;
127 } 127 }
128 128
129 .footer strong { 129 .footer strong {
130 color: #333; 130 color: #333;
131 } 131 }
132 '''; 132 ''';
133 133
134 String get _sdkVersion {
135 String version = Platform.version;
136 if (version.contains(' ')) {
137 version = version.substring(0, version.indexOf(' '));
138 }
139 return version;
140 }
141
142 String writeOption(String name, dynamic value) {
143 return '$name: <code>$value</code><br> ';
144 }
145
134 class AstPage extends DiagnosticPageWithNav { 146 class AstPage extends DiagnosticPageWithNav {
135 String _description; 147 String _description;
136 148
137 AstPage(DiagnosticsSite site) 149 AstPage(DiagnosticsSite site)
138 : super(site, 'ast', 'AST', description: 'The AST for a file.'); 150 : super(site, 'ast', 'AST', description: 'The AST for a file.');
139 151
140 @override 152 @override
141 String get description => _description ?? super.description; 153 String get description => _description ?? super.description;
142 154
143 @override 155 @override
144 Future<Null> generatePage(Map<String, String> params) async {
145 try {
146 String path = params['file'];
147 _description = path == null ? null : 'The AST for $path.';
148 await super.generatePage(params);
149 } finally {
150 _description = null;
151 }
152 }
153
154 @override
155 Future<Null> generateContent(Map<String, String> params) async { 156 Future<Null> generateContent(Map<String, String> params) async {
156 String path = params['file']; 157 String path = params['file'];
157 if (path == null) { 158 if (path == null) {
158 p('No file path provided.'); 159 p('No file path provided.');
159 return; 160 return;
160 } 161 }
161 AnalysisDriver driver = server.getAnalysisDriver(path); 162 AnalysisDriver driver = server.getAnalysisDriver(path);
162 if (driver == null) { 163 if (driver == null) {
163 p('The file <code>${escape(path)}</code> is not being analyzed.', 164 p('The file <code>${escape(path)}</code> is not being analyzed.',
164 raw: true); 165 raw: true);
165 return; 166 return;
166 } 167 }
167 AnalysisResult result = await driver.getResult(path); 168 AnalysisResult result = await driver.getResult(path);
168 if (result == null) { 169 if (result == null) {
169 p('An AST could not be produced for the file <code>${escape(path)}</code>. ', 170 p('An AST could not be produced for the file <code>${escape(path)}</code>. ',
170 raw: true); 171 raw: true);
171 return; 172 return;
172 } 173 }
173 174
174 AstWriter writer = new AstWriter(buf); 175 AstWriter writer = new AstWriter(buf);
175 result.unit.accept(writer); 176 result.unit.accept(writer);
176 } 177 }
177 } 178
178 179 @override
179 class DiagnosticsSite extends Site implements AbstractGetHandler { 180 Future<Null> generatePage(Map<String, String> params) async {
180 /// An object that can handle either a WebSocket connection or a connection 181 try {
181 /// to the client over stdio. 182 String path = params['file'];
182 SocketServer socketServer; 183 _description = path == null ? null : 'The AST for $path.';
183 184 await super.generatePage(params);
184 /// The last few lines printed. 185 } finally {
185 List<String> lastPrintedLines = <String>[]; 186 _description = null;
186 187 }
187 DiagnosticsSite(this.socketServer, this.lastPrintedLines) 188 }
188 : super('Analysis Server') { 189 }
189 pages.add(new CompletionPage(this)); 190
190 pages.add(new CommunicationsPage(this)); 191 class CommunicationsPage extends DiagnosticPageWithNav {
191 pages.add(new ContextsPage(this)); 192 CommunicationsPage(DiagnosticsSite site)
192 pages.add(new ExceptionsPage(this)); 193 : super(site, 'communications', 'Communications',
193 pages.add(new InstrumentationPage(this)); 194 description:
194 pages.add(new OverlaysPage(this)); 195 'Latency statistics for analysis server communications.');
195 pages.add(new PluginsPage(this)); 196
196 pages.add(new ProfilePage(this)); 197 @override
197 pages.add(new SubscriptionsPage(this)); 198 void generateContent(Map<String, String> params) {
198 199 void writeRow(List<String> data, {List<String> classes}) {
199 ProcessProfiler profiler = ProcessProfiler.getProfilerForPlatform(); 200 buf.write("<tr>");
200 if (profiler != null) { 201 for (int i = 0; i < data.length; i++) {
201 pages.add(new MemoryAndCpuPage(this, profiler)); 202 String c = classes == null ? null : classes[i];
202 } 203 if (c != null) {
203 204 buf.write('<td class="$c">${escape(data[i])}</td>');
204 pages.sort(((Page a, Page b) => 205 } else {
205 a.title.toLowerCase().compareTo(b.title.toLowerCase()))); 206 buf.write('<td>${escape(data[i])}</td>');
206 207 }
207 // Add the status page at the beginning. 208 }
208 pages.insert(0, new StatusPage(this)); 209 buf.writeln("</tr>");
209 210 }
210 // Add non-nav pages. 211
211 pages.add(new FeedbackPage(this)); 212 buf.writeln('<div class="columns">');
212 213
213 secondaryPages.add(new AstPage(this)); 214 ServerPerformance perf = server.performanceAfterStartup;
214 secondaryPages.add(new ElementModelPage(this)); 215 if (perf != null) {
215 } 216 buf.writeln('<div class="column one-half">');
216 217 h3('Current');
217 String get customCss => kCustomCss; 218
218 219 int requestCount = perf.requestCount;
219 Page createUnknownPage(String unknownPath) => 220 int averageLatency =
220 new NotFoundPage(this, unknownPath); 221 requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
221 222 int maximumLatency = perf.maxLatency;
222 Page createExceptionPage(String message, StackTrace trace) => 223 double slowRequestPercent =
223 new ExceptionPage(this, message, trace); 224 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
225
226 buf.write('<table>');
227 writeRow([printInteger(requestCount), 'requests'],
228 classes: ["right", null]);
229 writeRow([printMilliseconds(averageLatency), 'average latency'],
230 classes: ["right", null]);
231 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
232 classes: ["right", null]);
233 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
234 classes: ["right", null]);
235 buf.write('</table>');
236
237 String time = server.uptime.toString();
238 if (time.contains('.')) {
239 time = time.substring(0, time.indexOf('.'));
240 }
241 buf.writeln(writeOption('Uptime', time));
242
243 buf.write('</div>');
244 }
245
246 buf.writeln('<div class="column one-half">');
247 h3('Startup');
248 perf = server.performanceDuringStartup;
249
250 int requestCount = perf.requestCount;
251 int averageLatency =
252 requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
253 int maximumLatency = perf.maxLatency;
254 double slowRequestPercent =
255 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
256
257 buf.write('<table>');
258 writeRow([printInteger(requestCount), 'requests'],
259 classes: ["right", null]);
260 writeRow([printMilliseconds(averageLatency), 'average latency'],
261 classes: ["right", null]);
262 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
263 classes: ["right", null]);
264 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
265 classes: ["right", null]);
266 buf.write('</table>');
267
268 if (server.performanceAfterStartup != null) {
269 int startupTime =
270 server.performanceAfterStartup.startTime - perf.startTime;
271 buf.writeln(
272 writeOption('Initial analysis time', printMilliseconds(startupTime)));
273 }
274 buf.write('</div>');
275
276 buf.write('</div>');
277 }
278 }
279
280 class CompletionPage extends DiagnosticPageWithNav {
281 CompletionPage(DiagnosticsSite site)
282 : super(site, 'completion', 'Code Completion',
283 description: 'Latency statistics for code completion.');
284
285 @override
286 void generateContent(Map<String, String> params) {
287 CompletionDomainHandler completionDomain = server.handlers
288 .firstWhere((handler) => handler is CompletionDomainHandler);
289
290 List<CompletionPerformance> completions =
291 completionDomain.performanceList.items.toList();
292
293 if (completions.isEmpty) {
294 blankslate('No completions recorded.');
295 return;
296 }
297
298 int fastCount =
299 completions.where((c) => c.elapsedInMilliseconds <= 100).length;
300 p('${completions.length} results; ${printPercentage(fastCount / completions. length)} within 100ms.');
301
302 // draw a chart
303 buf.writeln(
304 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
305 StringBuffer rowData = new StringBuffer();
306 for (int i = completions.length - 1; i >= 0; i--) {
307 // [' ', 101.5]
308 if (rowData.isNotEmpty) {
309 rowData.write(',');
310 }
311 rowData.write("[' ', ${completions[i].elapsedInMilliseconds}]");
312 }
313 buf.writeln('''
314 <script type="text/javascript">
315 google.charts.load('current', {'packages':['bar']});
316 google.charts.setOnLoadCallback(drawChart);
317 function drawChart() {
318 var data = google.visualization.arrayToDataTable([
319 ['Completions', 'Time'],
320 $rowData
321 ]);
322 var options = { bars: 'vertical', vAxis: {format: 'decimal'}, height: 30 0 };
323 var chart = new google.charts.Bar(document.getElementById('chart-div'));
324 chart.draw(data, google.charts.Bar.convertOptions(options));
325 }
326 </script>
327 ''');
328
329 // emit the data as a table
330 buf.writeln('<table>');
331 buf.writeln(
332 '<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>') ;
333 for (CompletionPerformance completion in completions) {
334 buf.writeln('<tr>'
335 '<td class="pre right">${printMilliseconds(completion.elapsedInMillise conds)}</td>'
336 '<td class="right">${completion.suggestionCount}</td>'
337 '<td>${escape(completion.source.shortName)}</td>'
338 '<td><code>${escape(completion.snippet)}</code></td>'
339 '</tr>');
340 }
341 buf.writeln('</table>');
342 }
343 }
344
345 class ContextsPage extends DiagnosticPageWithNav {
346 ContextsPage(DiagnosticsSite site)
347 : super(site, 'contexts', 'Contexts',
348 description:
349 'An analysis context defines the options and the set of sources being analyzed.');
350
351 String get navDetail => printInteger(server.driverMap.length);
352
353 String describe(AnalysisOptionsImpl options) {
354 StringBuffer b = new StringBuffer();
355
356 b.write(
357 writeOption('Analyze function bodies', options.analyzeFunctionBodies));
358 b.write(writeOption('Enable asserts in initializer lists',
359 options.enableAssertInitializer));
360 b.write(writeOption(
361 'Enable strict call checks', options.enableStrictCallChecks));
362 b.write(writeOption('Enable super mixins', options.enableSuperMixins));
363 b.write(writeOption('Generate dart2js hints', options.dart2jsHint));
364 b.write(writeOption(
365 'Generate errors in implicit files', options.generateImplicitErrors));
366 b.write(
367 writeOption('Generate errors in SDK files', options.generateSdkErrors));
368 b.write(writeOption('Generate hints', options.hint));
369 b.write(writeOption('Incremental resolution', options.incremental));
370 b.write(writeOption(
371 'Incremental resolution with API changes', options.incrementalApi));
372 b.write(writeOption('Preserve comments', options.preserveComments));
373 b.write(writeOption('Strong mode', options.strongMode));
374 b.write(writeOption('Strong mode hints', options.strongModeHints));
375
376 return b.toString();
377 }
378
379 @override
380 void generateContent(Map<String, String> params) {
381 Map<Folder, AnalysisDriver> driverMap = server.driverMap;
382 if (driverMap.isEmpty) {
383 blankslate('No contexts.');
384 return;
385 }
386
387 String contextPath = params['context'];
388 List<Folder> folders = driverMap.keys.toList();
389 folders
390 .sort((first, second) => first.shortName.compareTo(second.shortName));
391 Folder folder =
392 folders.firstWhere((f) => f.path == contextPath, orElse: () => null);
393
394 if (folder == null) {
395 folder = folders.first;
396 contextPath = folder.path;
397 }
398
399 AnalysisDriver driver = driverMap[folder];
400
401 buf.writeln('<div class="tabnav">');
402 buf.writeln('<nav class="tabnav-tabs">');
403 for (Folder f in folders) {
404 if (f == folder) {
405 buf.writeln(
406 '<a class="tabnav-tab selected">${escape(f.shortName)}</a>');
407 } else {
408 String p = '$path?context=${Uri.encodeQueryComponent(f.path)}';
409 buf.writeln(
410 '<a href="$p" class="tabnav-tab">${escape(f.shortName)}</a>');
411 }
412 }
413 buf.writeln('</nav>');
414 buf.writeln('</div>');
415
416 buf.writeln(writeOption('Context location', escape(contextPath)));
417 buf.writeln(writeOption('Analysis options path',
418 escape(driver.contextRoot.optionsFilePath ?? 'none')));
419
420 buf.writeln('<div class="columns">');
421
422 buf.writeln('<div class="column one-half">');
423 h3('Analysis options');
424 p(describe(driver.analysisOptions), raw: true);
425 buf.writeln(
426 writeOption('Has .packages file', folder.getChild('.packages').exists));
427 buf.writeln(writeOption(
428 'Has pubspec.yaml file', folder.getChild('pubspec.yaml').exists));
429 buf.writeln('</div>');
430
431 buf.writeln('<div class="column one-half">');
432 DartSdk sdk = driver?.sourceFactory?.dartSdk;
433 AnalysisOptionsImpl sdkOptions = sdk?.context?.analysisOptions;
434 if (sdkOptions != null) {
435 h3('SDK analysis options');
436 p(describe(sdkOptions), raw: true);
437
438 if (sdk is FolderBasedDartSdk) {
439 p(writeOption('Use summaries', sdk.useSummary), raw: true);
440 }
441 }
442 buf.writeln('</div>');
443
444 buf.writeln('</div>');
445
446 h3('Lints');
447 p(driver.analysisOptions.lintRules.map((l) => l.name).join(', '));
448
449 h3('Error processors');
450 p(driver.analysisOptions.errorProcessors
451 .map((e) => e.description)
452 .join(', '));
453
454 List<String> priorityFiles = driver.priorityFiles;
455 List<String> addedFiles = driver.addedFiles.toList();
456 List<String> implicitFiles =
457 driver.knownFiles.difference(driver.addedFiles).toList();
458 addedFiles.sort();
459 implicitFiles.sort();
460
461 String lenCounter(List list) {
462 return '<span class="counter" style="float: right;">${list.length}</span>' ;
463 }
464
465 h3('Context files');
466
467 void writeFile(String file) {
468 String astPath = '/ast?file=${Uri.encodeQueryComponent(file)}';
469 String elementPath = '/element?file=${Uri.encodeQueryComponent(file)}';
470
471 buf.write(file);
472 buf.write(' (');
473 buf.writeln('<a href="$astPath">ast</a>');
474 buf.write(' ');
475 buf.writeln('<a href="$elementPath">element</a>');
476 buf.write(')');
477 }
478
479 h4('Priority files ${lenCounter(priorityFiles)}', raw: true);
480 ul(priorityFiles, writeFile, classes: 'scroll-table');
481
482 h4('Added files ${lenCounter(addedFiles)}', raw: true);
483 ul(addedFiles, writeFile, classes: 'scroll-table');
484
485 h4('ImplicitFiles files ${lenCounter(implicitFiles)}', raw: true);
486 ul(implicitFiles, writeFile, classes: 'scroll-table');
487
488 SourceFactory sourceFactory = driver.sourceFactory;
489 if (sourceFactory is SourceFactoryImpl) {
490 h3('Resolvers');
491 for (UriResolver resolver in sourceFactory.resolvers) {
492 h4(resolver.runtimeType.toString());
493 buf.write('<p class="scroll-table">');
494 if (resolver is DartUriResolver) {
495 DartSdk sdk = resolver.dartSdk;
496 buf.write(' (sdk = ');
497 buf.write(sdk.runtimeType);
498 if (sdk is FolderBasedDartSdk) {
499 buf.write(' (path = ');
500 buf.write(sdk.directory.path);
501 buf.write(')');
502 } else if (sdk is EmbedderSdk) {
503 buf.write(' (map = ');
504 writeMap(sdk.urlMappings);
505 buf.write(')');
506 }
507 buf.write(')');
508 } else if (resolver is SdkExtUriResolver) {
509 buf.write(' (map = ');
510 writeMap(resolver.urlMappings);
511 buf.write(')');
512 } else if (resolver is PackageMapUriResolver) {
513 writeMap(resolver.packageMap);
514 }
515 buf.write('</p>');
516 }
517 }
518 }
519
520 void writeList<E>(List<E> list) {
521 buf.writeln('[${list.join(', ')}]');
522 }
523
524 void writeMap<V>(Map<String, V> map) {
525 List<String> keys = map.keys.toList();
526 keys.sort();
527 int length = keys.length;
528 buf.write('{');
529 for (int i = 0; i < length; i++) {
530 buf.write('<br>');
531 String key = keys[i];
532 V value = map[key];
533 buf.write(key);
534 buf.write(' = ');
535 if (value is List) {
536 writeList(value);
537 } else {
538 buf.write(value);
539 }
540 buf.write(',');
541 }
542 buf.write('<br>}');
543 }
224 } 544 }
225 545
226 /// A page with a proscriptive notion of layout. 546 /// A page with a proscriptive notion of layout.
227 abstract class DiagnosticPage extends Page { 547 abstract class DiagnosticPage extends Page {
228 final Site site; 548 final Site site;
229 549
230 DiagnosticPage(this.site, String id, String title, {String description}) 550 DiagnosticPage(this.site, String id, String title, {String description})
231 : super(id, title, description: description); 551 : super(id, title, description: description);
232 552
553 bool get isNavPage => false;
554
233 AnalysisServer get server => 555 AnalysisServer get server =>
234 (site as DiagnosticsSite).socketServer.analysisServer; 556 (site as DiagnosticsSite).socketServer.analysisServer;
235 557
558 Future<Null> generateContainer(Map<String, String> params) async {
559 buf.writeln('<div class="columns docs-layout">');
560 buf.writeln('<div class="three-fourths column markdown-body">');
561 h1(title, classes: 'page-title');
562 await asyncDiv(() async {
563 p(description);
564 await generateContent(params);
565 }, classes: 'markdown-body');
566 buf.writeln('</div>');
567 buf.writeln('</div>');
568 }
569
570 void generateContent(Map<String, String> params);
571
572 void generateFooter() {
573 buf.writeln('''
574 <footer class="footer">
575 Dart ${site.title} <span style="float:right">SDK ${_sdkVersion}</span>
576 </footer>
577 ''');
578 }
579
580 void generateHeader() {
581 buf.writeln('''
582 <header class="masthead">
583 <div class="container">
584 <span class="masthead-logo">
585 <span class="mega-octicon octicon-database"></span>
586 ${site.title} Diagnostics
587 </span>
588
589 <nav class="masthead-nav">
590 <a href="/status" ${isNavPage ? ' class="active"' : ''}>Diagnostics</a>
591 <a href="/feedback" ${isCurrentPage('/feedback') ? ' class="active"' : ' '}>Feedback</a>
592 <a href="https://www.dartlang.org/tools/analyzer" target="_blank">Docs</ a>
593 <a href="https://htmlpreview.github.io/?https://github.com/dart-lang/sdk /blob/master/pkg/analysis_server/doc/api.html" target="_blank">Spec</a>
594 </nav>
595 </div>
596 </header>
597 ''');
598 }
599
236 Future<Null> generatePage(Map<String, String> params) async { 600 Future<Null> generatePage(Map<String, String> params) async {
237 buf.writeln('<!DOCTYPE html><html lang="en">'); 601 buf.writeln('<!DOCTYPE html><html lang="en">');
238 buf.write('<head>'); 602 buf.write('<head>');
239 buf.write('<meta charset="utf-8">'); 603 buf.write('<meta charset="utf-8">');
240 buf.write('<meta name="viewport" content="width=device-width, ' 604 buf.write('<meta name="viewport" content="width=device-width, '
241 'initial-scale=1.0">'); 605 'initial-scale=1.0">');
242 buf.writeln('<title>${site.title}</title>'); 606 buf.writeln('<title>${site.title}</title>');
243 buf.writeln('<link rel="stylesheet" ' 607 buf.writeln('<link rel="stylesheet" '
244 'href="https://cdnjs.cloudflare.com/ajax/libs/Primer/6.0.0/build.css">') ; 608 'href="https://cdnjs.cloudflare.com/ajax/libs/Primer/6.0.0/build.css">') ;
245 buf.writeln('<link rel="stylesheet" ' 609 buf.writeln('<link rel="stylesheet" '
246 'href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octico ns.css">'); 610 'href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octico ns.css">');
247 buf.writeln('<script type="text/javascript" ' 611 buf.writeln('<script type="text/javascript" '
248 'src="https://www.gstatic.com/charts/loader.js"></script>'); 612 'src="https://www.gstatic.com/charts/loader.js"></script>');
249 buf.writeln('<style>${site.customCss}</style>'); 613 buf.writeln('<style>${site.customCss}</style>');
250 buf.writeln('</head>'); 614 buf.writeln('</head>');
251 615
252 buf.writeln('<body>'); 616 buf.writeln('<body>');
253 generateHeader(); 617 generateHeader();
254 buf.writeln('<div class="container">'); 618 buf.writeln('<div class="container">');
255 await generateContainer(params); 619 await generateContainer(params);
256 generateFooter(); 620 generateFooter();
257 buf.writeln('</div>'); // div.container 621 buf.writeln('</div>'); // div.container
258 buf.writeln('</body>'); 622 buf.writeln('</body>');
259 buf.writeln('</html>'); 623 buf.writeln('</html>');
260 } 624 }
261
262 void generateHeader() {
263 buf.writeln('''
264 <header class="masthead">
265 <div class="container">
266 <span class="masthead-logo">
267 <span class="mega-octicon octicon-database"></span>
268 ${site.title} Diagnostics
269 </span>
270
271 <nav class="masthead-nav">
272 <a href="/status" ${isNavPage ? ' class="active"' : ''}>Diagnostics</a>
273 <a href="/feedback" ${isCurrentPage('/feedback') ? ' class="active"' : ' '}>Feedback</a>
274 <a href="https://www.dartlang.org/tools/analyzer" target="_blank">Docs</ a>
275 <a href="https://htmlpreview.github.io/?https://github.com/dart-lang/sdk /blob/master/pkg/analysis_server/doc/api.html" target="_blank">Spec</a>
276 </nav>
277 </div>
278 </header>
279 ''');
280 }
281
282 Future<Null> generateContainer(Map<String, String> params) async {
283 buf.writeln('<div class="columns docs-layout">');
284 buf.writeln('<div class="three-fourths column markdown-body">');
285 h1(title, classes: 'page-title');
286 await asyncDiv(() async {
287 p(description);
288 await generateContent(params);
289 }, classes: 'markdown-body');
290 buf.writeln('</div>');
291 buf.writeln('</div>');
292 }
293
294 void generateContent(Map<String, String> params);
295
296 void generateFooter() {
297 buf.writeln('''
298 <footer class="footer">
299 Dart ${site.title} <span style="float:right">SDK ${_sdkVersion}</span>
300 </footer>
301 ''');
302 }
303
304 bool get isNavPage => false;
305 } 625 }
306 626
307 abstract class DiagnosticPageWithNav extends DiagnosticPage { 627 abstract class DiagnosticPageWithNav extends DiagnosticPage {
308 DiagnosticPageWithNav(Site site, String id, String title, 628 DiagnosticPageWithNav(Site site, String id, String title,
309 {String description}) 629 {String description})
310 : super(site, id, title, description: description); 630 : super(site, id, title, description: description);
311 631
632 bool get isNavPage => true;
633
634 String get navDetail => null;
635
312 Future<Null> generateContainer(Map<String, String> params) async { 636 Future<Null> generateContainer(Map<String, String> params) async {
313 buf.writeln('<div class="columns docs-layout">'); 637 buf.writeln('<div class="columns docs-layout">');
314 638
315 buf.writeln('<div class="one-fifth column">'); 639 buf.writeln('<div class="one-fifth column">');
316 buf.writeln('<nav class="menu docs-menu">'); 640 buf.writeln('<nav class="menu docs-menu">');
317 for (Page page in site.pages.where((p) => p is DiagnosticPageWithNav)) { 641 for (Page page in site.pages.where((p) => p is DiagnosticPageWithNav)) {
318 buf.write('<a class="menu-item ${page == this ? ' selected' : ''}" ' 642 buf.write('<a class="menu-item ${page == this ? ' selected' : ''}" '
319 'href="${page.path}">${escape(page.title)}'); 643 'href="${page.path}">${escape(page.title)}');
320 String detail = (page as DiagnosticPageWithNav).navDetail; 644 String detail = (page as DiagnosticPageWithNav).navDetail;
321 if (detail != null) { 645 if (detail != null) {
322 buf.write('<span class="counter">$detail</span>'); 646 buf.write('<span class="counter">$detail</span>');
323 } 647 }
324 buf.writeln('</a>'); 648 buf.writeln('</a>');
325 } 649 }
326 buf.writeln('</nav>'); 650 buf.writeln('</nav>');
327 buf.writeln('</div>'); 651 buf.writeln('</div>');
328 652
329 buf.writeln('<div class="four-fifths column markdown-body">'); 653 buf.writeln('<div class="four-fifths column markdown-body">');
330 h1(title, classes: 'page-title'); 654 h1(title, classes: 'page-title');
331 await asyncDiv(() async { 655 await asyncDiv(() async {
332 p(description); 656 p(description);
333 await generateContent(params); 657 await generateContent(params);
334 }, classes: 'markdown-body'); 658 }, classes: 'markdown-body');
335 buf.writeln('</div>'); 659 buf.writeln('</div>');
336 660
337 buf.writeln('</div>'); 661 buf.writeln('</div>');
338 } 662 }
339 663 }
340 String get navDetail => null; 664
341 665 class DiagnosticsSite extends Site implements AbstractGetHandler {
342 bool get isNavPage => true; 666 /// An object that can handle either a WebSocket connection or a connection
667 /// to the client over stdio.
668 SocketServer socketServer;
669
670 /// The last few lines printed.
671 List<String> lastPrintedLines = <String>[];
672
673 DiagnosticsSite(this.socketServer, this.lastPrintedLines)
674 : super('Analysis Server') {
675 pages.add(new CompletionPage(this));
676 pages.add(new CommunicationsPage(this));
677 pages.add(new ContextsPage(this));
678 pages.add(new ExceptionsPage(this));
679 pages.add(new InstrumentationPage(this));
680 pages.add(new OverlaysPage(this));
681 pages.add(new PluginsPage(this));
682 pages.add(new ProfilePage(this));
683 pages.add(new SubscriptionsPage(this));
684
685 ProcessProfiler profiler = ProcessProfiler.getProfilerForPlatform();
686 if (profiler != null) {
687 pages.add(new MemoryAndCpuPage(this, profiler));
688 }
689
690 pages.sort(((Page a, Page b) =>
691 a.title.toLowerCase().compareTo(b.title.toLowerCase())));
692
693 // Add the status page at the beginning.
694 pages.insert(0, new StatusPage(this));
695
696 // Add non-nav pages.
697 pages.add(new FeedbackPage(this));
698
699 secondaryPages.add(new AstPage(this));
700 secondaryPages.add(new ElementModelPage(this));
701 }
702
703 String get customCss => kCustomCss;
704
705 Page createExceptionPage(String message, StackTrace trace) =>
706 new ExceptionPage(this, message, trace);
707
708 Page createUnknownPage(String unknownPath) =>
709 new NotFoundPage(this, unknownPath);
343 } 710 }
344 711
345 class ElementModelPage extends DiagnosticPageWithNav { 712 class ElementModelPage extends DiagnosticPageWithNav {
346 String _description; 713 String _description;
347 714
348 ElementModelPage(DiagnosticsSite site) 715 ElementModelPage(DiagnosticsSite site)
349 : super(site, 'element', 'Element model', 716 : super(site, 'element', 'Element model',
350 description: 'The element model for a file.'); 717 description: 'The element model for a file.');
351 718
352 @override 719 @override
353 String get description => _description ?? super.description; 720 String get description => _description ?? super.description;
354 721
355 @override 722 @override
723 Future<Null> generateContent(Map<String, String> params) async {
724 String path = params['file'];
725 if (path == null) {
726 p('No file path provided.');
727 return;
728 }
729 AnalysisDriver driver = server.getAnalysisDriver(path);
730 if (driver == null) {
731 p('The file <code>${escape(path)}</code> is not being analyzed.',
732 raw: true);
733 return;
734 }
735 AnalysisResult result = await driver.getResult(path);
736 if (result == null) {
737 p('An element model could not be produced for the file <code>${escape(path )}</code>.',
738 raw: true);
739 return;
740 }
741
742 ElementWriter writer = new ElementWriter(buf);
743 result.unit.element.accept(writer);
744 }
745
746 @override
356 Future<Null> generatePage(Map<String, String> params) async { 747 Future<Null> generatePage(Map<String, String> params) async {
357 try { 748 try {
358 String path = params['file']; 749 String path = params['file'];
359 _description = path == null ? null : 'The element model for $path.'; 750 _description = path == null ? null : 'The element model for $path.';
360 await super.generatePage(params); 751 await super.generatePage(params);
361 } finally { 752 } finally {
362 _description = null; 753 _description = null;
363 } 754 }
364 } 755 }
365
366 @override
367 Future<Null> generateContent(Map<String, String> params) async {
368 String path = params['file'];
369 if (path == null) {
370 p('No file path provided.');
371 return;
372 }
373 AnalysisDriver driver = server.getAnalysisDriver(path);
374 if (driver == null) {
375 p('The file <code>${escape(path)}</code> is not being analyzed.',
376 raw: true);
377 return;
378 }
379 AnalysisResult result = await driver.getResult(path);
380 if (result == null) {
381 p('An element model could not be produced for the file <code>${escape(path )}</code>.',
382 raw: true);
383 return;
384 }
385
386 ElementWriter writer = new ElementWriter(buf);
387 result.unit.element.accept(writer);
388 }
389 }
390
391 class NotFoundPage extends DiagnosticPage {
392 final String path;
393
394 NotFoundPage(Site site, this.path)
395 : super(site, '', '404 Not found', description: "'$path' not found.");
396
397 void generateContent(Map<String, String> params) {}
398 } 756 }
399 757
400 class ExceptionPage extends DiagnosticPage { 758 class ExceptionPage extends DiagnosticPage {
401 final StackTrace trace; 759 final StackTrace trace;
402 760
403 ExceptionPage(Site site, String message, this.trace) 761 ExceptionPage(Site site, String message, this.trace)
404 : super(site, '', '500 Oops', description: message); 762 : super(site, '', '500 Oops', description: message);
405 763
406 void generateContent(Map<String, String> params) { 764 void generateContent(Map<String, String> params) {
407 p(trace.toString(), style: 'white-space: pre'); 765 p(trace.toString(), style: 'white-space: pre');
408 } 766 }
409 } 767 }
410 768
411 class FeedbackPage extends DiagnosticPage {
412 FeedbackPage(DiagnosticsSite site)
413 : super(site, 'feedback', 'Feedback',
414 description: 'Providing feedback and filing issues.');
415
416 @override
417 void generateContent(Map<String, String> params) {
418 final String issuesUrl = 'https://github.com/dart-lang/sdk/issues';
419 p(
420 'To file issues or feature requests, see our '
421 '<a href="$issuesUrl">bug tracker</a>. When filing an issue, please de scribe:',
422 raw: true,
423 );
424 ul([
425 'what you were doing',
426 'what occured',
427 'what you think the expected behavior should have been',
428 ], (line) => buf.writeln(line));
429
430 List<String> ideInfo = [];
431 if (server.options.clientId != null) {
432 ideInfo.add(server.options.clientId);
433 }
434 if (server.options.clientVersion != null) {
435 ideInfo.add(server.options.clientVersion);
436 }
437 String ideText = ideInfo.map((str) => '<code>$str</code>').join(', ');
438
439 p('Other data to include:');
440 ul([
441 "the IDE you are using and it's version${ideText.isEmpty ? '' : ' ($ideTex t)'}",
442 'the Dart SDK version (<code>${escape(_sdkVersion)}</code>)',
443 'your operating system (<code>${escape(Platform.operatingSystem)}</code>)' ,
444 ], (line) => buf.writeln(line));
445
446 p('Thanks!');
447 }
448 }
449
450 class StatusPage extends DiagnosticPageWithNav {
451 StatusPage(DiagnosticsSite site)
452 : super(site, 'status', 'Status',
453 description:
454 'General status and diagnostics for the analysis server.');
455
456 @override
457 void generateContent(Map<String, String> params) {
458 buf.writeln('<div class="columns">');
459
460 buf.writeln('<div class="column one-half">');
461 h3('Status');
462 buf.writeln(writeOption(
463 'New analysis driver enabled', server.options.enableNewAnalysisDriver));
464 buf.writeln(writeOption('Instrumentation enabled',
465 AnalysisEngine.instance.instrumentationService.isActive));
466 buf.writeln(writeOption('Server process ID', pid));
467 buf.writeln('</div>');
468
469 buf.writeln('<div class="column one-half">');
470 h3('Versions');
471 buf.writeln(writeOption('Analysis server version', AnalysisServer.VERSION));
472 buf.writeln(writeOption('Dart SDK', Platform.version));
473 buf.writeln('</div>');
474
475 buf.writeln('</div>');
476
477 List<String> lines = (site as DiagnosticsSite).lastPrintedLines;
478 if (lines.isNotEmpty) {
479 h3('Debug output');
480 p(lines.join('\n'), style: 'white-space: pre');
481 }
482 }
483 }
484
485 class InstrumentationPage extends DiagnosticPageWithNav {
486 InstrumentationPage(DiagnosticsSite site)
487 : super(site, 'instrumentation', 'Instrumentation',
488 description:
489 'Verbose instrumentation data from the analysis server.');
490
491 @override
492 void generateContent(Map<String, String> params) {
493 p(
494 'Instrumentation can be enabled by starting the analysis server with the '
495 '<code>--instrumentation-log-file=path/to/file</code> flag.',
496 raw: true);
497
498 if (!AnalysisEngine.instance.instrumentationService.isActive) {
499 blankslate('Instrumentation not active.');
500 return;
501 }
502
503 h3('Instrumentation');
504
505 p('Instrumentation active.');
506
507 InstrumentationServer instrumentation =
508 AnalysisEngine.instance.instrumentationService.instrumentationServer;
509 String description = instrumentation.describe;
510 HtmlEscape htmlEscape = new HtmlEscape(HtmlEscapeMode.ELEMENT);
511 description = htmlEscape.convert(description);
512 // Convert http(s): references to hyperlinks.
513 final RegExp urlRegExp = new RegExp(r'[http|https]+:\/*(\S+)');
514 description = description.replaceAllMapped(urlRegExp, (Match match) {
515 return '<a href="${match.group(0)}">${match.group(1)}</a>';
516 });
517 p(description.replaceAll('\n', '<br>'), raw: true);
518 }
519 }
520
521 class ProfilePage extends DiagnosticPageWithNav {
522 ProfilePage(DiagnosticsSite site)
523 : super(site, 'profile', 'Profiling Info',
524 description: 'Profiling performance tag data.');
525
526 @override
527 void generateContent(Map<String, String> params) {
528 // prepare sorted tags
529 List<PerformanceTag> tags = PerformanceTag.all.toList();
530 tags.remove(ServerPerformanceStatistics.idle);
531 tags.remove(PerformanceTag.unknown);
532 tags.removeWhere((tag) => tag.elapsedMs == 0);
533 tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
534
535 // draw a pie chart
536 String rowData =
537 tags.map((tag) => "['${tag.label}', ${tag.elapsedMs}]").join(',');
538 buf.writeln(
539 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
540 buf.writeln('''
541 <script type="text/javascript">
542 google.charts.load('current', {'packages':['corechart']});
543 google.charts.setOnLoadCallback(drawChart);
544
545 function drawChart() {
546 var data = new google.visualization.DataTable();
547 data.addColumn('string', 'Tag');
548 data.addColumn('number', 'Time (ms)');
549 data.addRows([$rowData]);
550 var options = {'title': 'Performance Tag Data', 'width': 700, 'height' : 300};
551 var chart = new google.visualization.PieChart(document.getElementById( 'chart-div'));
552 chart.draw(data, options);
553 }
554 </script>
555 ''');
556
557 // print total time
558 int totalTime =
559 tags.fold<int>(0, (int a, PerformanceTag tag) => a + tag.elapsedMs);
560 p('Total measured time: ${printMilliseconds(totalTime)}');
561
562 // write out a table
563 void _writeRow(List<String> data, {bool header: false}) {
564 buf.write('<tr>');
565 if (header) {
566 for (String d in data) {
567 buf.write('<th>$d</th>');
568 }
569 } else {
570 buf.write('<td>${data[0]}</td>');
571
572 for (String d in data.sublist(1)) {
573 buf.write('<td class="right">$d</td>');
574 }
575 }
576 buf.writeln('</tr>');
577 }
578
579 buf.write('<table>');
580 _writeRow(['Tag name', 'Time (in ms)', 'Percent'], header: true);
581 void writeRow(PerformanceTag tag) {
582 double percent = tag.elapsedMs / totalTime;
583 _writeRow([
584 tag.label,
585 printMilliseconds(tag.elapsedMs),
586 printPercentage(percent)
587 ]);
588 }
589
590 tags.forEach(writeRow);
591 buf.write('</table>');
592 }
593 }
594
595 class ExceptionsPage extends DiagnosticPageWithNav { 769 class ExceptionsPage extends DiagnosticPageWithNav {
596 ExceptionsPage(DiagnosticsSite site) 770 ExceptionsPage(DiagnosticsSite site)
597 : super(site, 'exceptions', 'Exceptions', 771 : super(site, 'exceptions', 'Exceptions',
598 description: 'Exceptions from the analysis server.'); 772 description: 'Exceptions from the analysis server.');
599 773
774 Iterable<ServerException> get exceptions => server.exceptions.items;
775
600 String get navDetail => printInteger(exceptions.length); 776 String get navDetail => printInteger(exceptions.length);
601 777
602 @override 778 @override
603 void generateContent(Map<String, String> params) { 779 void generateContent(Map<String, String> params) {
604 if (exceptions.isEmpty) { 780 if (exceptions.isEmpty) {
605 blankslate('No exceptions encountered!'); 781 blankslate('No exceptions encountered!');
606 } else { 782 } else {
607 for (ServerException ex in exceptions) { 783 for (ServerException ex in exceptions) {
608 h3('Exception ${ex.exception}'); 784 h3('Exception ${ex.exception}');
609 p('${escape(ex.message)}<br>${writeOption('fatal', ex.fatal)}', 785 p('${escape(ex.message)}<br>${writeOption('fatal', ex.fatal)}',
610 raw: true); 786 raw: true);
611 pre(() { 787 pre(() {
612 buf.writeln('<code>${escape(ex.stackTrace.toString())}</code>'); 788 buf.writeln('<code>${escape(ex.stackTrace.toString())}</code>');
613 }, classes: "scroll-table"); 789 }, classes: "scroll-table");
614 } 790 }
615 } 791 }
616 } 792 }
617
618 Iterable<ServerException> get exceptions => server.exceptions.items;
619 } 793 }
620 794
621 class ContextsPage extends DiagnosticPageWithNav { 795 class FeedbackPage extends DiagnosticPage {
622 ContextsPage(DiagnosticsSite site) 796 FeedbackPage(DiagnosticsSite site)
623 : super(site, 'contexts', 'Contexts', 797 : super(site, 'feedback', 'Feedback',
624 description: 798 description: 'Providing feedback and filing issues.');
625 'An analysis context defines the options and the set of sources being analyzed.');
626
627 String get navDetail => printInteger(server.driverMap.length);
628 799
629 @override 800 @override
630 void generateContent(Map<String, String> params) { 801 void generateContent(Map<String, String> params) {
631 Map<Folder, AnalysisDriver> driverMap = server.driverMap; 802 final String issuesUrl = 'https://github.com/dart-lang/sdk/issues';
632 if (driverMap.isEmpty) { 803 p(
633 blankslate('No contexts.'); 804 'To file issues or feature requests, see our '
805 '<a href="$issuesUrl">bug tracker</a>. When filing an issue, please de scribe:',
806 raw: true,
807 );
808 ul([
809 'what you were doing',
810 'what occured',
811 'what you think the expected behavior should have been',
812 ], (line) => buf.writeln(line));
813
814 List<String> ideInfo = [];
815 if (server.options.clientId != null) {
816 ideInfo.add(server.options.clientId);
817 }
818 if (server.options.clientVersion != null) {
819 ideInfo.add(server.options.clientVersion);
820 }
821 String ideText = ideInfo.map((str) => '<code>$str</code>').join(', ');
822
823 p('Other data to include:');
824 ul([
825 "the IDE you are using and it's version${ideText.isEmpty ? '' : ' ($ideTex t)'}",
826 'the Dart SDK version (<code>${escape(_sdkVersion)}</code>)',
827 'your operating system (<code>${escape(Platform.operatingSystem)}</code>)' ,
828 ], (line) => buf.writeln(line));
829
830 p('Thanks!');
831 }
832 }
833
834 class InstrumentationPage extends DiagnosticPageWithNav {
835 InstrumentationPage(DiagnosticsSite site)
836 : super(site, 'instrumentation', 'Instrumentation',
837 description:
838 'Verbose instrumentation data from the analysis server.');
839
840 @override
841 void generateContent(Map<String, String> params) {
842 p(
843 'Instrumentation can be enabled by starting the analysis server with the '
844 '<code>--instrumentation-log-file=path/to/file</code> flag.',
845 raw: true);
846
847 if (!AnalysisEngine.instance.instrumentationService.isActive) {
848 blankslate('Instrumentation not active.');
634 return; 849 return;
635 } 850 }
636 851
637 String contextPath = params['context']; 852 h3('Instrumentation');
638 List<Folder> folders = driverMap.keys.toList();
639 folders
640 .sort((first, second) => first.shortName.compareTo(second.shortName));
641 Folder folder =
642 folders.firstWhere((f) => f.path == contextPath, orElse: () => null);
643 853
644 if (folder == null) { 854 p('Instrumentation active.');
645 folder = folders.first;
646 contextPath = folder.path;
647 }
648 855
649 AnalysisDriver driver = driverMap[folder]; 856 InstrumentationServer instrumentation =
650 857 AnalysisEngine.instance.instrumentationService.instrumentationServer;
651 buf.writeln('<div class="tabnav">'); 858 String description = instrumentation.describe;
652 buf.writeln('<nav class="tabnav-tabs">'); 859 HtmlEscape htmlEscape = new HtmlEscape(HtmlEscapeMode.ELEMENT);
653 for (Folder f in folders) { 860 description = htmlEscape.convert(description);
654 if (f == folder) { 861 // Convert http(s): references to hyperlinks.
655 buf.writeln( 862 final RegExp urlRegExp = new RegExp(r'[http|https]+:\/*(\S+)');
656 '<a class="tabnav-tab selected">${escape(f.shortName)}</a>'); 863 description = description.replaceAllMapped(urlRegExp, (Match match) {
657 } else { 864 return '<a href="${match.group(0)}">${match.group(1)}</a>';
658 String p = '$path?context=${Uri.encodeQueryComponent(f.path)}'; 865 });
659 buf.writeln( 866 p(description.replaceAll('\n', '<br>'), raw: true);
660 '<a href="$p" class="tabnav-tab">${escape(f.shortName)}</a>');
661 }
662 }
663 buf.writeln('</nav>');
664 buf.writeln('</div>');
665
666 buf.writeln(writeOption('Context location', escape(contextPath)));
667 buf.writeln(writeOption('Analysis options path',
668 escape(driver.contextRoot.optionsFilePath ?? 'none')));
669
670 buf.writeln('<div class="columns">');
671
672 buf.writeln('<div class="column one-half">');
673 h3('Analysis options');
674 p(describe(driver.analysisOptions), raw: true);
675 buf.writeln(
676 writeOption('Has .packages file', folder.getChild('.packages').exists));
677 buf.writeln(writeOption(
678 'Has pubspec.yaml file', folder.getChild('pubspec.yaml').exists));
679 buf.writeln('</div>');
680
681 buf.writeln('<div class="column one-half">');
682 DartSdk sdk = driver?.sourceFactory?.dartSdk;
683 AnalysisOptionsImpl sdkOptions = sdk?.context?.analysisOptions;
684 if (sdkOptions != null) {
685 h3('SDK analysis options');
686 p(describe(sdkOptions), raw: true);
687
688 if (sdk is FolderBasedDartSdk) {
689 p(writeOption('Use summaries', sdk.useSummary), raw: true);
690 }
691 }
692 buf.writeln('</div>');
693
694 buf.writeln('</div>');
695
696 h3('Lints');
697 p(driver.analysisOptions.lintRules.map((l) => l.name).join(', '));
698
699 h3('Error processors');
700 p(driver.analysisOptions.errorProcessors
701 .map((e) => e.description)
702 .join(', '));
703
704 List<String> priorityFiles = driver.priorityFiles;
705 List<String> addedFiles = driver.addedFiles.toList();
706 List<String> implicitFiles =
707 driver.knownFiles.difference(driver.addedFiles).toList();
708 addedFiles.sort();
709 implicitFiles.sort();
710
711 String lenCounter(List list) {
712 return '<span class="counter" style="float: right;">${list.length}</span>' ;
713 }
714
715 h3('Context files');
716
717 void writeFile(String file) {
718 String astPath = '/ast?file=${Uri.encodeQueryComponent(file)}';
719 String elementPath = '/element?file=${Uri.encodeQueryComponent(file)}';
720
721 buf.write(file);
722 buf.write(' (');
723 buf.writeln('<a href="$astPath">ast</a>');
724 buf.write(' ');
725 buf.writeln('<a href="$elementPath">element</a>');
726 buf.write(')');
727 }
728
729 h4('Priority files ${lenCounter(priorityFiles)}', raw: true);
730 ul(priorityFiles, writeFile, classes: 'scroll-table');
731
732 h4('Added files ${lenCounter(addedFiles)}', raw: true);
733 ul(addedFiles, writeFile, classes: 'scroll-table');
734
735 h4('ImplicitFiles files ${lenCounter(implicitFiles)}', raw: true);
736 ul(implicitFiles, writeFile, classes: 'scroll-table');
737
738 SourceFactory sourceFactory = driver.sourceFactory;
739 if (sourceFactory is SourceFactoryImpl) {
740 h3('Resolvers');
741 for (UriResolver resolver in sourceFactory.resolvers) {
742 h4(resolver.runtimeType.toString());
743 buf.write('<p class="scroll-table">');
744 if (resolver is DartUriResolver) {
745 DartSdk sdk = resolver.dartSdk;
746 buf.write(' (sdk = ');
747 buf.write(sdk.runtimeType);
748 if (sdk is FolderBasedDartSdk) {
749 buf.write(' (path = ');
750 buf.write(sdk.directory.path);
751 buf.write(')');
752 } else if (sdk is EmbedderSdk) {
753 buf.write(' (map = ');
754 writeMap(sdk.urlMappings);
755 buf.write(')');
756 }
757 buf.write(')');
758 } else if (resolver is SdkExtUriResolver) {
759 buf.write(' (map = ');
760 writeMap(resolver.urlMappings);
761 buf.write(')');
762 } else if (resolver is PackageMapUriResolver) {
763 writeMap(resolver.packageMap);
764 }
765 buf.write('</p>');
766 }
767 }
768 }
769
770 String describe(AnalysisOptionsImpl options) {
771 StringBuffer b = new StringBuffer();
772
773 b.write(
774 writeOption('Analyze function bodies', options.analyzeFunctionBodies));
775 b.write(writeOption('Enable asserts in initializer lists',
776 options.enableAssertInitializer));
777 b.write(writeOption(
778 'Enable strict call checks', options.enableStrictCallChecks));
779 b.write(writeOption('Enable super mixins', options.enableSuperMixins));
780 b.write(writeOption('Generate dart2js hints', options.dart2jsHint));
781 b.write(writeOption(
782 'Generate errors in implicit files', options.generateImplicitErrors));
783 b.write(
784 writeOption('Generate errors in SDK files', options.generateSdkErrors));
785 b.write(writeOption('Generate hints', options.hint));
786 b.write(writeOption('Incremental resolution', options.incremental));
787 b.write(writeOption(
788 'Incremental resolution with API changes', options.incrementalApi));
789 b.write(writeOption('Preserve comments', options.preserveComments));
790 b.write(writeOption('Strong mode', options.strongMode));
791 b.write(writeOption('Strong mode hints', options.strongModeHints));
792
793 return b.toString();
794 }
795
796 void writeList<E>(List<E> list) {
797 buf.writeln('[${list.join(', ')}]');
798 }
799
800 void writeMap<V>(Map<String, V> map) {
801 List<String> keys = map.keys.toList();
802 keys.sort();
803 int length = keys.length;
804 buf.write('{');
805 for (int i = 0; i < length; i++) {
806 buf.write('<br>');
807 String key = keys[i];
808 V value = map[key];
809 buf.write(key);
810 buf.write(' = ');
811 if (value is List) {
812 writeList(value);
813 } else {
814 buf.write(value);
815 }
816 buf.write(',');
817 }
818 buf.write('<br>}');
819 } 867 }
820 } 868 }
821 869
822 class MemoryAndCpuPage extends DiagnosticPageWithNav { 870 class MemoryAndCpuPage extends DiagnosticPageWithNav {
823 final ProcessProfiler profiler; 871 final ProcessProfiler profiler;
824 872
825 MemoryAndCpuPage(DiagnosticsSite site, this.profiler) 873 MemoryAndCpuPage(DiagnosticsSite site, this.profiler)
826 : super(site, 'memory', 'Memory and CPU Usage', 874 : super(site, 'memory', 'Memory and CPU Usage',
827 description: 'Memory and CPU usage for the analysis server.'); 875 description: 'Memory and CPU usage for the analysis server.');
828 876
877 DiagnosticDomainHandler get diagnosticDomain {
878 return server.handlers
879 .firstWhere((handler) => handler is DiagnosticDomainHandler);
880 }
881
829 @override 882 @override
830 void generateContent(Map<String, String> params) { 883 void generateContent(Map<String, String> params) {
831 UsageInfo usage = profiler.getProcessUsageSync(pid); 884 UsageInfo usage = profiler.getProcessUsageSync(pid);
832 if (usage != null) { 885 if (usage != null) {
833 buf.writeln( 886 buf.writeln(
834 writeOption('CPU', printPercentage(usage.cpuPercentage / 100.0))); 887 writeOption('CPU', printPercentage(usage.cpuPercentage / 100.0)));
835 buf.writeln( 888 buf.writeln(
836 writeOption('Memory', '${printInteger(usage.memoryMB.round())} MB')); 889 writeOption('Memory', '${printInteger(usage.memoryMB.round())} MB'));
837 } else { 890 } else {
838 p('Error retreiving the memory and cpu usage information.'); 891 p('Error retreiving the memory and cpu usage information.');
839 } 892 }
840 } 893 }
894 }
841 895
842 DiagnosticDomainHandler get diagnosticDomain { 896 class NotFoundPage extends DiagnosticPage {
843 return server.handlers 897 final String path;
844 .firstWhere((handler) => handler is DiagnosticDomainHandler); 898
845 } 899 NotFoundPage(Site site, this.path)
900 : super(site, '', '404 Not found', description: "'$path' not found.");
901
902 void generateContent(Map<String, String> params) {}
846 } 903 }
847 904
848 class OverlaysPage extends DiagnosticPageWithNav { 905 class OverlaysPage extends DiagnosticPageWithNav {
849 OverlaysPage(DiagnosticsSite site) 906 OverlaysPage(DiagnosticsSite site)
850 : super(site, 'overlays', 'Overlays', 907 : super(site, 'overlays', 'Overlays',
851 description: 'Editing overlays - unsaved file changes.'); 908 description: 'Editing overlays - unsaved file changes.');
852 909
853 @override 910 @override
854 void generateContent(Map<String, String> params) { 911 void generateContent(Map<String, String> params) {
855 FileContentOverlay overlays = server.fileContentOverlay; 912 FileContentOverlay overlays = server.fileContentOverlay;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
913 970
914 List<Plugin> plugins = [ 971 List<Plugin> plugins = [
915 AnalysisEngine.instance.enginePlugin, 972 AnalysisEngine.instance.enginePlugin,
916 server.serverPlugin 973 server.serverPlugin
917 ]; 974 ];
918 plugins.addAll(server.userDefinedPlugins); 975 plugins.addAll(server.userDefinedPlugins);
919 ul(plugins, writePlugin); 976 ul(plugins, writePlugin);
920 } 977 }
921 } 978 }
922 979
980 // TODO(devoncarew): Show the last x requests and responses.
981 class ProfilePage extends DiagnosticPageWithNav {
982 ProfilePage(DiagnosticsSite site)
983 : super(site, 'profile', 'Profiling Info',
984 description: 'Profiling performance tag data.');
985
986 @override
987 void generateContent(Map<String, String> params) {
988 // prepare sorted tags
989 List<PerformanceTag> tags = PerformanceTag.all.toList();
990 tags.remove(ServerPerformanceStatistics.idle);
991 tags.remove(PerformanceTag.unknown);
992 tags.removeWhere((tag) => tag.elapsedMs == 0);
993 tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
994
995 // draw a pie chart
996 String rowData =
997 tags.map((tag) => "['${tag.label}', ${tag.elapsedMs}]").join(',');
998 buf.writeln(
999 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
1000 buf.writeln('''
1001 <script type="text/javascript">
1002 google.charts.load('current', {'packages':['corechart']});
1003 google.charts.setOnLoadCallback(drawChart);
1004
1005 function drawChart() {
1006 var data = new google.visualization.DataTable();
1007 data.addColumn('string', 'Tag');
1008 data.addColumn('number', 'Time (ms)');
1009 data.addRows([$rowData]);
1010 var options = {'title': 'Performance Tag Data', 'width': 700, 'height' : 300};
1011 var chart = new google.visualization.PieChart(document.getElementById( 'chart-div'));
1012 chart.draw(data, options);
1013 }
1014 </script>
1015 ''');
1016
1017 // print total time
1018 int totalTime =
1019 tags.fold<int>(0, (int a, PerformanceTag tag) => a + tag.elapsedMs);
1020 p('Total measured time: ${printMilliseconds(totalTime)}');
1021
1022 // write out a table
1023 void _writeRow(List<String> data, {bool header: false}) {
1024 buf.write('<tr>');
1025 if (header) {
1026 for (String d in data) {
1027 buf.write('<th>$d</th>');
1028 }
1029 } else {
1030 buf.write('<td>${data[0]}</td>');
1031
1032 for (String d in data.sublist(1)) {
1033 buf.write('<td class="right">$d</td>');
1034 }
1035 }
1036 buf.writeln('</tr>');
1037 }
1038
1039 buf.write('<table>');
1040 _writeRow(['Tag name', 'Time (in ms)', 'Percent'], header: true);
1041 void writeRow(PerformanceTag tag) {
1042 double percent = tag.elapsedMs / totalTime;
1043 _writeRow([
1044 tag.label,
1045 printMilliseconds(tag.elapsedMs),
1046 printPercentage(percent)
1047 ]);
1048 }
1049
1050 tags.forEach(writeRow);
1051 buf.write('</table>');
1052 }
1053 }
1054
1055 class StatusPage extends DiagnosticPageWithNav {
1056 StatusPage(DiagnosticsSite site)
1057 : super(site, 'status', 'Status',
1058 description:
1059 'General status and diagnostics for the analysis server.');
1060
1061 @override
1062 void generateContent(Map<String, String> params) {
1063 buf.writeln('<div class="columns">');
1064
1065 buf.writeln('<div class="column one-half">');
1066 h3('Status');
1067 buf.writeln(writeOption(
1068 'New analysis driver enabled', server.options.enableNewAnalysisDriver));
1069 buf.writeln(writeOption('Instrumentation enabled',
1070 AnalysisEngine.instance.instrumentationService.isActive));
1071 buf.writeln(writeOption('Server process ID', pid));
1072 buf.writeln('</div>');
1073
1074 buf.writeln('<div class="column one-half">');
1075 h3('Versions');
1076 buf.writeln(writeOption('Analysis server version', AnalysisServer.VERSION));
1077 buf.writeln(writeOption('Dart SDK', Platform.version));
1078 buf.writeln('</div>');
1079
1080 buf.writeln('</div>');
1081
1082 List<String> lines = (site as DiagnosticsSite).lastPrintedLines;
1083 if (lines.isNotEmpty) {
1084 h3('Debug output');
1085 p(lines.join('\n'), style: 'white-space: pre');
1086 }
1087 }
1088 }
1089
923 class SubscriptionsPage extends DiagnosticPageWithNav { 1090 class SubscriptionsPage extends DiagnosticPageWithNav {
924 SubscriptionsPage(DiagnosticsSite site) 1091 SubscriptionsPage(DiagnosticsSite site)
925 : super(site, 'subscriptions', 'Subscriptions', 1092 : super(site, 'subscriptions', 'Subscriptions',
926 description: 'Registered subscriptions to analysis server events.'); 1093 description: 'Registered subscriptions to analysis server events.');
927 1094
928 @override 1095 @override
929 void generateContent(Map<String, String> params) { 1096 void generateContent(Map<String, String> params) {
930 // server domain 1097 // server domain
931 h3('Server domain subscriptions'); 1098 h3('Server domain subscriptions');
932 ul(ServerService.VALUES, (item) { 1099 ul(ServerService.VALUES, (item) {
(...skipping 21 matching lines...) Expand all
954 h3('Execution domain'); 1121 h3('Execution domain');
955 ul(ExecutionService.VALUES, (item) { 1122 ul(ExecutionService.VALUES, (item) {
956 if (domain.onFileAnalyzed != null) { 1123 if (domain.onFileAnalyzed != null) {
957 buf.write('$item (has subscriptions)'); 1124 buf.write('$item (has subscriptions)');
958 } else { 1125 } else {
959 buf.write('$item (no subscriptions)'); 1126 buf.write('$item (no subscriptions)');
960 } 1127 }
961 }); 1128 });
962 } 1129 }
963 } 1130 }
964
965 class CompletionPage extends DiagnosticPageWithNav {
966 CompletionPage(DiagnosticsSite site)
967 : super(site, 'completion', 'Code Completion',
968 description: 'Latency statistics for code completion.');
969
970 @override
971 void generateContent(Map<String, String> params) {
972 CompletionDomainHandler completionDomain = server.handlers
973 .firstWhere((handler) => handler is CompletionDomainHandler);
974
975 List<CompletionPerformance> completions =
976 completionDomain.performanceList.items.toList();
977
978 if (completions.isEmpty) {
979 blankslate('No completions recorded.');
980 return;
981 }
982
983 int fastCount =
984 completions.where((c) => c.elapsedInMilliseconds <= 100).length;
985 p('${completions.length} results; ${printPercentage(fastCount / completions. length)} within 100ms.');
986
987 // draw a chart
988 buf.writeln(
989 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
990 StringBuffer rowData = new StringBuffer();
991 for (int i = completions.length - 1; i >= 0; i--) {
992 // [' ', 101.5]
993 if (rowData.isNotEmpty) {
994 rowData.write(',');
995 }
996 rowData.write("[' ', ${completions[i].elapsedInMilliseconds}]");
997 }
998 buf.writeln('''
999 <script type="text/javascript">
1000 google.charts.load('current', {'packages':['bar']});
1001 google.charts.setOnLoadCallback(drawChart);
1002 function drawChart() {
1003 var data = google.visualization.arrayToDataTable([
1004 ['Completions', 'Time'],
1005 $rowData
1006 ]);
1007 var options = { bars: 'vertical', vAxis: {format: 'decimal'}, height: 30 0 };
1008 var chart = new google.charts.Bar(document.getElementById('chart-div'));
1009 chart.draw(data, google.charts.Bar.convertOptions(options));
1010 }
1011 </script>
1012 ''');
1013
1014 // emit the data as a table
1015 buf.writeln('<table>');
1016 buf.writeln(
1017 '<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>') ;
1018 for (CompletionPerformance completion in completions) {
1019 buf.writeln('<tr>'
1020 '<td class="pre right">${printMilliseconds(completion.elapsedInMillise conds)}</td>'
1021 '<td class="right">${completion.suggestionCount}</td>'
1022 '<td>${escape(completion.source.shortName)}</td>'
1023 '<td><code>${escape(completion.snippet)}</code></td>'
1024 '</tr>');
1025 }
1026 buf.writeln('</table>');
1027 }
1028 }
1029
1030 // TODO(devoncarew): Show the last x requests and responses.
1031 class CommunicationsPage extends DiagnosticPageWithNav {
1032 CommunicationsPage(DiagnosticsSite site)
1033 : super(site, 'communications', 'Communications',
1034 description:
1035 'Latency statistics for analysis server communications.');
1036
1037 @override
1038 void generateContent(Map<String, String> params) {
1039 void writeRow(List<String> data, {List<String> classes}) {
1040 buf.write("<tr>");
1041 for (int i = 0; i < data.length; i++) {
1042 String c = classes == null ? null : classes[i];
1043 if (c != null) {
1044 buf.write('<td class="$c">${escape(data[i])}</td>');
1045 } else {
1046 buf.write('<td>${escape(data[i])}</td>');
1047 }
1048 }
1049 buf.writeln("</tr>");
1050 }
1051
1052 buf.writeln('<div class="columns">');
1053
1054 ServerPerformance perf = server.performanceAfterStartup;
1055 if (perf != null) {
1056 buf.writeln('<div class="column one-half">');
1057 h3('Current');
1058
1059 int requestCount = perf.requestCount;
1060 int averageLatency =
1061 requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
1062 int maximumLatency = perf.maxLatency;
1063 double slowRequestPercent =
1064 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
1065
1066 buf.write('<table>');
1067 writeRow([printInteger(requestCount), 'requests'],
1068 classes: ["right", null]);
1069 writeRow([printMilliseconds(averageLatency), 'average latency'],
1070 classes: ["right", null]);
1071 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
1072 classes: ["right", null]);
1073 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
1074 classes: ["right", null]);
1075 buf.write('</table>');
1076
1077 String time = server.uptime.toString();
1078 if (time.contains('.')) {
1079 time = time.substring(0, time.indexOf('.'));
1080 }
1081 buf.writeln(writeOption('Uptime', time));
1082
1083 buf.write('</div>');
1084 }
1085
1086 buf.writeln('<div class="column one-half">');
1087 h3('Startup');
1088 perf = server.performanceDuringStartup;
1089
1090 int requestCount = perf.requestCount;
1091 int averageLatency =
1092 requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
1093 int maximumLatency = perf.maxLatency;
1094 double slowRequestPercent =
1095 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
1096
1097 buf.write('<table>');
1098 writeRow([printInteger(requestCount), 'requests'],
1099 classes: ["right", null]);
1100 writeRow([printMilliseconds(averageLatency), 'average latency'],
1101 classes: ["right", null]);
1102 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
1103 classes: ["right", null]);
1104 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
1105 classes: ["right", null]);
1106 buf.write('</table>');
1107
1108 if (server.performanceAfterStartup != null) {
1109 int startupTime =
1110 server.performanceAfterStartup.startTime - perf.startTime;
1111 buf.writeln(
1112 writeOption('Initial analysis time', printMilliseconds(startupTime)));
1113 }
1114 buf.write('</div>');
1115
1116 buf.write('</div>');
1117 }
1118 }
1119
1120 String writeOption(String name, dynamic value) {
1121 return '$name: <code>$value</code><br> ';
1122 }
1123
1124 String get _sdkVersion {
1125 String version = Platform.version;
1126 if (version.contains(' ')) {
1127 version = version.substring(0, version.indexOf(' '));
1128 }
1129 return version;
1130 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/lib/src/constants.dart ('k') | pkg/analysis_server/lib/src/status/pages.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698