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

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: 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(a.title.toLowerCase())));
Brian Wilkerson 2017/05/30 21:09:19 One of these 'a's should be a 'b'.
devoncarew 2017/05/30 23:31:31 oops
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) buf.write('<span class="counter">$detail</span>');
Brian Wilkerson 2017/05/30 21:09:19 style nit: We have a convention (within the analyz
devoncarew 2017/05/30 23:31:31 Done.
260 buf.writeln('</a>');
261 }
262 buf.writeln('</nav>');
263 buf.writeln('</div>'); // div.one-fourth
Brian Wilkerson 2017/05/30 21:09:19 "one-fifth"? Consider dropping comments that can q
devoncarew 2017/05/30 23:31:31 Done.
264
265 buf.writeln('<div class="four-fifths column markdown-body">');
266 h1(title, classes: 'page-title');
267 div(() {
268 p(description);
269 generateContent(params);
270 }, classes: 'markdown-body');
271 buf.writeln('</div>'); // div.three-fourths
272
273 buf.writeln('</div>'); // div.columns
274 }
275
276 String get navDetail => null;
277
278 bool get isNavPage => true;
279 }
280
281 class NotFoundPage extends DiagnosticPage {
282 final String path;
283
284 NotFoundPage(Site site, this.path)
285 : super(site, '', '404 Not found', description: "'$path' not found.");
286
287 void generateContent(Map<String, String> params) {}
288 }
289
290 class ExceptionPage extends DiagnosticPage {
291 final StackTrace trace;
292
293 ExceptionPage(Site site, String message, this.trace)
294 : super(site, '', '500 Oops', description: message);
295
296 void generateContent(Map<String, String> params) {
297 p(trace.toString(), style: 'white-space: pre');
298 }
299 }
300
301 class FeedbackPage extends DiagnosticPage {
302 FeedbackPage(DiagnosticsSite site)
303 : super(site, 'feedback', 'Feedback',
304 description: 'Providing feedback and filing issues.');
305
306 @override
307 void generateContent(Map<String, String> params) {
308 final String issuesUrl = 'https://github.com/dart-lang/sdk/issues';
309 p(
310 'To file issues or feature requests, see our '
311 '<a href="$issuesUrl">bug tracker</a>. When filing an issue, please de scribe:',
312 raw: true,
313 );
314 ul([
315 'what you were doing',
316 'what occured',
317 'what you think the expected behavior should have been',
318 ], (line) => buf.writeln(line));
319
320 p('Other data to include:');
321 ul([
322 "the IDE you are using and it's version",
323 'the Dart SDK version (<code>${escape(_sdkVersion)}</code>',
324 'your operating system (<code>${escape(Platform.operatingSystem)}</code>)' ,
325 ], (line) => buf.writeln(line));
326
327 p('Thanks!');
328 }
329 }
330
331 class StatusPage extends DiagnosticPageWithNav {
332 StatusPage(DiagnosticsSite site)
333 : super(site, 'status', 'Status',
334 description:
335 'General status and diagnostics for the analysis server.');
336
337 @override
338 void generateContent(Map<String, String> params) {
339 buf.writeln('<div class="columns">');
340
341 buf.writeln('<div class="column one-half">');
342 h3('Status');
343 buf.writeln(writeOption(
344 'New analysis driver enabled', server.options.enableNewAnalysisDriver));
345 buf.writeln(writeOption('Instrumentation enabled',
346 AnalysisEngine.instance.instrumentationService.isActive));
347 buf.writeln(writeOption('Server process ID', pid));
348 buf.writeln('</div>');
349
350 buf.writeln('<div class="column one-half">');
351 h3('Versions');
352 buf.writeln(writeOption('Analysis server version', AnalysisServer.VERSION));
353 buf.writeln(writeOption('Dart SDK', Platform.version));
354 buf.writeln('</div>');
355
356 buf.writeln('</div>');
357
358 h3('Server domain subscriptions');
359 ul(ServerService.VALUES, (item) {
360 if (server.serverServices.contains(item)) {
361 buf.write('$item (has subscriptions)');
362 } else {
363 buf.write('$item (no subscriptions)');
364 }
365 });
366
367 h3('Analysis domain subscriptions');
368 for (AnalysisService service in AnalysisService.VALUES) {
369 buf.writeln('${service.name}<br>');
370 ul(server.analysisServices[service] ?? [], (item) {
371 buf.write('$item');
372 });
373 }
374
375 List<String> lines = (site as DiagnosticsSite).lastPrintedLines;
376 if (lines.isNotEmpty) {
377 h3('Debug output');
378 p(lines.join('\n'), style: 'white-space: pre');
379 }
380 }
381 }
382
383 class InstrumentationPage extends DiagnosticPageWithNav {
384 InstrumentationPage(DiagnosticsSite site)
385 : super(site, 'instrumentation', 'Instrumentation',
386 description:
387 'Verbose instrumentation data from the analysis server.');
388
389 @override
390 void generateContent(Map<String, String> params) {
391 p(
392 'Instrumentation can be enabled by starting the analysis server with the '
393 '<code>--instrumentation-log-file=path/to/file</code> flag.',
394 raw: true);
395
396 if (!AnalysisEngine.instance.instrumentationService.isActive) {
397 blankslate('Instrumentation not active.');
398 return;
399 }
400
401 h3('Instrumentation');
402
403 p('Instrumentation active.');
404
405 InstrumentationServer instrumentation =
406 AnalysisEngine.instance.instrumentationService.instrumentationServer;
407 String description = instrumentation.describe;
408 HtmlEscape htmlEscape = new HtmlEscape(HtmlEscapeMode.ELEMENT);
409 description = htmlEscape.convert(description);
410 // Convert http(s): references to hyperlinks.
411 final RegExp urlRegExp = new RegExp(r'[http|https]+:\/*(\S+)');
412 description = description.replaceAllMapped(urlRegExp, (Match match) {
413 return '<a href="${match.group(0)}">${match.group(1)}</a>';
414 });
415 p(description.replaceAll('\n', '<br>'), raw: true);
416 }
417 }
418
419 class ProfilePage extends DiagnosticPageWithNav {
420 ProfilePage(DiagnosticsSite site)
421 : super(site, 'profile', 'Profiling Info',
422 description: 'Profiling performance tag data.');
423
424 @override
425 void generateContent(Map<String, String> params) {
426 // prepare sorted tags
427 List<PerformanceTag> tags = PerformanceTag.all.toList();
428 tags.remove(ServerPerformanceStatistics.idle);
429 tags.removeWhere((tag) => tag.label == 'unknown');
430 tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
431
432 // draw a pie chart
433 String rowData =
434 tags.map((tag) => "['${tag.label}', ${tag.elapsedMs}]").join(',');
435 buf.writeln(
436 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
437 buf.writeln('''
438 <script type="text/javascript">
439 google.charts.load('current', {'packages':['corechart']});
440 google.charts.setOnLoadCallback(drawChart);
441
442 function drawChart() {
443 var data = new google.visualization.DataTable();
444 data.addColumn('string', 'Tag');
445 data.addColumn('number', 'Time (ms)');
446 data.addRows([$rowData]);
447 var options = {'title': 'Performance Tag Data', 'width': 700, 'height' : 300};
448 var chart = new google.visualization.PieChart(document.getElementById( 'chart-div'));
449 chart.draw(data, options);
450 }
451 </script>
452 ''');
453
454 // print total time
455 int totalTime =
456 tags.fold<int>(0, (int a, PerformanceTag tag) => a + tag.elapsedMs);
457 p('Total measured time: ${printMilliseconds(totalTime)}');
458
459 // write out a table
460 void _writeRow(List<String> data, {bool header: false}) {
461 buf.write('<tr>');
462 if (header) {
463 for (String d in data) {
464 buf.write('<th>$d</th>');
465 }
466 } else {
467 for (String d in data) {
468 buf.write('<td>$d</td>');
469 }
470 }
471 buf.writeln('</tr>');
472 }
473
474 buf.write('<table>');
475 _writeRow(['Tag name', 'Time (in ms)', 'Percent'], header: true);
476 void writeRow(PerformanceTag tag) {
477 double percent = tag.elapsedMs / totalTime;
478 _writeRow([
479 tag.label,
480 printMilliseconds(tag.elapsedMs),
481 printPercentage(percent)
482 ]);
483 }
484
485 tags.forEach(writeRow);
486 buf.write('</table>');
487 }
488 }
489
490 // TODO(devoncarew): This is not hooked up.
491 class ExceptionsPage extends DiagnosticPageWithNav {
492 ExceptionsPage(DiagnosticsSite site)
493 : super(site, 'exceptions', 'Exceptions',
494 description: 'Exceptions from the analysis server.');
495
496 String get navDetail => exceptions.length.toString();
497
498 @override
499 void generateContent(Map<String, String> params) {
500 if (exceptions.isEmpty) {
501 blankslate('No exceptions encountered!');
502 } else {
503 for (CaughtException ex in exceptions) {
504 h3('${ex.exception}');
505 p(ex.toString(), style: 'white-space: pre');
506 }
507 }
508 }
509
510 // TODO: Implement - read this from a server exception ring buffer.
511 List<CaughtException> get exceptions => [];
512 }
513
514 class ContextsPage extends DiagnosticPageWithNav {
515 ContextsPage(DiagnosticsSite site)
516 : super(site, 'contexts', 'Contexts',
517 description:
518 'An analysis context defines the options and the set of sources being analyzed.');
519
520 String get navDetail => printInteger(server.driverMap.length);
521
522 @override
523 void generateContent(Map<String, String> params) {
524 Map<Folder, AnalysisDriver> driverMap = server.driverMap;
525 if (driverMap.isEmpty) {
526 blankslate('No contexts.');
527 return;
528 }
529
530 String contextPath = params['context'];
531 Folder folder = driverMap.keys
532 .firstWhere((f) => f.path == contextPath, orElse: () => null);
533
534 if (folder == null) {
535 folder = driverMap.keys.first;
536 contextPath = folder.path;
537 }
538
539 AnalysisDriver driver = driverMap[folder];
540
541 buf.writeln('<div class="tabnav">');
542 buf.writeln('<nav class="tabnav-tabs">');
543 for (Folder f in driverMap.keys) {
544 if (f == folder) {
545 buf.writeln(
546 '<a class="tabnav-tab selected">${escape(f.shortName)}</a>');
547 } else {
548 String p = '$path?context=${Uri.encodeQueryComponent(f.path)}';
549 buf.writeln(
550 '<a href="$p" class="tabnav-tab">${escape(f.shortName)}</a>');
551 }
552 }
553 buf.writeln('</nav>');
554 buf.writeln('</div>');
555
556 p('Context path is $contextPath.');
557
558 buf.writeln(
559 writeOption('Has .packages file', folder.getChild('.packages').exists));
560 buf.writeln(writeOption(
561 'Has pubspec.yaml file', folder.getChild('pubspec.yaml').exists));
562 buf.writeln(writeOption('Analysis options path',
563 escape(driver.contextRoot.optionsFilePath) ?? 'none'));
564
565 buf.writeln('<div class="columns">');
566
567 buf.writeln('<div class="column one-half">');
568 h3('Analysis options');
569 p(describe(driver.analysisOptions), raw: true);
570 buf.writeln('</div>');
571
572 buf.writeln('<div class="column one-half">');
573 DartSdk sdk = driver?.sourceFactory?.dartSdk;
574 AnalysisOptionsImpl sdkOptions = sdk?.context?.analysisOptions;
575 if (sdkOptions != null) {
576 h3('SDK analysis options');
577 p(describe(sdkOptions), raw: true);
578
579 if (sdk is FolderBasedDartSdk) {
580 p(writeOption('Use summaries', sdk.useSummary), raw: true);
581 }
582 }
583 buf.writeln('</div>');
584
585 buf.writeln('</div>');
586
587 h3('Lints');
588 p(driver.analysisOptions.lintRules.map((l) => l.name).join(', '));
589
590 h3('Error processors');
591 p(driver.analysisOptions.errorProcessors
592 .map((e) => e.description)
593 .join(', '));
594
595 List<String> priorityFiles = driver.priorityFiles;
596 List<String> addedFiles = driver.addedFiles.toList();
597 List<String> implicitFiles =
598 driver.knownFiles.difference(driver.addedFiles).toList();
599 addedFiles.sort();
600 implicitFiles.sort();
601
602 String lenCounter(List list) {
603 return '<span class="counter" style="float: right;">${list.length}</span>' ;
604 }
605
606 h3('Context files');
607
608 h4('Priority files ${lenCounter(priorityFiles)}', raw: true);
609 inputList(priorityFiles, (file) => buf.write(file));
610
611 h4('Added files ${lenCounter(addedFiles)}', raw: true);
612 inputList(addedFiles, (file) => buf.write(file));
613
614 h4('ImplicitFiles files ${lenCounter(implicitFiles)}', raw: true);
615 inputList(implicitFiles, (file) => buf.write(file));
616
617 SourceFactory sourceFactory = driver.sourceFactory;
618 if (sourceFactory is SourceFactoryImpl) {
619 h3('Resolvers');
620 for (UriResolver resolver in sourceFactory.resolvers) {
621 h4(resolver.runtimeType.toString());
622 buf.write('<p>');
623 if (resolver is DartUriResolver) {
624 DartSdk sdk = resolver.dartSdk;
625 buf.write(' (sdk = ');
626 buf.write(sdk.runtimeType);
627 if (sdk is FolderBasedDartSdk) {
628 buf.write(' (path = ');
629 buf.write(sdk.directory.path);
630 buf.write(')');
631 } else if (sdk is EmbedderSdk) {
632 buf.write(' (map = ');
633 writeMap(sdk.urlMappings);
634 buf.write(')');
635 }
636 buf.write(')');
637 } else if (resolver is SdkExtUriResolver) {
638 buf.write(' (map = ');
639 writeMap(resolver.urlMappings);
640 buf.write(')');
641 } else if (resolver is PackageMapUriResolver) {
642 writeMap(resolver.packageMap);
643 }
644 buf.write('</p>');
645 }
646 }
647 }
648
649 String describe(AnalysisOptionsImpl options) {
650 StringBuffer b = new StringBuffer();
651
652 b.write(
653 writeOption('Analyze function bodies', options.analyzeFunctionBodies));
654 b.write(writeOption('Enable asserts in initializer lists',
655 options.enableAssertInitializer));
656 b.write(writeOption(
657 'Enable strict call checks', options.enableStrictCallChecks));
658 b.write(writeOption('Enable super mixins', options.enableSuperMixins));
659 b.write(writeOption('Generate dart2js hints', options.dart2jsHint));
660 b.write(writeOption(
661 'Generate errors in implicit files', options.generateImplicitErrors));
662 b.write(
663 writeOption('Generate errors in SDK files', options.generateSdkErrors));
664 b.write(writeOption('Generate hints', options.hint));
665 b.write(writeOption('Incremental resolution', options.incremental));
666 b.write(writeOption(
667 'Incremental resolution with API changes', options.incrementalApi));
668 b.write(writeOption('Preserve comments', options.preserveComments));
669 b.write(writeOption('Strong mode', options.strongMode));
670 b.write(writeOption('Strong mode hints', options.strongModeHints));
671
672 return b.toString();
673 }
674
675 void writeList<E>(List<E> list) {
676 buf.writeln('[${list.join(', ')}]');
677 }
678
679 void writeMap<V>(Map<String, V> map) {
680 List<String> keys = map.keys.toList();
681 keys.sort();
682 int length = keys.length;
683 buf.write('{');
684 for (int i = 0; i < length; i++) {
685 buf.write('<br>');
686 String key = keys[i];
687 V value = map[key];
688 buf.write(key);
689 buf.write(' = ');
690 if (value is List) {
691 writeList(value);
692 } else {
693 buf.write(value);
694 }
695 buf.write(',');
696 }
697 buf.write('<br>}');
698 }
699 }
700
701 class OverlaysPage extends DiagnosticPageWithNav {
702 OverlaysPage(DiagnosticsSite site)
703 : super(site, 'overlays', 'Overlays',
704 description: 'Editing overlays - unsaved file changes.');
705
706 @override
707 void generateContent(Map<String, String> params) {
708 FileContentOverlay overlays = server.fileContentOverlay;
709 List<String> paths = overlays.paths.toList()..sort();
710
711 String overlayPath = params['overlay'];
712 if (overlayPath != null) {
713 if (overlays[overlayPath] != null) {
714 buf.write('<pre><code>');
715 buf.write(overlays[overlayPath]);
716 buf.writeln('</code></pre>');
717 } else {
718 p('<code>${escape(overlayPath)}</code> not found.', raw: true);
719 }
720
721 return;
722 }
723
724 if (paths.isEmpty) {
725 blankslate('No overlays.');
726 } else {
727 String lenCounter(List list) {
728 return '<span class="counter" style="float: right;">${list.length}</span >';
729 }
730
731 h3('Overlays ${lenCounter(paths)}', raw: true);
732 ul(paths, (String overlayPath) {
733 String uri = '$path?overlay=${Uri.encodeQueryComponent(overlayPath)}';
734 buf.writeln('<a href="$uri">${escape(overlayPath)}</a>');
735 });
736 }
737 }
738 }
739
740 class PluginsPage extends DiagnosticPageWithNav {
741 PluginsPage(DiagnosticsSite site)
742 : super(site, 'plugins', 'Plugins', description: 'Plugins in use.');
743
744 @override
745 void generateContent(Map<String, String> params) {
746 h3('Analysis plugins');
747 List<PluginInfo> analysisPlugins = server.pluginManager.plugins;
748
749 if (analysisPlugins.isEmpty) {
750 blankslate('No analysis plugins active.');
751 } else {
752 ul(analysisPlugins, (PluginInfo p) {
753 buf.writeln('${p.data.name} ${p.pluginId} (${p.data.version})');
754 });
755 }
756
757 h3('Analyzer plugins');
758 void writePlugin(Plugin plugin) {
759 buf.write(plugin.uniqueIdentifier);
760 buf.write(' (');
761 buf.write(plugin.runtimeType);
762 buf.write(')');
763 }
764
765 List<Plugin> plugins = [
766 AnalysisEngine.instance.enginePlugin,
767 server.serverPlugin
768 ];
769 plugins.addAll(server.userDefinedPlugins);
770 ul(plugins, writePlugin);
771 }
772 }
773
774 class ExecutionDomainPage extends DiagnosticPageWithNav {
775 ExecutionDomainPage(DiagnosticsSite site)
776 : super(site, 'execution', 'Execution Domain',
777 description: 'Data for the analysis server\'s execution domain.');
778
779 @override
780 void generateContent(Map<String, String> params) {
781 ExecutionDomainHandler domain = server.handlers.firstWhere(
782 (handler) => handler is ExecutionDomainHandler,
783 orElse: () => null);
784
785 h3('Subscriptions');
786 ul(ExecutionService.VALUES, (item) {
787 if (domain.onFileAnalyzed != null) {
788 buf.write('$item (has subscriptions)');
789 } else {
790 buf.write('$item (no subscriptions)');
791 }
792 });
793 }
794 }
795
796 class CompletionPage extends DiagnosticPageWithNav {
797 CompletionPage(DiagnosticsSite site)
798 : super(site, 'completion', 'Code Completion',
799 description: 'Latency statistics for code completion.');
800
801 @override
802 void generateContent(Map<String, String> params) {
803 CompletionDomainHandler domain = server.handlers.firstWhere(
804 (handler) => handler is CompletionDomainHandler,
805 orElse: () => null);
806
807 List<CompletionPerformance> completions = domain.performanceList.toList();
808 completions.sort((a, b) => b.start.compareTo(a.start));
809
810 if (completions.isEmpty) {
811 blankslate('No completions recorded.');
812 return;
813 }
814
815 int fastCount =
816 completions.where((c) => c.elapsedInMilliseconds <= 100).length;
817 p('${completions.length} results; ${printPercentage(fastCount / completions. length)} within 100ms.');
818
819 // draw a chart
820 buf.writeln(
821 '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
822 StringBuffer rowData = new StringBuffer();
823 for (int i = completions.length - 1; i >= 0; i--) {
824 // [' ', 101.5]
825 if (rowData.isNotEmpty) rowData.write(',');
826 rowData.write("[' ', ${completions[i].elapsedInMilliseconds}]");
827 }
828 buf.writeln('''
829 <script type="text/javascript">
830 google.charts.load('current', {'packages':['bar']});
831 google.charts.setOnLoadCallback(drawChart);
832 function drawChart() {
833 var data = google.visualization.arrayToDataTable([
834 ['Completions', 'Time'],
835 $rowData
836 ]);
837 var options = { bars: 'vertical', vAxis: {format: 'decimal'}, height: 30 0 };
838 var chart = new google.charts.Bar(document.getElementById('chart-div'));
839 chart.draw(data, google.charts.Bar.convertOptions(options));
840 }
841 </script>
842 ''');
843
844 // emit the data as a table
845 buf.writeln('<table>');
846 buf.writeln(
847 '<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>') ;
848 for (CompletionPerformance completion in completions) {
849 buf.writeln('<tr>'
850 '<td class="pre right">${printMilliseconds(completion.elapsedInMillise conds)}</td>'
851 '<td class="right">${completion.suggestionCount}</td>'
852 '<td>${escape(completion.source.shortName)}</td>'
853 '<td><code>${escape(completion.snippet)}</code></td>'
854 '</tr>');
855 }
856 buf.writeln('</table>');
857 }
858 }
859
860 // TODO(devoncarew): Show the last x requests and responses.
861 class CommunicationsPage extends DiagnosticPageWithNav {
862 CommunicationsPage(DiagnosticsSite site)
863 : super(site, 'communications', 'Communications',
864 description:
865 'Latency statistics for analysis server communications.');
866
867 @override
868 void generateContent(Map<String, String> params) {
869 void writeRow(List<String> data, {List<String> classes}) {
870 buf.write("<tr>");
871 for (int i = 0; i < data.length; i++) {
872 String c = classes == null ? null : classes[i];
873 if (c != null) {
874 buf.write('<td class="$c">${escape(data[i])}</td>');
875 } else {
876 buf.write('<td>${escape(data[i])}</td>');
877 }
878 }
879 buf.writeln("</tr>");
880 }
881
882 buf.writeln('<div class="columns">');
883
884 ServerPerformance perf = server.performanceAfterStartup;
885 if (perf != null) {
886 buf.writeln('<div class="column one-half">');
887 h3('Current');
888
889 int requestCount = perf.requestCount;
890 double averageLatency =
891 requestCount > 0 ? (perf.requestLatency / requestCount) : 0.0;
892 int maximumLatency = perf.maxLatency;
893 double slowRequestPercent =
894 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
895
896 buf.write('<table>');
897 writeRow([printInteger(requestCount), 'requests'],
898 classes: ["right", null]);
899 writeRow([printMilliseconds(averageLatency), 'average latency'],
900 classes: ["right", null]);
901 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
902 classes: ["right", null]);
903 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
904 classes: ["right", null]);
905 buf.write('</table>');
906 buf.write('</div>');
907 }
908
909 buf.writeln('<div class="column one-half">');
910 h3('Startup');
911 perf = server.performanceDuringStartup;
912
913 int requestCount = perf.requestCount;
914 double averageLatency =
915 requestCount > 0 ? (perf.requestLatency / requestCount) : 0.0;
916 int maximumLatency = perf.maxLatency;
917 double slowRequestPercent =
918 requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
919
920 buf.write('<table>');
921 writeRow([printInteger(requestCount), 'requests'],
922 classes: ["right", null]);
923 writeRow([printMilliseconds(averageLatency), 'average latency'],
924 classes: ["right", null]);
925 writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
926 classes: ["right", null]);
927 writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
928 classes: ["right", null]);
929 buf.write('</table>');
930
931 if (server.performanceAfterStartup != null) {
932 int startupTime =
933 server.performanceAfterStartup.startTime - perf.startTime;
934 p('(initial analysis time: ${printMilliseconds(startupTime)})');
935 }
936 buf.write('</div>');
937
938 buf.write('</div>');
939 }
940 }
941
942 String writeOption(String name, dynamic value) {
943 return '$name: <code>$value</code><br> ';
944 }
945
946 String get _sdkVersion {
947 String version = Platform.version;
948 if (version.contains(' ')) {
949 version = version.substring(0, version.indexOf(' '));
950 }
951 return version;
952 }
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