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

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

Issue 2911313002: Update the analysis server diagnostic page - re-enable code completion and overlay pages, (Closed)
Patch Set: small tweaks 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:convert';
6 import 'dart:io';
7
8 import 'package:analysis_server/protocol/protocol_generated.dart';
9 import 'package:analysis_server/src/analysis_server.dart';
10 import 'package:analysis_server/src/domain_completion.dart';
11 import 'package:analysis_server/src/domain_execution.dart';
12 import 'package:analysis_server/src/plugin/plugin_manager.dart';
13 import 'package:analysis_server/src/server/http_server.dart';
14 import 'package:analysis_server/src/services/completion/completion_performance.d art';
15 import 'package:analysis_server/src/socket_server.dart';
16 import 'package:analysis_server/src/status/pages.dart';
17 import 'package:analyzer/exception/exception.dart';
18 import 'package:analyzer/file_system/file_system.dart';
19 import 'package:analyzer/instrumentation/instrumentation.dart';
20 import 'package:analyzer/source/package_map_resolver.dart';
21 import 'package:analyzer/source/sdk_ext.dart';
22 import 'package:analyzer/src/context/source.dart';
23 import 'package:analyzer/src/dart/analysis/driver.dart';
24 import 'package:analyzer/src/dart/analysis/file_state.dart';
25 import 'package:analyzer/src/dart/sdk/sdk.dart';
26 import 'package:analyzer/src/generated/engine.dart';
27 import 'package:analyzer/src/generated/sdk.dart';
28 import 'package:analyzer/src/generated/source.dart';
29 import 'package:analyzer/src/generated/utilities_general.dart';
30 import 'package:plugin/plugin.dart';
31
32 final String kCustomCss = '''
33 .lead, .page-title+.markdown-body>p:first-child {
34 margin-bottom: 30px;
35 font-size: 20px;
36 font-weight: 300;
37 color: #555;
38 }
39
40 .container {
41 width: 1160px;
42 }
43
44 .masthead {
45 padding-top: 1rem;
46 padding-bottom: 1rem;
47 margin-bottom: 1.5rem;
48 text-align: center;
49 background-color: #4078c0;
50 }
51
52 .masthead .masthead-logo {
53 display: inline-block;
54 font-size: 1.5rem;
55 color: #fff;
56 float: left;
57 }
58
59 .masthead-nav {
60 float: right;
61 margin-top: .5rem;
62 }
63
64 .masthead-nav a:not(:last-child) {
65 margin-right: 1.25rem;
66 }
67
68 .masthead a {
69 color: rgba(255,255,255,0.5);
70 font-size: 1rem;
71 }
72
73 .masthead a:hover {
74 color: #fff;
75 text-decoration: none;
76 }
77
78 .masthead-nav .active {
79 color: #fff;
80 font-weight: 500;
81 }
82
83 .counter {
84 display: inline-block;
85 padding: 2px 5px;
86 font-size: 11px;
87 font-weight: bold;
88 line-height: 1;
89 color: #666;
90 background-color: #eee;
91 border-radius: 20px;
92 }
93
94 .menu-item .counter {
95 float: right;
96 margin-left: 5px;
97 }
98
99 td.right {
100 text-align: right;
101 }
102
103 td.pre {
104 white-space: pre;
105 }
106
107 .nowrap {
108 white-space: nowrap;
109 }
110
111 .footer {
112 padding-top: 3rem;
113 padding-bottom: 3rem;
114 margin-top: 3rem;
115 line-height: 1.75;
116 color: #7a7a7a;
117 border-top: 1px solid #eee;
118 }
119
120 .footer strong {
121 color: #333;
122 }
123 ''';
124
125 class DiagnosticsSite extends Site implements AbstractGetHandler {
126 /// An object that can handle either a WebSocket connection or a connection
127 /// to the client over stdio.
128 SocketServer socketServer;
129
130 /// The last few lines printed.
131 List<String> lastPrintedLines = <String>[];
132
133 DiagnosticsSite(this.socketServer, this.lastPrintedLines)
134 : super('Analysis Server') {
135 pages.add(new CompletionPage(this));
136 pages.add(new CommunicationsPage(this));
137 pages.add(new ContextsPage(this));
138 pages.add(new ExecutionDomainPage(this));
139 pages.add(new OverlaysPage(this));
140 pages.add(new ProfilePage(this));
141 //pages.add(new ExceptionsPage(this));
142 pages.add(new InstrumentationPage(this));
143 pages.add(new PluginsPage(this));
144
145 pages.sort(((Page a, Page b) =>
146 a.title.toLowerCase().compareTo(b.title.toLowerCase())));
147
148 // Add the status page at the beginning.
149 pages.insert(0, new StatusPage(this));
150
151 // Add non-nav pages.
152 pages.add(new FeedbackPage(this));
153 }
154
155 String get customCss => kCustomCss;
156
157 Page createUnknownPage(String unknownPath) =>
158 new NotFoundPage(this, unknownPath);
159
160 Page createExceptionPage(String message, StackTrace trace) =>
161 new ExceptionPage(this, message, trace);
162 }
163
164 /// A page with a proscriptive notion of layout.
165 abstract class DiagnosticPage extends Page {
166 final Site site;
167
168 DiagnosticPage(this.site, String id, String title, {String description})
169 : super(id, title, description: description);
170
171 AnalysisServer get server =>
172 (site as DiagnosticsSite).socketServer.analysisServer;
173
174 void generatePage(Map<String, String> params) {
175 buf.writeln('<!DOCTYPE html><html lang="en">');
176 buf.write('<head>');
177 buf.write('<meta charset="utf-8">');
178 buf.write('<meta name="viewport" content="width=device-width, '
179 'initial-scale=1.0">');
180 buf.writeln('<title>${site.title}</title>');
181 buf.writeln('<link rel="stylesheet" '
182 'href="https://cdnjs.cloudflare.com/ajax/libs/Primer/6.0.0/build.css">') ;
183 buf.writeln('<link rel="stylesheet" '
184 'href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octico ns.css">');
185 buf.writeln('<script type="text/javascript" '
186 'src="https://www.gstatic.com/charts/loader.js"></script>');
187 buf.writeln('<style>${site.customCss}</style>');
188 buf.writeln('</head>');
189
190 buf.writeln('<body>');
191 generateHeader();
192 buf.writeln('<div class="container">');
193 generateContainer(params);
194 generateFooter();
195 buf.writeln('</div>'); // div.container
196 buf.writeln('</body>');
197 buf.writeln('</html>');
198 }
199
200 void generateHeader() {
201 buf.writeln('''
202 <header class="masthead">
203 <div class="container">
204 <span class="masthead-logo">
205 <span class="mega-octicon octicon-database"></span>
206 ${site.title} Diagnostics
207 </span>
208
209 <nav class="masthead-nav">
210 <a href="/status" ${isNavPage ? ' class="active"' : ''}>Diagnostics</a>
211 <a href="/feedback" ${isCurrentPage('/feedback') ? ' class="active"' : ' '}>Feedback</a>
212 <a href="https://www.dartlang.org/tools/analyzer" target="_blank">Docs</ a>
213 <a href="https://htmlpreview.github.io/?https://github.com/dart-lang/sdk /blob/master/pkg/analysis_server/doc/api.html" target="_blank">Spec</a>
214 </nav>
215 </div>
216 </header>
217 ''');
218 }
219
220 void generateContainer(Map<String, String> params) {
221 buf.writeln('<div class="columns docs-layout">');
222 buf.writeln('<div class="three-fourths column markdown-body">');
223 h1(title, classes: 'page-title');
224 div(() {
225 p(description);
226 generateContent(params);
227 }, classes: 'markdown-body');
228 buf.writeln('</div>');
229 buf.writeln('</div>');
230 }
231
232 void generateContent(Map<String, String> params);
233
234 void generateFooter() {
235 buf.writeln('''
236 <footer class="footer">
237 Dart ${site.title} <span style="float:right">SDK ${_sdkVersion}</span>
238 </footer>
239 ''');
240 }
241
242 bool get isNavPage => false;
243 }
244
245 abstract class DiagnosticPageWithNav extends DiagnosticPage {
246 DiagnosticPageWithNav(Site site, String id, String title,
247 {String description})
248 : super(site, id, title, description: description);
249
250 void generateContainer(Map<String, String> params) {
251 buf.writeln('<div class="columns docs-layout">');
252
253 buf.writeln('<div class="one-fifth column">');
254 buf.writeln('<nav class="menu docs-menu">');
255 for (Page page in site.pages.where((p) => p is DiagnosticPageWithNav)) {
256 buf.write('<a class="menu-item ${page == this ? ' selected' : ''}" '
257 'href="${page.path}">${escape(page.title)}');
258 String detail = (page as DiagnosticPageWithNav).navDetail;
259 if (detail != null) {
260 buf.write('<span class="counter">$detail</span>');
261 }
262 buf.writeln('</a>');
263 }
264 buf.writeln('</nav>');
265 buf.writeln('</div>');
266
267 buf.writeln('<div class="four-fifths column markdown-body">');
268 h1(title, classes: 'page-title');
269 div(() {
270 p(description);
271 generateContent(params);
272 }, classes: 'markdown-body');
273 buf.writeln('</div>');
274
275 buf.writeln('</div>');
276 }
277
278 String get navDetail => null;
279
280 bool get isNavPage => true;
281 }
282
283 class NotFoundPage extends DiagnosticPage {
284 final String path;
285
286 NotFoundPage(Site site, this.path)
287 : super(site, '', '404 Not found', description: "'$path' not found.");
288
289 void generateContent(Map<String, String> params) {}
290 }
291
292 class ExceptionPage extends DiagnosticPage {
293 final StackTrace trace;
294
295 ExceptionPage(Site site, String message, this.trace)
296 : super(site, '', '500 Oops', description: message);
297
298 void generateContent(Map<String, String> params) {
299 p(trace.toString(), style: 'white-space: pre');
300 }
301 }
302
303 class FeedbackPage extends DiagnosticPage {
304 FeedbackPage(DiagnosticsSite site)
305 : super(site, 'feedback', 'Feedback',
306 description: 'Providing feedback and filing issues.');
307
308 @override
309 void generateContent(Map<String, String> params) {
310 final String issuesUrl = 'https://github.com/dart-lang/sdk/issues';
311 p(
312 'To file issues or feature requests, see our '
313 '<a href="$issuesUrl">bug tracker</a>. When filing an issue, please de scribe:',
314 raw: true,
315 );
316 ul([
317 'what you were doing',
318 'what occured',
319 'what you think the expected behavior should have been',
320 ], (line) => buf.writeln(line));
321
322 p('Other data to include:');
323 ul([
324 "the IDE you are using and it's version",
325 'the Dart SDK version (<code>${escape(_sdkVersion)}</code>)',
326 'your operating system (<code>${escape(Platform.operatingSystem)}</code>)' ,
327 ], (line) => buf.writeln(line));
328
329 p('Thanks!');
330 }
331 }
332
333 class StatusPage extends DiagnosticPageWithNav {
334 StatusPage(DiagnosticsSite site)
335 : super(site, 'status', 'Status',
336 description:
337 'General status and diagnostics for the analysis server.');
338
339 @override
340 void generateContent(Map<String, String> params) {
341 buf.writeln('<div class="columns">');
342
343 buf.writeln('<div class="column one-half">');
344 h3('Status');
345 buf.writeln(writeOption(
346 'New analysis driver enabled', server.options.enableNewAnalysisDriver));
347 buf.writeln(writeOption('Instrumentation enabled',
348 AnalysisEngine.instance.instrumentationService.isActive));
349 buf.writeln(writeOption('Server process ID', pid));
350 buf.writeln('</div>');
351
352 buf.writeln('<div class="column one-half">');
353 h3('Versions');
354 buf.writeln(writeOption('Analysis server version', AnalysisServer.VERSION));
355 buf.writeln(writeOption('Dart SDK', Platform.version));
356 buf.writeln('</div>');
357
358 buf.writeln('</div>');
359
360 h3('Server domain subscriptions');
361 ul(ServerService.VALUES, (item) {
362 if (server.serverServices.contains(item)) {
363 buf.write('$item (has subscriptions)');
364 } else {
365 buf.write('$item (no subscriptions)');
366 }
367 });
368
369 h3('Analysis domain subscriptions');
370 for (AnalysisService service in AnalysisService.VALUES) {
371 buf.writeln('${service.name}<br>');
372 ul(server.analysisServices[service] ?? [], (item) {
373 buf.write('$item');
374 });
375 }
376
377 List<String> lines = (site as DiagnosticsSite).lastPrintedLines;
378 if (lines.isNotEmpty) {
379 h3('Debug output');
380 p(lines.join('\n'), style: 'white-space: pre');
381 }
382 }
383 }
384
385 class InstrumentationPage extends DiagnosticPageWithNav {
386 InstrumentationPage(DiagnosticsSite site)
387 : super(site, 'instrumentation', 'Instrumentation',
388 description:
389 'Verbose instrumentation data from the analysis server.');
390
391 @override
392 void generateContent(Map<String, String> params) {
393 p(
394 'Instrumentation can be enabled by starting the analysis server with the '
395 '<code>--instrumentation-log-file=path/to/file</code> flag.',
396 raw: true);
397
398 if (!AnalysisEngine.instance.instrumentationService.isActive) {
399 blankslate('Instrumentation not active.');
400 return;
401 }
402
403 h3('Instrumentation');
404
405 p('Instrumentation active.');
406
407 InstrumentationServer instrumentation =
408 AnalysisEngine.instance.instrumentationService.instrumentationServer;
409 String description = instrumentation.describe;
410 HtmlEscape htmlEscape = new HtmlEscape(HtmlEscapeMode.ELEMENT);
411 description = htmlEscape.convert(description);
412 // Convert http(s): references to hyperlinks.
413 final RegExp urlRegExp = new RegExp(r'[http|https]+:\/*(\S+)');
414 description = description.replaceAllMapped(urlRegExp, (Match match) {
415 return '<a href="${match.group(0)}">${match.group(1)}</a>';
416 });
417 p(description.replaceAll('\n', '<br>'), raw: true);
418 }
419 }
420
421 class ProfilePage extends DiagnosticPageWithNav {
422 ProfilePage(DiagnosticsSite site)
423 : super(site, 'profile', 'Profiling Info',
424 description: 'Profiling performance tag data.');
425
426 @override
427 void generateContent(Map<String, String> params) {
428 // prepare sorted tags
429 List<PerformanceTag> tags = PerformanceTag.all.toList();
430 tags.remove(ServerPerformanceStatistics.idle);
431 tags.removeWhere((tag) => tag.label == 'unknown');
432 tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
433
434 // draw a pie chart
435 String rowData =
436 tags.map((tag) => "['${tag.label}', ${tag.elapsedMs}]").join(',');
437 buf.writeln(
438 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
439 buf.writeln('''
440 <script type="text/javascript">
441 google.charts.load('current', {'packages':['corechart']});
442 google.charts.setOnLoadCallback(drawChart);
443
444 function drawChart() {
445 var data = new google.visualization.DataTable();
446 data.addColumn('string', 'Tag');
447 data.addColumn('number', 'Time (ms)');
448 data.addRows([$rowData]);
449 var options = {'title': 'Performance Tag Data', 'width': 700, 'height' : 300};
450 var chart = new google.visualization.PieChart(document.getElementById( 'chart-div'));
451 chart.draw(data, options);
452 }
453 </script>
454 ''');
455
456 // print total time
457 int totalTime =
458 tags.fold<int>(0, (int a, PerformanceTag tag) => a + tag.elapsedMs);
459 p('Total measured time: ${printMilliseconds(totalTime)}');
460
461 // write out a table
462 void _writeRow(List<String> data, {bool header: false}) {
463 buf.write('<tr>');
464 if (header) {
465 for (String d in data) {
466 buf.write('<th>$d</th>');
467 }
468 } else {
469 for (String d in data) {
470 buf.write('<td>$d</td>');
471 }
472 }
473 buf.writeln('</tr>');
474 }
475
476 buf.write('<table>');
477 _writeRow(['Tag name', 'Time (in ms)', 'Percent'], header: true);
478 void writeRow(PerformanceTag tag) {
479 double percent = tag.elapsedMs / totalTime;
480 _writeRow([
481 tag.label,
482 printMilliseconds(tag.elapsedMs),
483 printPercentage(percent)
484 ]);
485 }
486
487 tags.forEach(writeRow);
488 buf.write('</table>');
489 }
490 }
491
492 // TODO(devoncarew): This is not hooked up.
493 class ExceptionsPage extends DiagnosticPageWithNav {
494 ExceptionsPage(DiagnosticsSite site)
495 : super(site, 'exceptions', 'Exceptions',
496 description: 'Exceptions from the analysis server.');
497
498 String get navDetail => exceptions.length.toString();
499
500 @override
501 void generateContent(Map<String, String> params) {
502 if (exceptions.isEmpty) {
503 blankslate('No exceptions encountered!');
504 } else {
505 for (CaughtException ex in exceptions) {
506 h3('${ex.exception}');
507 p(ex.toString(), style: 'white-space: pre');
508 }
509 }
510 }
511
512 // TODO: Implement - read this from a server exception ring buffer.
513 List<CaughtException> get exceptions => [];
514 }
515
516 class ContextsPage extends DiagnosticPageWithNav {
517 ContextsPage(DiagnosticsSite site)
518 : super(site, 'contexts', 'Contexts',
519 description:
520 'An analysis context defines the options and the set of sources being analyzed.');
521
522 String get navDetail => printInteger(server.driverMap.length);
523
524 @override
525 void generateContent(Map<String, String> params) {
526 Map<Folder, AnalysisDriver> driverMap = server.driverMap;
527 if (driverMap.isEmpty) {
528 blankslate('No contexts.');
529 return;
530 }
531
532 String contextPath = params['context'];
533 Folder folder = driverMap.keys
534 .firstWhere((f) => f.path == contextPath, orElse: () => null);
535
536 if (folder == null) {
537 folder = driverMap.keys.first;
538 contextPath = folder.path;
539 }
540
541 AnalysisDriver driver = driverMap[folder];
542
543 buf.writeln('<div class="tabnav">');
544 buf.writeln('<nav class="tabnav-tabs">');
545 for (Folder f in driverMap.keys) {
546 if (f == folder) {
547 buf.writeln(
548 '<a class="tabnav-tab selected">${escape(f.shortName)}</a>');
549 } else {
550 String p = '$path?context=${Uri.encodeQueryComponent(f.path)}';
551 buf.writeln(
552 '<a href="$p" class="tabnav-tab">${escape(f.shortName)}</a>');
553 }
554 }
555 buf.writeln('</nav>');
556 buf.writeln('</div>');
557
558 buf.writeln(writeOption('Context location', escape(contextPath)));
559 buf.writeln(writeOption('Analysis options path',
560 escape(driver.contextRoot.optionsFilePath ?? 'none')));
561
562 buf.writeln('<div class="columns">');
563
564 buf.writeln('<div class="column one-half">');
565 h3('Analysis options');
566 p(describe(driver.analysisOptions), raw: true);
567 buf.writeln(
568 writeOption('Has .packages file', folder.getChild('.packages').exists));
569 buf.writeln(writeOption(
570 'Has pubspec.yaml file', folder.getChild('pubspec.yaml').exists));
571 buf.writeln('</div>');
572
573 buf.writeln('<div class="column one-half">');
574 DartSdk sdk = driver?.sourceFactory?.dartSdk;
575 AnalysisOptionsImpl sdkOptions = sdk?.context?.analysisOptions;
576 if (sdkOptions != null) {
577 h3('SDK analysis options');
578 p(describe(sdkOptions), raw: true);
579
580 if (sdk is FolderBasedDartSdk) {
581 p(writeOption('Use summaries', sdk.useSummary), raw: true);
582 }
583 }
584 buf.writeln('</div>');
585
586 buf.writeln('</div>');
587
588 h3('Lints');
589 p(driver.analysisOptions.lintRules.map((l) => l.name).join(', '));
590
591 h3('Error processors');
592 p(driver.analysisOptions.errorProcessors
593 .map((e) => e.description)
594 .join(', '));
595
596 List<String> priorityFiles = driver.priorityFiles;
597 List<String> addedFiles = driver.addedFiles.toList();
598 List<String> implicitFiles =
599 driver.knownFiles.difference(driver.addedFiles).toList();
600 addedFiles.sort();
601 implicitFiles.sort();
602
603 String lenCounter(List list) {
604 return '<span class="counter" style="float: right;">${list.length}</span>' ;
605 }
606
607 h3('Context files');
608
609 h4('Priority files ${lenCounter(priorityFiles)}', raw: true);
610 inputList(priorityFiles, (file) => buf.write(file));
611
612 h4('Added files ${lenCounter(addedFiles)}', raw: true);
613 inputList(addedFiles, (file) => buf.write(file));
614
615 h4('ImplicitFiles files ${lenCounter(implicitFiles)}', raw: true);
616 inputList(implicitFiles, (file) => buf.write(file));
617
618 SourceFactory sourceFactory = driver.sourceFactory;
619 if (sourceFactory is SourceFactoryImpl) {
620 h3('Resolvers');
621 for (UriResolver resolver in sourceFactory.resolvers) {
622 h4(resolver.runtimeType.toString());
623 buf.write('<p>');
624 if (resolver is DartUriResolver) {
625 DartSdk sdk = resolver.dartSdk;
626 buf.write(' (sdk = ');
627 buf.write(sdk.runtimeType);
628 if (sdk is FolderBasedDartSdk) {
629 buf.write(' (path = ');
630 buf.write(sdk.directory.path);
631 buf.write(')');
632 } else if (sdk is EmbedderSdk) {
633 buf.write(' (map = ');
634 writeMap(sdk.urlMappings);
635 buf.write(')');
636 }
637 buf.write(')');
638 } else if (resolver is SdkExtUriResolver) {
639 buf.write(' (map = ');
640 writeMap(resolver.urlMappings);
641 buf.write(')');
642 } else if (resolver is PackageMapUriResolver) {
643 writeMap(resolver.packageMap);
644 }
645 buf.write('</p>');
646 }
647 }
648 }
649
650 String describe(AnalysisOptionsImpl options) {
651 StringBuffer b = new StringBuffer();
652
653 b.write(
654 writeOption('Analyze function bodies', options.analyzeFunctionBodies));
655 b.write(writeOption('Enable asserts in initializer lists',
656 options.enableAssertInitializer));
657 b.write(writeOption(
658 'Enable strict call checks', options.enableStrictCallChecks));
659 b.write(writeOption('Enable super mixins', options.enableSuperMixins));
660 b.write(writeOption('Generate dart2js hints', options.dart2jsHint));
661 b.write(writeOption(
662 'Generate errors in implicit files', options.generateImplicitErrors));
663 b.write(
664 writeOption('Generate errors in SDK files', options.generateSdkErrors));
665 b.write(writeOption('Generate hints', options.hint));
666 b.write(writeOption('Incremental resolution', options.incremental));
667 b.write(writeOption(
668 'Incremental resolution with API changes', options.incrementalApi));
669 b.write(writeOption('Preserve comments', options.preserveComments));
670 b.write(writeOption('Strong mode', options.strongMode));
671 b.write(writeOption('Strong mode hints', options.strongModeHints));
672
673 return b.toString();
674 }
675
676 void writeList<E>(List<E> list) {
677 buf.writeln('[${list.join(', ')}]');
678 }
679
680 void writeMap<V>(Map<String, V> map) {
681 List<String> keys = map.keys.toList();
682 keys.sort();
683 int length = keys.length;
684 buf.write('{');
685 for (int i = 0; i < length; i++) {
686 buf.write('<br>');
687 String key = keys[i];
688 V value = map[key];
689 buf.write(key);
690 buf.write(' = ');
691 if (value is List) {
692 writeList(value);
693 } else {
694 buf.write(value);
695 }
696 buf.write(',');
697 }
698 buf.write('<br>}');
699 }
700 }
701
702 class OverlaysPage extends DiagnosticPageWithNav {
703 OverlaysPage(DiagnosticsSite site)
704 : super(site, 'overlays', 'Overlays',
705 description: 'Editing overlays - unsaved file changes.');
706
707 @override
708 void generateContent(Map<String, String> params) {
709 FileContentOverlay overlays = server.fileContentOverlay;
710 List<String> paths = overlays.paths.toList()..sort();
711
712 String overlayPath = params['overlay'];
713 if (overlayPath != null) {
714 if (overlays[overlayPath] != null) {
715 buf.write('<pre><code>');
716 buf.write(overlays[overlayPath]);
717 buf.writeln('</code></pre>');
718 } else {
719 p('<code>${escape(overlayPath)}</code> not found.', raw: true);
720 }
721
722 return;
723 }
724
725 if (paths.isEmpty) {
726 blankslate('No overlays.');
727 } else {
728 String lenCounter(List list) {
729 return '<span class="counter" style="float: right;">${list.length}</span >';
730 }
731
732 h3('Overlays ${lenCounter(paths)}', raw: true);
733 ul(paths, (String overlayPath) {
734 String uri = '$path?overlay=${Uri.encodeQueryComponent(overlayPath)}';
735 buf.writeln('<a href="$uri">${escape(overlayPath)}</a>');
736 });
737 }
738 }
739 }
740
741 class PluginsPage extends DiagnosticPageWithNav {
742 PluginsPage(DiagnosticsSite site)
743 : super(site, 'plugins', 'Plugins', description: 'Plugins in use.');
744
745 @override
746 void generateContent(Map<String, String> params) {
747 h3('Analysis plugins');
748 List<PluginInfo> analysisPlugins = server.pluginManager.plugins;
749
750 if (analysisPlugins.isEmpty) {
751 blankslate('No analysis plugins active.');
752 } else {
753 ul(analysisPlugins, (PluginInfo p) {
754 buf.writeln('${p.data.name} ${p.pluginId} (${p.data.version})');
755 });
756 }
757
758 h3('Analyzer plugins');
759 void writePlugin(Plugin plugin) {
760 buf.write(plugin.uniqueIdentifier);
761 buf.write(' (');
762 buf.write(plugin.runtimeType);
763 buf.write(')');
764 }
765
766 List<Plugin> plugins = [
767 AnalysisEngine.instance.enginePlugin,
768 server.serverPlugin
769 ];
770 plugins.addAll(server.userDefinedPlugins);
771 ul(plugins, writePlugin);
772 }
773 }
774
775 class ExecutionDomainPage extends DiagnosticPageWithNav {
776 ExecutionDomainPage(DiagnosticsSite site)
777 : super(site, 'execution', 'Execution Domain',
778 description: 'Data for the analysis server\'s execution domain.');
779
780 @override
781 void generateContent(Map<String, String> params) {
782 ExecutionDomainHandler domain = server.handlers.firstWhere(
783 (handler) => handler is ExecutionDomainHandler,
784 orElse: () => null);
785
786 h3('Subscriptions');
787 ul(ExecutionService.VALUES, (item) {
788 if (domain.onFileAnalyzed != null) {
789 buf.write('$item (has subscriptions)');
790 } else {
791 buf.write('$item (no subscriptions)');
792 }
793 });
794 }
795 }
796
797 class CompletionPage extends DiagnosticPageWithNav {
798 CompletionPage(DiagnosticsSite site)
799 : super(site, 'completion', 'Code Completion',
800 description: 'Latency statistics for code completion.');
801
802 @override
803 void generateContent(Map<String, String> params) {
804 CompletionDomainHandler domain = server.handlers.firstWhere(
805 (handler) => handler is CompletionDomainHandler,
806 orElse: () => null);
807
808 List<CompletionPerformance> completions = domain.performanceList.toList();
809 completions.sort((a, b) => b.start.compareTo(a.start));
810
811 if (completions.isEmpty) {
812 blankslate('No completions recorded.');
813 return;
814 }
815
816 int fastCount =
817 completions.where((c) => c.elapsedInMilliseconds <= 100).length;
818 p('${completions.length} results; ${printPercentage(fastCount / completions. length)} within 100ms.');
819
820 // draw a chart
821 buf.writeln(
822 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
823 StringBuffer rowData = new StringBuffer();
824 for (int i = completions.length - 1; i >= 0; i--) {
825 // [' ', 101.5]
826 if (rowData.isNotEmpty) {
827 rowData.write(',');
828 }
829 rowData.write("[' ', ${completions[i].elapsedInMilliseconds}]");
830 }
831 buf.writeln('''
832 <script type="text/javascript">
833 google.charts.load('current', {'packages':['bar']});
834 google.charts.setOnLoadCallback(drawChart);
835 function drawChart() {
836 var data = google.visualization.arrayToDataTable([
837 ['Completions', 'Time'],
838 $rowData
839 ]);
840 var options = { bars: 'vertical', vAxis: {format: 'decimal'}, height: 30 0 };
841 var chart = new google.charts.Bar(document.getElementById('chart-div'));
842 chart.draw(data, google.charts.Bar.convertOptions(options));
843 }
844 </script>
845 ''');
846
847 // emit the data as a table
848 buf.writeln('<table>');
849 buf.writeln(
850 '<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>') ;
851 for (CompletionPerformance completion in completions) {
852 buf.writeln('<tr>'
853 '<td class="pre right">${printMilliseconds(completion.elapsedInMillise conds)}</td>'
854 '<td class="right">${completion.suggestionCount}</td>'
855 '<td>${escape(completion.source.shortName)}</td>'
856 '<td><code>${escape(completion.snippet)}</code></td>'
857 '</tr>');
858 }
859 buf.writeln('</table>');
860 }
861 }
862
863 // TODO(devoncarew): Show the last x requests and responses.
864 class CommunicationsPage extends DiagnosticPageWithNav {
865 CommunicationsPage(DiagnosticsSite site)
866 : super(site, 'communications', 'Communications',
867 description:
868 'Latency statistics for analysis server communications.');
869
870 @override
871 void generateContent(Map<String, String> params) {
872 void writeRow(List<String> data, {List<String> classes}) {
873 buf.write("<tr>");
874 for (int i = 0; i < data.length; i++) {
875 String c = classes == null ? null : classes[i];
876 if (c != null) {
877 buf.write('<td class="$c">${escape(data[i])}</td>');
878 } else {
879 buf.write('<td>${escape(data[i])}</td>');
880 }
881 }
882 buf.writeln("</tr>");
883 }
884
885 buf.writeln('<div class="columns">');
886
887 ServerPerformance perf = server.performanceAfterStartup;
888 if (perf != null) {
889 buf.writeln('<div class="column one-half">');
890 h3('Current');
891
892 int requestCount = perf.requestCount;
893 double averageLatency =
894 requestCount > 0 ? (perf.requestLatency / requestCount) : 0.0;
895 int maximumLatency = perf.maxLatency;
896 double slowRequestPercent =
897 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
898
899 buf.write('<table>');
900 writeRow([printInteger(requestCount), 'requests'],
901 classes: ["right", null]);
902 writeRow([printMilliseconds(averageLatency), 'average latency'],
903 classes: ["right", null]);
904 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
905 classes: ["right", null]);
906 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
907 classes: ["right", null]);
908 buf.write('</table>');
909 buf.write('</div>');
910 }
911
912 buf.writeln('<div class="column one-half">');
913 h3('Startup');
914 perf = server.performanceDuringStartup;
915
916 int requestCount = perf.requestCount;
917 double averageLatency =
918 requestCount > 0 ? (perf.requestLatency / requestCount) : 0.0;
919 int maximumLatency = perf.maxLatency;
920 double slowRequestPercent =
921 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
922
923 buf.write('<table>');
924 writeRow([printInteger(requestCount), 'requests'],
925 classes: ["right", null]);
926 writeRow([printMilliseconds(averageLatency), 'average latency'],
927 classes: ["right", null]);
928 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
929 classes: ["right", null]);
930 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
931 classes: ["right", null]);
932 buf.write('</table>');
933
934 if (server.performanceAfterStartup != null) {
935 int startupTime =
936 server.performanceAfterStartup.startTime - perf.startTime;
937 p('(initial analysis time: ${printMilliseconds(startupTime)})');
938 }
939 buf.write('</div>');
940
941 buf.write('</div>');
942 }
943 }
944
945 String writeOption(String name, dynamic value) {
946 return '$name: <code>$value</code><br> ';
947 }
948
949 String get _sdkVersion {
950 String version = Platform.version;
951 if (version.contains(' ')) {
952 version = version.substring(0, version.indexOf(' '));
953 }
954 return version;
955 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/lib/src/server/http_server.dart ('k') | pkg/analysis_server/lib/src/status/get_handler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698