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

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

Issue 2918553002: Remove dead code related to the diagnostics server. (Closed)
Patch Set: grammar 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) 2014, 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:collection';
6 import 'dart:convert';
7 import 'dart:io';
8 import 'dart:math';
9
10 import 'package:analysis_server/protocol/protocol.dart';
11 import 'package:analysis_server/protocol/protocol_generated.dart';
12 import 'package:analysis_server/src/analysis_server.dart';
13 import 'package:analysis_server/src/domain_completion.dart';
14 import 'package:analysis_server/src/domain_diagnostic.dart';
15 import 'package:analysis_server/src/domain_execution.dart';
16 import 'package:analysis_server/src/operation/operation.dart';
17 import 'package:analysis_server/src/operation/operation_analysis.dart';
18 import 'package:analysis_server/src/operation/operation_queue.dart';
19 import 'package:analysis_server/src/server/http_server.dart';
20 import 'package:analysis_server/src/services/completion/completion_performance.d art';
21 import 'package:analysis_server/src/socket_server.dart';
22 import 'package:analysis_server/src/status/ast_writer.dart';
23 import 'package:analysis_server/src/status/element_writer.dart';
24 import 'package:analysis_server/src/status/memory_use.dart';
25 import 'package:analysis_server/src/status/validator.dart';
26 import 'package:analysis_server/src/utilities/average.dart';
27 import 'package:analyzer/dart/ast/ast.dart';
28 import 'package:analyzer/dart/element/element.dart';
29 import 'package:analyzer/dart/element/visitor.dart';
30 import 'package:analyzer/exception/exception.dart';
31 import 'package:analyzer/file_system/file_system.dart';
32 import 'package:analyzer/source/error_processor.dart';
33 import 'package:analyzer/source/sdk_ext.dart';
34 import 'package:analyzer/src/context/cache.dart';
35 import 'package:analyzer/src/context/context.dart' show AnalysisContextImpl;
36 import 'package:analyzer/src/context/source.dart';
37 import 'package:analyzer/src/dart/sdk/sdk.dart';
38 import 'package:analyzer/src/generated/engine.dart';
39 import 'package:analyzer/src/generated/resolver.dart';
40 import 'package:analyzer/src/generated/sdk.dart';
41 import 'package:analyzer/src/generated/source.dart';
42 import 'package:analyzer/src/generated/utilities_collection.dart';
43 import 'package:analyzer/src/generated/utilities_general.dart';
44 import 'package:analyzer/src/services/lint.dart';
45 import 'package:analyzer/src/task/dart.dart';
46 import 'package:analyzer/src/task/driver.dart';
47 import 'package:analyzer/src/task/html.dart';
48 import 'package:analyzer/src/task/options.dart';
49 import 'package:analyzer/task/dart.dart';
50 import 'package:analyzer/task/general.dart';
51 import 'package:analyzer/task/html.dart';
52 import 'package:analyzer/task/model.dart';
53 import 'package:plugin/plugin.dart';
54
55 /**
56 * A function that can be used to generate HTML output into the given [buffer].
57 * The HTML that is generated must be valid (special characters must already be
58 * encoded).
59 */
60 typedef void HtmlGenerator(StringBuffer buffer);
61
62 class ElementCounter extends RecursiveElementVisitor {
63 Map<Type, int> counts = new HashMap<Type, int>();
64 int elementsWithDocs = 0;
65 int totalDocSpan = 0;
66
67 void visit(Element element) {
68 String comment = element.documentationComment;
69 if (comment != null) {
70 ++elementsWithDocs;
71 totalDocSpan += comment.length;
72 }
73
74 Type type = element.runtimeType;
75 if (counts[type] == null) {
76 counts[type] = 1;
77 } else {
78 counts[type]++;
79 }
80 }
81
82 @override
83 visitClassElement(ClassElement element) {
84 visit(element);
85 super.visitClassElement(element);
86 }
87
88 @override
89 visitCompilationUnitElement(CompilationUnitElement element) {
90 visit(element);
91 super.visitCompilationUnitElement(element);
92 }
93
94 @override
95 visitConstructorElement(ConstructorElement element) {
96 visit(element);
97 super.visitConstructorElement(element);
98 }
99
100 @override
101 visitExportElement(ExportElement element) {
102 visit(element);
103 super.visitExportElement(element);
104 }
105
106 @override
107 visitFieldElement(FieldElement element) {
108 visit(element);
109 super.visitFieldElement(element);
110 }
111
112 @override
113 visitFieldFormalParameterElement(FieldFormalParameterElement element) {
114 visit(element);
115 super.visitFieldFormalParameterElement(element);
116 }
117
118 @override
119 visitFunctionElement(FunctionElement element) {
120 visit(element);
121 super.visitFunctionElement(element);
122 }
123
124 @override
125 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
126 visit(element);
127 super.visitFunctionTypeAliasElement(element);
128 }
129
130 @override
131 visitImportElement(ImportElement element) {
132 visit(element);
133 super.visitImportElement(element);
134 }
135
136 @override
137 visitLabelElement(LabelElement element) {
138 visit(element);
139 super.visitLabelElement(element);
140 }
141
142 @override
143 visitLibraryElement(LibraryElement element) {
144 visit(element);
145 super.visitLibraryElement(element);
146 }
147
148 @override
149 visitLocalVariableElement(LocalVariableElement element) {
150 visit(element);
151 super.visitLocalVariableElement(element);
152 }
153
154 @override
155 visitMethodElement(MethodElement element) {
156 visit(element);
157 super.visitMethodElement(element);
158 }
159
160 @override
161 visitMultiplyDefinedElement(MultiplyDefinedElement element) {
162 visit(element);
163 super.visitMultiplyDefinedElement(element);
164 }
165
166 @override
167 visitParameterElement(ParameterElement element) {
168 visit(element);
169 super.visitParameterElement(element);
170 }
171
172 @override
173 visitPrefixElement(PrefixElement element) {
174 visit(element);
175 super.visitPrefixElement(element);
176 }
177
178 @override
179 visitPropertyAccessorElement(PropertyAccessorElement element) {
180 visit(element);
181 super.visitPropertyAccessorElement(element);
182 }
183
184 @override
185 visitTopLevelVariableElement(TopLevelVariableElement element) {
186 visit(element);
187 super.visitTopLevelVariableElement(element);
188 }
189
190 @override
191 visitTypeParameterElement(TypeParameterElement element) {
192 visit(element);
193 super.visitTypeParameterElement(element);
194 }
195 }
196
197 /**
198 * Instances of the class [GetHandler] handle GET requests.
199 */
200 class GetHandler implements AbstractGetHandler {
201 /**
202 * The path used to request overall performance information.
203 */
204 static const String ANALYSIS_PERFORMANCE_PATH = '/perf/analysis';
205
206 /**
207 * The path used to request information about a element model.
208 */
209 static const String AST_PATH = '/ast';
210
211 /**
212 * The path used to request information about the cache entry corresponding
213 * to a single file.
214 */
215 static const String CACHE_ENTRY_PATH = '/cache_entry';
216
217 /**
218 * The path used to request the list of source files in a certain cache
219 * state.
220 */
221 static const String CACHE_STATE_PATH = '/cache_state';
222
223 /**
224 * The path used to request code completion information.
225 */
226 static const String COMPLETION_PATH = '/completion';
227
228 /**
229 * The path used to request communication performance information.
230 */
231 static const String COMMUNICATION_PERFORMANCE_PATH = '/perf/communication';
232
233 /**
234 * The path used to request diagnostic information for a single context.
235 */
236 static const String CONTEXT_DIAGNOSTICS_PATH = '/diagnostic/context';
237
238 /**
239 * The path used to request running a validation report for a single context.
240 */
241 static const String CONTEXT_VALIDATION_DIAGNOSTICS_PATH =
242 '/diagnostic/contextValidation';
243
244 /**
245 * The path used to request information about a specific context.
246 */
247 static const String CONTEXT_PATH = '/context';
248
249 /**
250 * The path used to request diagnostic information.
251 */
252 static const String DIAGNOSTIC_PATH = '/diagnostic';
253
254 /**
255 * The path used to request information about a element model.
256 */
257 static const String ELEMENT_PATH = '/element';
258
259 /**
260 * The path used to request an analysis of the memory use of the analyzer.
261 */
262 static const String MEMORY_USE_PATH = '/memoryUse';
263
264 /**
265 * The path used to request an overlay contents.
266 */
267 static const String OVERLAY_PATH = '/overlay';
268
269 /**
270 * The path used to request overlays information.
271 */
272 static const String OVERLAYS_PATH = '/overlays';
273
274 /**
275 * The path used to request the status of the analysis server as a whole.
276 */
277 static const String STATUS_PATH = '/status';
278
279 /**
280 * Query parameter used to represent the context to search for, when
281 * accessing [CACHE_ENTRY_PATH] or [CACHE_STATE_PATH].
282 */
283 static const String CONTEXT_QUERY_PARAM = 'context';
284
285 /**
286 * Query parameter used to represent the descriptor to search for, when
287 * accessing [CACHE_STATE_PATH].
288 */
289 static const String DESCRIPTOR_QUERY_PARAM = 'descriptor';
290
291 /**
292 * Query parameter used to represent the name of elements to search for, when
293 * accessing [INDEX_ELEMENT_BY_NAME].
294 */
295 static const String INDEX_ELEMENT_NAME = 'name';
296
297 /**
298 * Query parameter used to represent the path of an overlayed file.
299 */
300 static const String PATH_PARAM = 'path';
301
302 /**
303 * Query parameter used to represent the source to search for, when accessing
304 * [CACHE_ENTRY_PATH].
305 */
306 static const String SOURCE_QUERY_PARAM = 'entry';
307
308 /**
309 * Query parameter used to represent the cache state to search for, when
310 * accessing [CACHE_STATE_PATH].
311 */
312 static const String STATE_QUERY_PARAM = 'state';
313
314 static final ContentType _htmlContent =
315 new ContentType("text", "html", charset: "utf-8");
316
317 /**
318 * Rolling average of calls to get diagnostics.
319 */
320 Average _diagnosticCallAverage = new Average();
321
322 /**
323 * The socket server whose status is to be reported on.
324 */
325 SocketServer _server;
326
327 /**
328 * Buffer containing strings printed by the analysis server.
329 */
330 List<String> _printBuffer;
331
332 /**
333 * Contents of overlay files.
334 */
335 final Map<String, String> _overlayContents = <String, String>{};
336
337 /**
338 * Handler for diagnostics requests.
339 */
340 DiagnosticDomainHandler _diagnosticHandler;
341
342 /**
343 * Initialize a newly created handler for GET requests.
344 */
345 GetHandler(this._server, this._printBuffer);
346
347 DiagnosticDomainHandler get diagnosticHandler {
348 if (_diagnosticHandler == null) {
349 _diagnosticHandler = new DiagnosticDomainHandler(_server.analysisServer);
350 }
351 return _diagnosticHandler;
352 }
353
354 /**
355 * Return the active [CompletionDomainHandler]
356 * or `null` if either analysis server is not running
357 * or there is no completion domain handler.
358 */
359 CompletionDomainHandler get _completionDomainHandler {
360 AnalysisServer analysisServer = _server.analysisServer;
361 if (analysisServer == null) {
362 return null;
363 }
364 return analysisServer.handlers
365 .firstWhere((h) => h is CompletionDomainHandler, orElse: () => null);
366 }
367
368 /**
369 * Handle a GET request received by the HTTP server.
370 */
371 void handleGetRequest(HttpRequest request) {
372 String path = request.uri.path;
373 if (path == '/' || path == STATUS_PATH) {
374 _returnServerStatus(request);
375 } else if (path == ANALYSIS_PERFORMANCE_PATH) {
376 _returnAnalysisPerformance(request);
377 } else if (path == AST_PATH) {
378 _returnAst(request);
379 } else if (path == CACHE_STATE_PATH) {
380 _returnCacheState(request);
381 } else if (path == CACHE_ENTRY_PATH) {
382 _returnCacheEntry(request);
383 } else if (path == COMPLETION_PATH) {
384 _returnCompletionInfo(request);
385 } else if (path == COMMUNICATION_PERFORMANCE_PATH) {
386 _returnCommunicationPerformance(request);
387 } else if (path == CONTEXT_DIAGNOSTICS_PATH) {
388 _returnContextDiagnostics(request);
389 } else if (path == CONTEXT_VALIDATION_DIAGNOSTICS_PATH) {
390 _returnContextValidationDiagnostics(request);
391 } else if (path == CONTEXT_PATH) {
392 _returnContextInfo(request);
393 } else if (path == DIAGNOSTIC_PATH) {
394 _returnDiagnosticInfo(request);
395 } else if (path == ELEMENT_PATH) {
396 _returnElement(request);
397 } else if (path == MEMORY_USE_PATH) {
398 _returnMemoryUsage(request);
399 } else if (path == OVERLAY_PATH) {
400 _returnOverlayContents(request);
401 } else if (path == OVERLAYS_PATH) {
402 _returnOverlaysInfo(request);
403 } else {
404 _returnUnknownRequest(request);
405 }
406 }
407
408 /**
409 * Produce an encoded version of the given [descriptor] that can be used to
410 * find the descriptor later.
411 */
412 String _encodeSdkDescriptor(SdkDescription descriptor) {
413 StringBuffer buffer = new StringBuffer();
414 buffer.write(descriptor.options.signature.join(','));
415 for (String path in descriptor.paths) {
416 buffer.write('+');
417 buffer.write(path);
418 }
419 return buffer.toString();
420 }
421
422 /**
423 * Return the folder being managed by the given [analysisServer] that matches
424 * the given [contextFilter], or `null` if there is none.
425 */
426 Folder _findFolder(AnalysisServer analysisServer, String contextFilter) {
427 return analysisServer.folderMap.keys.firstWhere(
428 (Folder folder) => folder.path == contextFilter,
429 orElse: () => null);
430 }
431
432 /**
433 * Return any AST structure stored in the given [entry].
434 */
435 CompilationUnit _getAnyAst(CacheEntry entry) {
436 CompilationUnit unit = entry.getValue(PARSED_UNIT);
437 if (unit != null) {
438 return unit;
439 }
440 unit = entry.getValue(RESOLVED_UNIT1);
441 if (unit != null) {
442 return unit;
443 }
444 unit = entry.getValue(RESOLVED_UNIT2);
445 if (unit != null) {
446 return unit;
447 }
448 unit = entry.getValue(RESOLVED_UNIT3);
449 if (unit != null) {
450 return unit;
451 }
452 unit = entry.getValue(RESOLVED_UNIT4);
453 if (unit != null) {
454 return unit;
455 }
456 unit = entry.getValue(RESOLVED_UNIT5);
457 if (unit != null) {
458 return unit;
459 }
460 unit = entry.getValue(RESOLVED_UNIT6);
461 if (unit != null) {
462 return unit;
463 }
464 unit = entry.getValue(RESOLVED_UNIT7);
465 if (unit != null) {
466 return unit;
467 }
468 unit = entry.getValue(RESOLVED_UNIT8);
469 if (unit != null) {
470 return unit;
471 }
472 unit = entry.getValue(RESOLVED_UNIT9);
473 if (unit != null) {
474 return unit;
475 }
476 unit = entry.getValue(RESOLVED_UNIT10);
477 if (unit != null) {
478 return unit;
479 }
480 unit = entry.getValue(RESOLVED_UNIT11);
481 if (unit != null) {
482 return unit;
483 }
484 unit = entry.getValue(RESOLVED_UNIT12);
485 if (unit != null) {
486 return unit;
487 }
488 return entry.getValue(RESOLVED_UNIT);
489 }
490
491 /**
492 * Return a list of the result descriptors whose state should be displayed for
493 * the given cache [entry].
494 */
495 List<ResultDescriptor> _getExpectedResults(CacheEntry entry) {
496 AnalysisTarget target = entry.target;
497 Set<ResultDescriptor> results = entry.nonInvalidResults.toSet();
498 if (target is Source) {
499 String name = target.shortName;
500 results.add(CONTENT);
501 results.add(LINE_INFO);
502 results.add(MODIFICATION_TIME);
503 if (AnalysisEngine.isDartFileName(name)) {
504 results.add(BUILD_DIRECTIVES_ERRORS);
505 results.add(BUILD_LIBRARY_ERRORS);
506 results.add(CONTAINING_LIBRARIES);
507 results.add(DART_ERRORS);
508 results.add(EXPLICITLY_IMPORTED_LIBRARIES);
509 results.add(EXPORT_SOURCE_CLOSURE);
510 results.add(EXPORTED_LIBRARIES);
511 results.add(IMPORTED_LIBRARIES);
512 results.add(INCLUDED_PARTS);
513 results.add(IS_LAUNCHABLE);
514 results.add(LIBRARY_ELEMENT1);
515 results.add(LIBRARY_ELEMENT2);
516 results.add(LIBRARY_ELEMENT3);
517 results.add(LIBRARY_ELEMENT4);
518 results.add(LIBRARY_ELEMENT5);
519 results.add(LIBRARY_ELEMENT6);
520 results.add(LIBRARY_ELEMENT);
521 results.add(LIBRARY_ERRORS_READY);
522 results.add(PARSE_ERRORS);
523 results.add(PARSED_UNIT);
524 results.add(SCAN_ERRORS);
525 results.add(SOURCE_KIND);
526 results.add(TOKEN_STREAM);
527 results.add(UNITS);
528 } else if (AnalysisEngine.isHtmlFileName(name)) {
529 results.add(DART_SCRIPTS);
530 results.add(HTML_DOCUMENT);
531 results.add(HTML_DOCUMENT_ERRORS);
532 results.add(HTML_ERRORS);
533 results.add(REFERENCED_LIBRARIES);
534 } else if (AnalysisEngine.isAnalysisOptionsFileName(name)) {
535 results.add(ANALYSIS_OPTIONS_ERRORS);
536 }
537 } else if (target is LibrarySpecificUnit) {
538 results.add(COMPILATION_UNIT_CONSTANTS);
539 results.add(COMPILATION_UNIT_ELEMENT);
540 results.add(HINTS);
541 results.add(LINTS);
542 results.add(INFERABLE_STATIC_VARIABLES_IN_UNIT);
543 results.add(LIBRARY_UNIT_ERRORS);
544 results.add(RESOLVE_DIRECTIVES_ERRORS);
545 results.add(RESOLVE_TYPE_NAMES_ERRORS);
546 results.add(RESOLVE_TYPE_BOUNDS_ERRORS);
547 results.add(RESOLVE_UNIT_ERRORS);
548 results.add(RESOLVED_UNIT1);
549 results.add(RESOLVED_UNIT2);
550 results.add(RESOLVED_UNIT3);
551 results.add(RESOLVED_UNIT4);
552 results.add(RESOLVED_UNIT5);
553 results.add(RESOLVED_UNIT6);
554 results.add(RESOLVED_UNIT7);
555 results.add(RESOLVED_UNIT8);
556 results.add(RESOLVED_UNIT9);
557 results.add(RESOLVED_UNIT10);
558 results.add(RESOLVED_UNIT11);
559 results.add(RESOLVED_UNIT12);
560 results.add(RESOLVED_UNIT);
561 results.add(STRONG_MODE_ERRORS);
562 results.add(USED_IMPORTED_ELEMENTS);
563 results.add(USED_LOCAL_ELEMENTS);
564 results.add(VARIABLE_REFERENCE_ERRORS);
565 results.add(VERIFY_ERRORS);
566 } else if (target is ConstantEvaluationTarget) {
567 results.add(CONSTANT_DEPENDENCIES);
568 results.add(CONSTANT_VALUE);
569 if (target is VariableElement) {
570 results.add(INFERABLE_STATIC_VARIABLE_DEPENDENCIES);
571 results.add(INFERRED_STATIC_VARIABLE);
572 }
573 } else if (target is AnalysisContextTarget) {
574 results.add(TYPE_PROVIDER);
575 }
576 return results.toList();
577 }
578
579 /**
580 * Return the context for the SDK whose descriptor is encoded to be the same
581 * as the given [contextFilter]. The [analysisServer] is used to access the
582 * SDKs.
583 */
584 AnalysisContext _getSdkContext(
585 AnalysisServer analysisServer, String contextFilter) {
586 DartSdkManager manager = analysisServer.sdkManager;
587 List<SdkDescription> descriptors = manager.sdkDescriptors;
588 for (SdkDescription descriptor in descriptors) {
589 if (contextFilter == _encodeSdkDescriptor(descriptor)) {
590 return manager.getSdk(descriptor, () => null)?.context;
591 }
592 }
593 return null;
594 }
595
596 /**
597 * Return `true` if the given analysis [context] has at least one entry with
598 * an exception.
599 */
600 bool _hasException(InternalAnalysisContext context) {
601 if (context == null) {
602 return false;
603 }
604 MapIterator<AnalysisTarget, CacheEntry> iterator =
605 context.analysisCache.iterator();
606 while (iterator.moveNext()) {
607 CacheEntry entry = iterator.value;
608 if (entry == null || entry.exception != null) {
609 return true;
610 }
611 }
612 return false;
613 }
614
615 /**
616 * Return the folder in the [folderMap] with which the given [context] is
617 * associated.
618 */
619 Folder _keyForValue(
620 Map<Folder, AnalysisContext> folderMap, AnalysisContext context) {
621 for (Folder folder in folderMap.keys) {
622 if (folderMap[folder] == context) {
623 return folder;
624 }
625 }
626 return null;
627 }
628
629 /**
630 * Return a response displaying overall performance information.
631 */
632 void _returnAnalysisPerformance(HttpRequest request) {
633 AnalysisServer analysisServer = _server.analysisServer;
634 if (analysisServer == null) {
635 return _returnFailure(request, 'Analysis server is not running');
636 }
637 _writeResponse(request, (StringBuffer buffer) {
638 _writePage(buffer, 'Analysis Server - Analysis Performance', [],
639 (StringBuffer buffer) {
640 buffer.write('<h3>Analysis Performance</h3>');
641 _writeTwoColumns(buffer, (StringBuffer buffer) {
642 //
643 // Write performance tags.
644 //
645 buffer.write('<p><b>Performance tag data</b></p>');
646 buffer.write(
647 '<table style="border-collapse: separate; border-spacing: 10px 5px ;">');
648 _writeRow(buffer, ['Time (in ms)', 'Percent', 'Tag name'],
649 header: true);
650 // prepare sorted tags
651 List<PerformanceTag> tags = PerformanceTag.all.toList();
652 tags.remove(ServerPerformanceStatistics.idle);
653 tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
654 // prepare total time
655 int totalTagTime = 0;
656 tags.forEach((PerformanceTag tag) {
657 totalTagTime += tag.elapsedMs;
658 });
659 // write rows
660 void writeRow(PerformanceTag tag) {
661 double percent = (tag.elapsedMs * 100) / totalTagTime;
662 String percentStr = '${percent.toStringAsFixed(2)}%';
663 _writeRow(buffer, [tag.elapsedMs, percentStr, tag.label],
664 classes: ["right", "right", null]);
665 }
666
667 tags.forEach(writeRow);
668 buffer.write('</table>');
669 //
670 // Write target counts.
671 //
672 void incrementCount(Map<String, int> counts, String key) {
673 int count = counts[key];
674 if (count == null) {
675 count = 1;
676 } else {
677 count++;
678 }
679 counts[key] = count;
680 }
681
682 Set<AnalysisTarget> countedTargets = new HashSet<AnalysisTarget>();
683 Map<String, int> sourceTypeCounts = new HashMap<String, int>();
684 Map<String, int> typeCounts = new HashMap<String, int>();
685 int explicitSourceCount = 0;
686 int explicitLineInfoCount = 0;
687 int explicitLineCount = 0;
688 int implicitSourceCount = 0;
689 int implicitLineInfoCount = 0;
690 int implicitLineCount = 0;
691 for (InternalAnalysisContext context
692 in analysisServer.analysisContexts) {
693 Set<Source> explicitSources = new HashSet<Source>();
694 Set<Source> implicitSources = new HashSet<Source>();
695 AnalysisCache cache = context.analysisCache;
696 MapIterator<AnalysisTarget, CacheEntry> iterator = cache.iterator();
697 while (iterator.moveNext()) {
698 AnalysisTarget target = iterator.key;
699 if (countedTargets.add(target)) {
700 if (target is Source) {
701 String name = target.fullName;
702 String sourceName;
703 if (AnalysisEngine.isDartFileName(name)) {
704 if (iterator.value.explicitlyAdded) {
705 explicitSources.add(target);
706 sourceName = 'Dart file (explicit)';
707 } else {
708 implicitSources.add(target);
709 sourceName = 'Dart file (implicit)';
710 }
711 } else if (AnalysisEngine.isHtmlFileName(name)) {
712 if (iterator.value.explicitlyAdded) {
713 sourceName = 'Html file (explicit)';
714 } else {
715 sourceName = 'Html file (implicit)';
716 }
717 } else {
718 if (iterator.value.explicitlyAdded) {
719 sourceName = 'Unknown file (explicit)';
720 } else {
721 sourceName = 'Unknown file (implicit)';
722 }
723 }
724 incrementCount(sourceTypeCounts, sourceName);
725 } else if (target is ConstantEvaluationTarget) {
726 incrementCount(typeCounts, 'ConstantEvaluationTarget');
727 } else {
728 String typeName = target.runtimeType.toString();
729 incrementCount(typeCounts, typeName);
730 }
731 }
732 }
733
734 int lineCount(Set<Source> sources, bool explicit) {
735 return sources.fold(0, (int previousTotal, Source source) {
736 LineInfo lineInfo = context.getLineInfo(source);
737 if (lineInfo is LineInfoWithCount) {
738 if (explicit) {
739 explicitLineInfoCount++;
740 } else {
741 implicitLineInfoCount++;
742 }
743 return previousTotal + lineInfo.lineCount;
744 } else {
745 return previousTotal;
746 }
747 });
748 }
749
750 explicitSourceCount += explicitSources.length;
751 explicitLineCount += lineCount(explicitSources, true);
752 implicitSourceCount += implicitSources.length;
753 implicitLineCount += lineCount(implicitSources, false);
754 }
755 List<String> sourceTypeNames = sourceTypeCounts.keys.toList();
756 sourceTypeNames.sort();
757 List<String> typeNames = typeCounts.keys.toList();
758 typeNames.sort();
759
760 buffer.write('<p><b>Target counts</b></p>');
761 buffer.write(
762 '<table style="border-collapse: separate; border-spacing: 10px 5px ;">');
763 _writeRow(buffer, ['Target', 'Count'], header: true);
764 for (String sourceTypeName in sourceTypeNames) {
765 _writeRow(
766 buffer, [sourceTypeName, sourceTypeCounts[sourceTypeName]],
767 classes: [null, "right"]);
768 }
769 for (String typeName in typeNames) {
770 _writeRow(buffer, [typeName, typeCounts[typeName]],
771 classes: [null, "right"]);
772 }
773 buffer.write('</table>');
774
775 buffer.write('<p><b>Line counts</b></p>');
776 buffer.write(
777 '<table style="border-collapse: separate; border-spacing: 10px 5px ;">');
778 _writeRow(buffer, ['Kind', 'Lines of Code', 'Source Counts'],
779 header: true);
780 _writeRow(buffer, [
781 'Explicit',
782 explicitLineCount.toString(),
783 '$explicitLineInfoCount / $explicitSourceCount'
784 ], classes: [
785 null,
786 "right"
787 ]);
788 _writeRow(buffer, [
789 'Implicit',
790 implicitLineCount.toString(),
791 '$implicitLineInfoCount / $implicitSourceCount'
792 ], classes: [
793 null,
794 "right"
795 ]);
796 _writeRow(buffer, [
797 'Total',
798 (explicitLineCount + implicitLineCount).toString(),
799 '${explicitLineInfoCount + implicitLineInfoCount} / ${explicitSource Count + implicitSourceCount}'
800 ], classes: [
801 null,
802 "right"
803 ]);
804 buffer.write('</table>');
805
806 Map<ResultDescriptor, int> recomputedCounts =
807 CacheEntry.recomputedCounts;
808 List<ResultDescriptor> descriptors = recomputedCounts.keys.toList();
809 descriptors.sort(ResultDescriptor.SORT_BY_NAME);
810 buffer.write('<p><b>Results computed after being flushed</b></p>');
811 buffer.write(
812 '<table style="border-collapse: separate; border-spacing: 10px 5px ;">');
813 _writeRow(buffer, ['Result', 'Count'], header: true);
814 for (ResultDescriptor descriptor in descriptors) {
815 _writeRow(buffer, [descriptor.name, recomputedCounts[descriptor]],
816 classes: [null, "right"]);
817 }
818 buffer.write('</table>');
819
820 {
821 buffer.write('<p><b>Cache consistency statistics</b></p>');
822 buffer.write(
823 '<table style="border-collapse: separate; border-spacing: 10px 5 px;">');
824 _writeRow(buffer, ['Name', 'Count'], header: true);
825 _writeRow(buffer, [
826 'Changed',
827 PerformanceStatistics
828 .cacheConsistencyValidationStatistics.numOfChanged
829 ], classes: [
830 null,
831 "right"
832 ]);
833 _writeRow(buffer, [
834 'Removed',
835 PerformanceStatistics
836 .cacheConsistencyValidationStatistics.numOfRemoved
837 ], classes: [
838 null,
839 "right"
840 ]);
841 buffer.write('</table>');
842 }
843 }, (StringBuffer buffer) {
844 //
845 // Write task model timing information.
846 //
847 buffer.write('<p><b>Task performance data</b></p>');
848 buffer.write(
849 '<table style="border-collapse: separate; border-spacing: 10px 5px ;">');
850 _writeRow(
851 buffer,
852 [
853 'Task Name',
854 'Count',
855 'Total Time (in ms)',
856 'Average Time (in ms)'
857 ],
858 header: true);
859
860 Map<Type, int> countMap = AnalysisTask.countMap;
861 Map<Type, Stopwatch> stopwatchMap = AnalysisTask.stopwatchMap;
862 List<Type> taskClasses = stopwatchMap.keys.toList();
863 taskClasses.sort((Type first, Type second) =>
864 first.toString().compareTo(second.toString()));
865 int totalTaskTime = 0;
866 taskClasses.forEach((Type taskClass) {
867 int count = countMap[taskClass];
868 if (count == null) {
869 count = 0;
870 }
871 int taskTime = stopwatchMap[taskClass].elapsedMilliseconds;
872 totalTaskTime += taskTime;
873 _writeRow(buffer, [
874 taskClass.toString(),
875 count,
876 taskTime,
877 count <= 0 ? '-' : (taskTime / count).toStringAsFixed(3)
878 ], classes: [
879 null,
880 "right",
881 "right",
882 "right"
883 ]);
884 });
885 _writeRow(buffer, ['Total', '-', totalTaskTime, '-'],
886 classes: [null, "right", "right", "right"]);
887 buffer.write('</table>');
888 });
889 });
890 });
891 }
892
893 /**
894 * Return a response containing information about an AST structure.
895 */
896 void _returnAst(HttpRequest request) {
897 AnalysisServer analysisServer = _server.analysisServer;
898 if (analysisServer == null) {
899 return _returnFailure(request, 'Analysis server not running');
900 }
901 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
902 if (contextFilter == null) {
903 return _returnFailure(
904 request, 'Query parameter $CONTEXT_QUERY_PARAM required');
905 }
906 Folder folder = _findFolder(analysisServer, contextFilter);
907 if (folder == null) {
908 return _returnFailure(request, 'Invalid context: $contextFilter');
909 }
910 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM];
911 if (sourceUri == null) {
912 return _returnFailure(
913 request, 'Query parameter $SOURCE_QUERY_PARAM required');
914 }
915
916 InternalAnalysisContext context = analysisServer.folderMap[folder];
917
918 _writeResponse(request, (StringBuffer buffer) {
919 _writePage(buffer, 'Analysis Server - AST Structure',
920 ['Context: $contextFilter', 'File: $sourceUri'], (HttpResponse) {
921 Source source = context.sourceFactory.forUri(sourceUri);
922 if (source == null) {
923 buffer.write('<p>Not found.</p>');
924 return;
925 }
926 List<Source> libraries = context.getLibrariesContaining(source);
927 for (Source library in libraries) {
928 AnalysisTarget target = new LibrarySpecificUnit(library, source);
929 CacheEntry entry = context.analysisCache.get(target);
930 buffer.write('<b>$target</b><br>');
931 if (entry == null) {
932 buffer.write('<p>Not found.</p>');
933 continue;
934 }
935 CompilationUnit ast = _getAnyAst(entry);
936 if (ast == null) {
937 buffer.write('<p>null</p>');
938 continue;
939 }
940 AstWriter writer = new AstWriter(buffer);
941 ast.accept(writer);
942 if (writer.exceptions.isNotEmpty) {
943 buffer.write('<h3>Exceptions while creating page</h3>');
944 for (CaughtException exception in writer.exceptions) {
945 _writeException(buffer, exception);
946 }
947 }
948 }
949 });
950 });
951 }
952
953 /**
954 * Return a response containing information about a single source file in the
955 * cache.
956 */
957 void _returnCacheEntry(HttpRequest request) {
958 AnalysisServer analysisServer = _server.analysisServer;
959 if (analysisServer == null) {
960 return _returnFailure(request, 'Analysis server not running');
961 }
962 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
963 if (contextFilter == null) {
964 return _returnFailure(
965 request, 'Query parameter $CONTEXT_QUERY_PARAM required');
966 }
967 InternalAnalysisContext context = null;
968 Folder folder = _findFolder(analysisServer, contextFilter);
969 if (folder == null) {
970 context = _getSdkContext(analysisServer, contextFilter);
971 if (context == null) {
972 return _returnFailure(request, 'Invalid context: $contextFilter');
973 }
974 return _returnFailure(request,
975 'Cannot view cache entries from an SDK context: $contextFilter');
976 } else {
977 context = analysisServer.folderMap[folder];
978 }
979 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM];
980 if (sourceUri == null) {
981 return _returnFailure(
982 request, 'Query parameter $SOURCE_QUERY_PARAM required');
983 }
984
985 List<Folder> allContexts = <Folder>[];
986 Map<Folder, List<CacheEntry>> entryMap =
987 new HashMap<Folder, List<CacheEntry>>();
988 StringBuffer invalidKeysBuffer = new StringBuffer();
989 analysisServer.folderMap.forEach((Folder folder, AnalysisContext context) {
990 Source source = context.sourceFactory.forUri(sourceUri);
991 if (source != null) {
992 MapIterator<AnalysisTarget, CacheEntry> iterator =
993 (context as InternalAnalysisContext).analysisCache.iterator();
994 while (iterator.moveNext()) {
995 if (source == iterator.key.source) {
996 if (!allContexts.contains(folder)) {
997 allContexts.add(folder);
998 }
999 List<CacheEntry> entries = entryMap[folder];
1000 if (entries == null) {
1001 entries = <CacheEntry>[];
1002 entryMap[folder] = entries;
1003 }
1004 CacheEntry value = iterator.value;
1005 if (value == null) {
1006 if (invalidKeysBuffer.isNotEmpty) {
1007 invalidKeysBuffer.write(', ');
1008 }
1009 invalidKeysBuffer.write(iterator.key.toString());
1010 } else {
1011 entries.add(value);
1012 }
1013 }
1014 }
1015 }
1016 });
1017 allContexts.sort((Folder firstFolder, Folder secondFolder) =>
1018 firstFolder.path.compareTo(secondFolder.path));
1019
1020 _writeResponse(request, (StringBuffer buffer) {
1021 _writePage(buffer, 'Analysis Server - Cache Entry',
1022 ['Context: $contextFilter', 'File: $sourceUri'], (HttpResponse) {
1023 if (invalidKeysBuffer.isNotEmpty) {
1024 buffer.write('<h3>Targets with null Entries</h3><p>');
1025 buffer.write(invalidKeysBuffer.toString());
1026 buffer.write('</p>');
1027 }
1028 List<CacheEntry> entries = entryMap[folder];
1029 buffer.write('<h3>Analyzing Contexts</h3><p>');
1030 bool first = true;
1031 allContexts.forEach((Folder folder) {
1032 if (first) {
1033 first = false;
1034 } else {
1035 buffer.write('<br>');
1036 }
1037 InternalAnalysisContext analyzingContext =
1038 analysisServer.folderMap[folder];
1039 if (analyzingContext == context) {
1040 buffer.write(folder.path);
1041 } else {
1042 buffer.write(makeLink(
1043 CACHE_ENTRY_PATH,
1044 {
1045 CONTEXT_QUERY_PARAM: folder.path,
1046 SOURCE_QUERY_PARAM: sourceUri
1047 },
1048 HTML_ESCAPE.convert(folder.path)));
1049 }
1050 if (entries == null) {
1051 buffer.write(' (file does not exist)');
1052 } else {
1053 CacheEntry sourceEntry = entries
1054 .firstWhere((CacheEntry entry) => entry.target is Source);
1055 if (sourceEntry == null) {
1056 buffer.write(' (missing source entry)');
1057 } else if (sourceEntry.explicitlyAdded) {
1058 buffer.write(' (explicit)');
1059 } else {
1060 buffer.write(' (implicit)');
1061 }
1062 }
1063 });
1064 buffer.write('</p>');
1065
1066 if (entries == null) {
1067 buffer.write('<p>Not being analyzed in this context.</p>');
1068 return;
1069 }
1070 for (CacheEntry entry in entries) {
1071 Map<String, String> linkParameters = <String, String>{
1072 CONTEXT_QUERY_PARAM: contextFilter,
1073 SOURCE_QUERY_PARAM: sourceUri
1074 };
1075 List<ResultDescriptor> results = _getExpectedResults(entry);
1076 results.sort(ResultDescriptor.SORT_BY_NAME);
1077
1078 buffer.write('<h3>');
1079 buffer.write(HTML_ESCAPE.convert(entry.target.toString()));
1080 buffer.write('</h3>');
1081 buffer.write('<p>time</p><blockquote><p>Value</p><blockquote>');
1082 buffer.write(entry.modificationTime);
1083 buffer.write('</blockquote></blockquote>');
1084 for (ResultDescriptor result in results) {
1085 ResultData data = entry.getResultData(result);
1086 CacheState state = entry.getState(result);
1087 String descriptorName = HTML_ESCAPE.convert(result.toString());
1088 String descriptorState = HTML_ESCAPE.convert(state.toString());
1089 buffer
1090 .write('<p>$descriptorName ($descriptorState)</p><blockquote>');
1091 if (state == CacheState.VALID) {
1092 buffer.write('<p>Value</p><blockquote>');
1093 try {
1094 _writeValueAsHtml(
1095 buffer, entry.getValue(result), linkParameters);
1096 } catch (exception) {
1097 buffer.write('(${HTML_ESCAPE.convert(exception.toString())})');
1098 }
1099 buffer.write('</blockquote>');
1100 }
1101 _writeTargetedResults(buffer, 'Depends on', data.dependedOnResults);
1102 _writeTargetedResults(
1103 buffer, 'Depended on by', data.dependentResults);
1104 buffer.write('</blockquote>');
1105 }
1106 if (entry.exception != null) {
1107 buffer.write('<dt>exception</dt><dd>');
1108 _writeException(buffer, entry.exception);
1109 buffer.write('</dd>');
1110 }
1111 }
1112 });
1113 });
1114 }
1115
1116 /**
1117 * Return a response indicating the set of source files in a certain cache
1118 * state.
1119 */
1120 void _returnCacheState(HttpRequest request) {
1121 AnalysisServer analysisServer = _server.analysisServer;
1122 if (analysisServer == null) {
1123 return _returnFailure(request, 'Analysis server not running');
1124 }
1125 // Figure out which context is being searched within.
1126 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
1127 if (contextFilter == null) {
1128 return _returnFailure(
1129 request, 'Query parameter $CONTEXT_QUERY_PARAM required');
1130 }
1131 // Figure out what CacheState is being searched for.
1132 String stateQueryParam = request.uri.queryParameters[STATE_QUERY_PARAM];
1133 if (stateQueryParam == null) {
1134 return _returnFailure(
1135 request, 'Query parameter $STATE_QUERY_PARAM required');
1136 }
1137 CacheState stateFilter = null;
1138 for (CacheState value in CacheState.values) {
1139 if (value.toString() == stateQueryParam) {
1140 stateFilter = value;
1141 }
1142 }
1143 if (stateFilter == null) {
1144 return _returnFailure(
1145 request, 'Query parameter $STATE_QUERY_PARAM is invalid');
1146 }
1147 // Figure out which descriptor is being searched for.
1148 String descriptorFilter =
1149 request.uri.queryParameters[DESCRIPTOR_QUERY_PARAM];
1150 if (descriptorFilter == null) {
1151 return _returnFailure(
1152 request, 'Query parameter $DESCRIPTOR_QUERY_PARAM required');
1153 }
1154
1155 // TODO(brianwilkerson) Figure out how to convert the 'descriptorFilter' to
1156 // a ResultDescriptor so that we can query the state, then uncomment the
1157 // code below that computes and prints the list of links.
1158 // Folder folder = _findFolder(analysisServer, contextFilter);
1159 // InternalAnalysisContext context = analysisServer.folderMap[folder];
1160 // List<String> links = <String>[];
1161 // MapIterator<AnalysisTarget, CacheEntry> iterator = context.analysisCache.i terator();
1162 // while (iterator.moveNext()) {
1163 // Source source = iterator.key.source;
1164 // if (source != null) {
1165 // CacheEntry entry = iterator.value;
1166 // if (entry.getState(result) == stateFilter) {
1167 // String link = makeLink(CACHE_ENTRY_PATH, {
1168 // CONTEXT_QUERY_PARAM: folder.path,
1169 // SOURCE_QUERY_PARAM: source.uri.toString()
1170 // }, HTML_ESCAPE.convert(source.fullName));
1171 // links.add(link);
1172 // }
1173 // }
1174 // }
1175
1176 _writeResponse(request, (StringBuffer buffer) {
1177 _writePage(buffer, 'Analysis Server - Cache Search', [
1178 'Context: $contextFilter',
1179 'Descriptor: ${HTML_ESCAPE.convert(descriptorFilter)}',
1180 'State: ${HTML_ESCAPE.convert(stateQueryParam)}'
1181 ], (StringBuffer buffer) {
1182 buffer.write('<p>Cache search is not yet implemented.</p>');
1183 // buffer.write('<p>${links.length} files found</p>');
1184 // buffer.write('<ul>');
1185 // links.forEach((String link) {
1186 // buffer.write('<li>$link</li>');
1187 // });
1188 // buffer.write('</ul>');
1189 });
1190 });
1191 }
1192
1193 /**
1194 * Return a response displaying overall performance information.
1195 */
1196 void _returnCommunicationPerformance(HttpRequest request) {
1197 AnalysisServer analysisServer = _server.analysisServer;
1198 if (analysisServer == null) {
1199 return _returnFailure(request, 'Analysis server is not running');
1200 }
1201 _writeResponse(request, (StringBuffer buffer) {
1202 _writePage(buffer, 'Analysis Server - Communication Performance', [],
1203 (StringBuffer buffer) {
1204 buffer.write('<h3>Communication Performance</h3>');
1205 _writeTwoColumns(buffer, (StringBuffer buffer) {
1206 ServerPerformance perf = analysisServer.performanceDuringStartup;
1207 int requestCount = perf.requestCount;
1208 num averageLatency = requestCount > 0
1209 ? (perf.requestLatency / requestCount).round()
1210 : 0;
1211 int maximumLatency = perf.maxLatency;
1212 num slowRequestPercent = requestCount > 0
1213 ? (perf.slowRequestCount * 100 / requestCount).round()
1214 : 0;
1215 buffer.write('<h4>Startup</h4>');
1216 buffer.write('<table>');
1217 _writeRow(buffer, [requestCount, 'requests'],
1218 classes: ["right", null]);
1219 _writeRow(buffer, [averageLatency, 'ms average latency'],
1220 classes: ["right", null]);
1221 _writeRow(buffer, [maximumLatency, 'ms maximum latency'],
1222 classes: ["right", null]);
1223 _writeRow(buffer, [slowRequestPercent, '% > 150 ms latency'],
1224 classes: ["right", null]);
1225 if (analysisServer.performanceAfterStartup != null) {
1226 int startupTime = analysisServer.performanceAfterStartup.startTime -
1227 perf.startTime;
1228 _writeRow(
1229 buffer, [startupTime, 'ms for initial analysis to complete']);
1230 }
1231 buffer.write('</table>');
1232 }, (StringBuffer buffer) {
1233 ServerPerformance perf = analysisServer.performanceAfterStartup;
1234 if (perf == null) {
1235 return;
1236 }
1237 int requestCount = perf.requestCount;
1238 num averageLatency = requestCount > 0
1239 ? (perf.requestLatency * 10 / requestCount).round() / 10
1240 : 0;
1241 int maximumLatency = perf.maxLatency;
1242 num slowRequestPercent = requestCount > 0
1243 ? (perf.slowRequestCount * 100 / requestCount).round()
1244 : 0;
1245 buffer.write('<h4>Current</h4>');
1246 buffer.write('<table>');
1247 _writeRow(buffer, [requestCount, 'requests'],
1248 classes: ["right", null]);
1249 _writeRow(buffer, [averageLatency, 'ms average latency'],
1250 classes: ["right", null]);
1251 _writeRow(buffer, [maximumLatency, 'ms maximum latency'],
1252 classes: ["right", null]);
1253 _writeRow(buffer, [slowRequestPercent, '% > 150 ms latency'],
1254 classes: ["right", null]);
1255 buffer.write('</table>');
1256 });
1257 });
1258 });
1259 }
1260
1261 /**
1262 * Return a response displaying code completion information.
1263 */
1264 void _returnCompletionInfo(HttpRequest request) {
1265 String value = request.requestedUri.queryParameters['index'];
1266 int index = value != null ? int.parse(value, onError: (_) => 0) : 0;
1267 _writeResponse(request, (StringBuffer buffer) {
1268 _writePage(buffer, 'Analysis Server - Completion Stats', [],
1269 (StringBuffer buffer) {
1270 _writeCompletionPerformanceDetail(buffer, index);
1271 _writeCompletionPerformanceList(buffer);
1272 });
1273 });
1274 }
1275
1276 /**
1277 * Return a response displaying diagnostic information for a single context.
1278 */
1279 void _returnContextDiagnostics(HttpRequest request) {
1280 AnalysisServer analysisServer = _server.analysisServer;
1281 if (analysisServer == null) {
1282 return _returnFailure(request, 'Analysis server is not running');
1283 }
1284 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
1285 if (contextFilter == null) {
1286 return _returnFailure(
1287 request, 'Query parameter $CONTEXT_QUERY_PARAM required');
1288 }
1289 InternalAnalysisContext context = null;
1290 Folder folder = _findFolder(analysisServer, contextFilter);
1291 if (folder == null) {
1292 context = _getSdkContext(analysisServer, contextFilter);
1293 if (context == null) {
1294 return _returnFailure(request, 'Invalid context: $contextFilter');
1295 }
1296 } else {
1297 context = analysisServer.folderMap[folder];
1298 }
1299
1300 _writeResponse(request, (StringBuffer buffer) {
1301 _writePage(buffer, 'Analysis Server - Context Diagnostics',
1302 ['Context: $contextFilter'], (StringBuffer buffer) {
1303 _writeContextDiagnostics(buffer, context, contextFilter);
1304 });
1305 });
1306 }
1307
1308 /**
1309 * Return a response containing information about a single source file in the
1310 * cache.
1311 */
1312 void _returnContextInfo(HttpRequest request) {
1313 AnalysisServer analysisServer = _server.analysisServer;
1314 if (analysisServer == null) {
1315 return _returnFailure(request, 'Analysis server not running');
1316 }
1317 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
1318 if (contextFilter == null) {
1319 return _returnFailure(
1320 request, 'Query parameter $CONTEXT_QUERY_PARAM required');
1321 }
1322 InternalAnalysisContext context = null;
1323 Folder folder = _findFolder(analysisServer, contextFilter);
1324 if (folder == null) {
1325 context = _getSdkContext(analysisServer, contextFilter);
1326 if (context == null) {
1327 return _returnFailure(request, 'Invalid context: $contextFilter');
1328 }
1329 } else {
1330 context = analysisServer.folderMap[folder];
1331 }
1332
1333 List<String> priorityNames = <String>[];
1334 List<String> explicitNames = <String>[];
1335 List<String> implicitNames = <String>[];
1336 Map<String, String> links = new HashMap<String, String>();
1337 List<CaughtException> exceptions = <CaughtException>[];
1338 context.prioritySources.forEach((Source source) {
1339 priorityNames.add(source.fullName);
1340 });
1341 MapIterator<AnalysisTarget, CacheEntry> iterator =
1342 context.analysisCache.iterator(context: context);
1343 while (iterator.moveNext()) {
1344 AnalysisTarget target = iterator.key;
1345 if (target is Source) {
1346 CacheEntry entry = iterator.value;
1347 String sourceName = target.fullName;
1348 if (!links.containsKey(sourceName)) {
1349 CaughtException exception = entry.exception;
1350 if (exception != null) {
1351 exceptions.add(exception);
1352 }
1353 String link = makeLink(
1354 CACHE_ENTRY_PATH,
1355 {
1356 CONTEXT_QUERY_PARAM: contextFilter,
1357 SOURCE_QUERY_PARAM: target.uri.toString()
1358 },
1359 sourceName,
1360 exception != null);
1361 if (entry.explicitlyAdded) {
1362 explicitNames.add(sourceName);
1363 } else {
1364 implicitNames.add(sourceName);
1365 }
1366 links[sourceName] = link;
1367 }
1368 }
1369 }
1370 explicitNames.sort();
1371 implicitNames.sort();
1372
1373 _overlayContents.clear();
1374 context.visitContentCache((String fullName, int stamp, String contents) {
1375 _overlayContents[fullName] = contents;
1376 });
1377
1378 void _writeFiles(
1379 StringBuffer buffer, String title, List<String> fileNames) {
1380 buffer.write('<h3>$title</h3>');
1381 if (fileNames == null || fileNames.isEmpty) {
1382 buffer.write('<p>None</p>');
1383 } else {
1384 buffer.write('<p><table style="width: 100%">');
1385 for (String fileName in fileNames) {
1386 buffer.write('<tr><td>');
1387 buffer.write(links[fileName]);
1388 buffer.write('</td><td>');
1389 if (_overlayContents.containsKey(fileName)) {
1390 buffer.write(
1391 makeLink(OVERLAY_PATH, {PATH_PARAM: fileName}, 'overlay'));
1392 }
1393 buffer.write('</td></tr>');
1394 }
1395 buffer.write('</table></p>');
1396 }
1397 }
1398
1399 void writeOptions(StringBuffer buffer, AnalysisOptionsImpl options,
1400 {void writeAdditionalOptions(StringBuffer buffer)}) {
1401 if (options == null) {
1402 buffer.write('<p>No option information available.</p>');
1403 return;
1404 }
1405 buffer.write('<p>');
1406 _writeOption(
1407 buffer, 'Analyze functon bodies', options.analyzeFunctionBodies);
1408 _writeOption(
1409 buffer, 'Enable strict call checks', options.enableStrictCallChecks);
1410 _writeOption(buffer, 'Enable super mixins', options.enableSuperMixins);
1411 _writeOption(buffer, 'Generate dart2js hints', options.dart2jsHint);
1412 _writeOption(buffer, 'Generate errors in implicit files',
1413 options.generateImplicitErrors);
1414 _writeOption(
1415 buffer, 'Generate errors in SDK files', options.generateSdkErrors);
1416 _writeOption(buffer, 'Generate hints', options.hint);
1417 _writeOption(buffer, 'Incremental resolution', options.incremental);
1418 _writeOption(buffer, 'Incremental resolution with API changes',
1419 options.incrementalApi);
1420 _writeOption(buffer, 'Preserve comments', options.preserveComments);
1421 _writeOption(buffer, 'Strong mode', options.strongMode);
1422 _writeOption(buffer, 'Strong mode hints', options.strongModeHints);
1423 if (writeAdditionalOptions != null) {
1424 writeAdditionalOptions(buffer);
1425 }
1426 buffer.write('</p>');
1427 }
1428
1429 _writeResponse(request, (StringBuffer buffer) {
1430 _writePage(
1431 buffer, 'Analysis Server - Context', ['Context: $contextFilter'],
1432 (StringBuffer buffer) {
1433 buffer.write('<h3>Configuration</h3>');
1434
1435 _writeColumns(buffer, <HtmlGenerator>[
1436 (StringBuffer buffer) {
1437 buffer.write('<p><b>Context Options</b></p>');
1438 writeOptions(buffer, context.analysisOptions);
1439 },
1440 (StringBuffer buffer) {
1441 buffer.write('<p><b>SDK Context Options</b></p>');
1442 DartSdk sdk = context?.sourceFactory?.dartSdk;
1443 writeOptions(buffer, sdk?.context?.analysisOptions,
1444 writeAdditionalOptions: (StringBuffer buffer) {
1445 if (sdk is FolderBasedDartSdk) {
1446 _writeOption(buffer, 'Use summaries', sdk.useSummary);
1447 }
1448 });
1449 },
1450 (StringBuffer buffer) {
1451 List<Linter> lints = context.analysisOptions.lintRules;
1452 buffer.write('<p><b>Lints</b></p>');
1453 if (lints.isEmpty) {
1454 buffer.write('<p>none</p>');
1455 } else {
1456 for (Linter lint in lints) {
1457 buffer.write('<p>');
1458 buffer.write(lint.runtimeType);
1459 buffer.write('</p>');
1460 }
1461 }
1462
1463 List<ErrorProcessor> errorProcessors =
1464 context.analysisOptions.errorProcessors;
1465 int processorCount = errorProcessors?.length ?? 0;
1466 buffer
1467 .write('<p><b>Error Processor count</b>: $processorCount</p>');
1468 }
1469 ]);
1470
1471 SourceFactory sourceFactory = context.sourceFactory;
1472 if (sourceFactory is SourceFactoryImpl) {
1473 buffer.write('<h3>Resolvers</h3>');
1474 for (UriResolver resolver in sourceFactory.resolvers) {
1475 buffer.write('<p>');
1476 buffer.write(resolver.runtimeType);
1477 if (resolver is DartUriResolver) {
1478 DartSdk sdk = resolver.dartSdk;
1479 buffer.write(' (sdk = ');
1480 buffer.write(sdk.runtimeType);
1481 if (sdk is FolderBasedDartSdk) {
1482 buffer.write(' (path = ');
1483 buffer.write(sdk.directory.path);
1484 buffer.write(')');
1485 } else if (sdk is EmbedderSdk) {
1486 buffer.write(' (map = ');
1487 _writeMapOfStringToString(buffer, sdk.urlMappings);
1488 buffer.write(')');
1489 }
1490 buffer.write(')');
1491 } else if (resolver is SdkExtUriResolver) {
1492 buffer.write(' (map = ');
1493 _writeMapOfStringToString(buffer, resolver.urlMappings);
1494 buffer.write(')');
1495 }
1496 buffer.write('</p>');
1497 }
1498 }
1499
1500 _writeFiles(
1501 buffer, 'Priority Files (${priorityNames.length})', priorityNames);
1502 _writeFiles(
1503 buffer,
1504 'Explicitly Analyzed Files (${explicitNames.length})',
1505 explicitNames);
1506 _writeFiles(
1507 buffer,
1508 'Implicitly Analyzed Files (${implicitNames.length})',
1509 implicitNames);
1510
1511 buffer.write('<h3>Exceptions</h3>');
1512 if (exceptions.isEmpty) {
1513 buffer.write('<p>none</p>');
1514 } else {
1515 exceptions.forEach((CaughtException exception) {
1516 _writeException(buffer, exception);
1517 });
1518 }
1519
1520 buffer.write('<h3>Targets Without Entries</h3>');
1521 bool foundEntry = false;
1522 MapIterator<AnalysisTarget, CacheEntry> iterator =
1523 context.analysisCache.iterator(context: context);
1524 while (iterator.moveNext()) {
1525 if (iterator.value == null) {
1526 foundEntry = true;
1527 buffer.write('<p>');
1528 buffer.write(iterator.key.toString());
1529 buffer.write(' (');
1530 buffer.write(iterator.key.runtimeType.toString());
1531 buffer.write(')</p>');
1532 }
1533 }
1534 if (!foundEntry) {
1535 buffer.write('<p>none</p>');
1536 }
1537 });
1538 });
1539 }
1540
1541 /**
1542 * Return a response displaying the results of running a validation report on
1543 * a single context.
1544 */
1545 void _returnContextValidationDiagnostics(HttpRequest request) {
1546 AnalysisServer analysisServer = _server.analysisServer;
1547 if (analysisServer == null) {
1548 return _returnFailure(request, 'Analysis server is not running');
1549 }
1550 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
1551 if (contextFilter == null) {
1552 return _returnFailure(
1553 request, 'Query parameter $CONTEXT_QUERY_PARAM required');
1554 }
1555 InternalAnalysisContext context = null;
1556 Folder folder = _findFolder(analysisServer, contextFilter);
1557 if (folder == null) {
1558 context = _getSdkContext(analysisServer, contextFilter);
1559 if (context == null) {
1560 return _returnFailure(request, 'Invalid context: $contextFilter');
1561 }
1562 } else {
1563 context = analysisServer.folderMap[folder];
1564 }
1565
1566 _writeResponse(request, (StringBuffer buffer) {
1567 _writePage(buffer, 'Analysis Server - Context Validation Diagnostics',
1568 ['Context: $contextFilter'], (StringBuffer buffer) {
1569 _writeContextValidationDiagnostics(buffer, context);
1570 });
1571 });
1572 }
1573
1574 /**
1575 * Return a response displaying diagnostic information.
1576 */
1577 void _returnDiagnosticInfo(HttpRequest request) {
1578 _writeResponse(request, (StringBuffer buffer) {
1579 _writePage(buffer, 'Analysis Server - Diagnostic info', [],
1580 (StringBuffer buffer) {
1581 _writeDiagnosticStatus(buffer);
1582 });
1583 });
1584 }
1585
1586 /**
1587 * Return a response containing information about an element structure.
1588 */
1589 void _returnElement(HttpRequest request) {
1590 AnalysisServer analysisServer = _server.analysisServer;
1591 if (analysisServer == null) {
1592 return _returnFailure(request, 'Analysis server not running');
1593 }
1594 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
1595 if (contextFilter == null) {
1596 return _returnFailure(
1597 request, 'Query parameter $CONTEXT_QUERY_PARAM required');
1598 }
1599 Folder folder = _findFolder(analysisServer, contextFilter);
1600 if (folder == null) {
1601 return _returnFailure(request, 'Invalid context: $contextFilter');
1602 }
1603 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM];
1604 if (sourceUri == null) {
1605 return _returnFailure(
1606 request, 'Query parameter $SOURCE_QUERY_PARAM required');
1607 }
1608
1609 InternalAnalysisContext context = analysisServer.folderMap[folder];
1610
1611 _writeResponse(request, (StringBuffer buffer) {
1612 _writePage(buffer, 'Analysis Server - Element Model', [
1613 'Context: $contextFilter',
1614 'File: $sourceUri'
1615 ], (StringBuffer buffer) {
1616 Source source = context.sourceFactory.forUri(sourceUri);
1617 if (source == null) {
1618 buffer.write('<p>Not found.</p>');
1619 return;
1620 }
1621 CacheEntry entry = context.analysisCache.get(source);
1622 if (entry == null) {
1623 buffer.write('<p>Not found.</p>');
1624 return;
1625 }
1626 LibraryElement element = entry.getValue(LIBRARY_ELEMENT);
1627 if (element == null) {
1628 buffer.write('<p>null</p>');
1629 return;
1630 }
1631 element.accept(new ElementWriter(buffer));
1632 });
1633 });
1634 }
1635
1636 void _returnFailure(HttpRequest request, String message) {
1637 _writeResponse(request, (StringBuffer buffer) {
1638 _writePage(buffer, 'Analysis Server - Failure', [],
1639 (StringBuffer buffer) {
1640 buffer.write(HTML_ESCAPE.convert(message));
1641 });
1642 });
1643 }
1644
1645 void _returnMemoryUsage(HttpRequest request) {
1646 _writeResponse(request, (StringBuffer buffer) {
1647 _writePage(buffer, 'Analysis Server - Memory Use', [],
1648 (StringBuffer buffer) {
1649 AnalysisServer server = _server.analysisServer;
1650 MemoryUseData data = new MemoryUseData();
1651 data.processAnalysisServer(server);
1652 Map<Type, Set> instances = data.instances;
1653 List<Type> instanceTypes = instances.keys.toList();
1654 instanceTypes.sort((Type left, Type right) =>
1655 left.toString().compareTo(right.toString()));
1656 Map<Type, Set> ownerMap = data.ownerMap;
1657 List<Type> ownerTypes = ownerMap.keys.toList();
1658 ownerTypes.sort((Type left, Type right) =>
1659 left.toString().compareTo(right.toString()));
1660
1661 _writeTwoColumns(buffer, (StringBuffer buffer) {
1662 buffer.write('<h3>Instance Counts (reachable from contexts)</h3>');
1663 buffer.write('<table>');
1664 _writeRow(buffer, ['Count', 'Class name'], header: true);
1665 instanceTypes.forEach((Type type) {
1666 _writeRow(buffer, [instances[type].length, type],
1667 classes: ['right', null]);
1668 });
1669 buffer.write('</table>');
1670
1671 buffer.write(
1672 '<h3>Ownership (which classes of objects hold on to others)</h3>') ;
1673 buffer.write('<table>');
1674 _writeRow(buffer, ['Referenced Type', 'Referencing Types'],
1675 header: true);
1676 ownerTypes.forEach((Type type) {
1677 List<String> referencingTypes =
1678 ownerMap[type].map((Type type) => type.toString()).toList();
1679 referencingTypes.sort();
1680 _writeRow(buffer, [type, referencingTypes.join('<br>')]);
1681 });
1682 buffer.write('</table>');
1683
1684 buffer.write('<h3>Other Data</h3>');
1685 buffer.write('<p>');
1686 buffer.write(data.uniqueTargetedResults.length);
1687 buffer.write(' non-equal TargetedResults</p>');
1688 buffer.write('<p>');
1689 buffer.write(data.uniqueLSUs.length);
1690 buffer.write(' non-equal LibrarySpecificUnits</p>');
1691 int count = data.mismatchedTargets.length;
1692 buffer.write('<p>');
1693 buffer.write(count);
1694 buffer.write(' mismatched targets</p>');
1695 if (count < 100) {
1696 for (AnalysisTarget target in data.mismatchedTargets) {
1697 buffer.write(target);
1698 buffer.write('<br>');
1699 }
1700 }
1701 }, (StringBuffer buffer) {
1702 void writeCountMap(String title, Map<Type, int> counts) {
1703 List<Type> classNames = counts.keys.toList();
1704 classNames.sort((Type left, Type right) =>
1705 left.toString().compareTo(right.toString()));
1706
1707 buffer.write('<h3>$title</h3>');
1708 buffer.write('<table>');
1709 _writeRow(buffer, ['Count', 'Class name'], header: true);
1710 classNames.forEach((Type type) {
1711 _writeRow(buffer, [counts[type], type], classes: ['right', null]);
1712 });
1713 buffer.write('</table>');
1714 }
1715
1716 writeCountMap('Directly Held AST Nodes', data.directNodeCounts);
1717 writeCountMap('Indirectly Held AST Nodes', data.indirectNodeCounts);
1718 writeCountMap('Directly Held Elements', data.elementCounts);
1719 });
1720 });
1721 });
1722 }
1723
1724 void _returnOverlayContents(HttpRequest request) {
1725 String path = request.requestedUri.queryParameters[PATH_PARAM];
1726 if (path == null) {
1727 return _returnFailure(request, 'Query parameter $PATH_PARAM required');
1728 }
1729 String contents = _overlayContents[path];
1730
1731 _writeResponse(request, (StringBuffer buffer) {
1732 _writePage(buffer, 'Analysis Server - Overlay', [],
1733 (StringBuffer buffer) {
1734 buffer.write('<pre>${HTML_ESCAPE.convert(contents)}</pre>');
1735 });
1736 });
1737 }
1738
1739 /**
1740 * Return a response displaying overlays information.
1741 */
1742 void _returnOverlaysInfo(HttpRequest request) {
1743 AnalysisServer analysisServer = _server.analysisServer;
1744 if (analysisServer == null) {
1745 return _returnFailure(request, 'Analysis server is not running');
1746 }
1747
1748 _writeResponse(request, (StringBuffer buffer) {
1749 _writePage(buffer, 'Analysis Server - Overlays', [],
1750 (StringBuffer buffer) {
1751 buffer.write('<table border="1">');
1752 _overlayContents.clear();
1753 ContentCache overlayState = analysisServer.overlayState;
1754 overlayState.accept((String fullName, int stamp, String contents) {
1755 buffer.write('<tr>');
1756 String link =
1757 makeLink(OVERLAY_PATH, {PATH_PARAM: fullName}, fullName);
1758 DateTime time = new DateTime.fromMillisecondsSinceEpoch(stamp);
1759 _writeRow(buffer, [link, time]);
1760 _overlayContents[fullName] = contents;
1761 });
1762 int count = _overlayContents.length;
1763 buffer.write('<tr><td colspan="2">Total: $count entries.</td></tr>');
1764 buffer.write('</table>');
1765 });
1766 });
1767 }
1768
1769 /**
1770 * Return a response indicating the status of the analysis server.
1771 */
1772 void _returnServerStatus(HttpRequest request) {
1773 _writeResponse(request, (StringBuffer buffer) {
1774 _writePage(buffer, 'Analysis Server - Status', [], (StringBuffer buffer) {
1775 if (_writeServerStatus(buffer)) {
1776 _writeAnalysisStatus(buffer);
1777 _writeEditStatus(buffer);
1778 _writeExecutionStatus(buffer);
1779 _writePluginStatus(buffer);
1780 _writeRecentOutput(buffer);
1781 }
1782 });
1783 });
1784 }
1785
1786 /**
1787 * Return an error in response to an unrecognized request received by the HTTP
1788 * server.
1789 */
1790 void _returnUnknownRequest(HttpRequest request) {
1791 _writeResponse(request, (StringBuffer buffer) {
1792 _writePage(buffer, 'Analysis Server', [], (StringBuffer buffer) {
1793 buffer.write('<h3>Unknown page: ');
1794 buffer.write(request.uri.path);
1795 buffer.write('</h3>');
1796 buffer.write('''
1797 <p>
1798 You have reached an un-recognized page. If you reached this page by
1799 following a link from a status page, please report the broken link to
1800 the Dart analyzer team:
1801 <a>https://github.com/dart-lang/sdk/issues/new</a>.
1802 </p><p>
1803 If you mistyped the URL, you can correct it or return to
1804 ${makeLink(STATUS_PATH, {}, 'the main status page')}.
1805 </p>''');
1806 });
1807 });
1808 }
1809
1810 /**
1811 * Return a two digit decimal representation of the given non-negative integer
1812 * [value].
1813 */
1814 String _twoDigit(int value) {
1815 if (value < 10) {
1816 return '0$value';
1817 }
1818 return value.toString();
1819 }
1820
1821 /**
1822 * Write the status of the analysis domain (on the main status page) to the
1823 * given [buffer] object.
1824 */
1825 void _writeAnalysisStatus(StringBuffer buffer) {
1826 AnalysisServer analysisServer = _server.analysisServer;
1827 Map<Folder, AnalysisContext> folderMap = analysisServer.folderMap;
1828 List<Folder> folders = folderMap.keys.toList();
1829 folders.sort((Folder first, Folder second) =>
1830 first.shortName.compareTo(second.shortName));
1831 ServerOperationQueue operationQueue = analysisServer.operationQueue;
1832
1833 buffer.write('<h3>Analysis Domain</h3>');
1834 _writeTwoColumns(buffer, (StringBuffer buffer) {
1835 if (operationQueue.isEmpty) {
1836 buffer.write('<p>Status: Done analyzing</p>');
1837 } else {
1838 ServerOperation operation = operationQueue.peek();
1839 if (operation is PerformAnalysisOperation) {
1840 Folder folder = _keyForValue(folderMap, operation.context);
1841 if (folder == null) {
1842 buffer.write('<p>Status: Analyzing in unmapped context</p>');
1843 } else {
1844 buffer.write('<p>Status: Analyzing in ${folder.path}</p>');
1845 }
1846 } else {
1847 buffer.write('<p>Status: Analyzing</p>');
1848 }
1849 }
1850 buffer.write('<p>Using package resolver provider: ');
1851 buffer.write(_server.packageResolverProvider != null);
1852 buffer.write('</p>');
1853 buffer.write('<p>');
1854 buffer.write(makeLink(OVERLAYS_PATH, {}, 'All overlay information'));
1855 buffer.write('</p>');
1856
1857 buffer.write('<p><b>Analysis Contexts</b></p>');
1858 buffer.write('<p>');
1859 bool first = true;
1860 folders.forEach((Folder folder) {
1861 if (first) {
1862 first = false;
1863 } else {
1864 buffer.write('<br>');
1865 }
1866 String key = folder.shortName;
1867 buffer.write(makeLink(CONTEXT_PATH, {CONTEXT_QUERY_PARAM: folder.path},
1868 key, _hasException(folderMap[folder])));
1869 buffer.write(' <small><b>[');
1870 buffer.write(makeLink(CONTEXT_DIAGNOSTICS_PATH,
1871 {CONTEXT_QUERY_PARAM: folder.path}, 'diagnostics'));
1872 buffer.write(']</b></small>');
1873 if (!folder.getChild('.packages').exists) {
1874 buffer.write(' <small>[no .packages file]</small>');
1875 }
1876 });
1877 buffer.write('</p>');
1878 buffer.write('<p><b>SDK Contexts</b></p>');
1879 buffer.write('<p>');
1880 first = true;
1881 DartSdkManager manager = analysisServer.sdkManager;
1882 List<SdkDescription> descriptors = manager.sdkDescriptors;
1883 if (descriptors.isEmpty) {
1884 buffer.write('none');
1885 } else {
1886 Map<String, SdkDescription> sdkMap = <String, SdkDescription>{};
1887 for (SdkDescription descriptor in descriptors) {
1888 sdkMap[descriptor.toString()] = descriptor;
1889 }
1890 List<String> descriptorNames = sdkMap.keys.toList();
1891 descriptorNames.sort();
1892 for (String name in descriptorNames) {
1893 if (first) {
1894 first = false;
1895 } else {
1896 buffer.write('<br>');
1897 }
1898 SdkDescription descriptor = sdkMap[name];
1899 String contextId = _encodeSdkDescriptor(descriptor);
1900 buffer.write(makeLink(
1901 CONTEXT_PATH,
1902 {CONTEXT_QUERY_PARAM: contextId},
1903 name,
1904 _hasException(manager.getSdk(descriptor, () => null)?.context)));
1905 buffer.write(' <small><b>[');
1906 buffer.write(makeLink(CONTEXT_DIAGNOSTICS_PATH,
1907 {CONTEXT_QUERY_PARAM: contextId}, 'diagnostics'));
1908 buffer.write(']</b></small>');
1909 }
1910 }
1911 buffer.write('</p>');
1912
1913 int freq = AnalysisServer.performOperationDelayFrequency;
1914 String delay = freq > 0 ? '1 ms every $freq ms' : 'off';
1915
1916 buffer.write('<p><b>Performance Data</b></p>');
1917 buffer.write('<p>Perform operation delay: $delay</p>');
1918 buffer.write('<p>');
1919 buffer.write(makeLink(ANALYSIS_PERFORMANCE_PATH, {}, 'Task data'));
1920 buffer.write('</p>');
1921 }, (StringBuffer buffer) {
1922 _writeSubscriptionMap(
1923 buffer, AnalysisService.VALUES, analysisServer.analysisServices);
1924 });
1925 }
1926
1927 /**
1928 * Write multiple columns of information to the given [buffer], where the list
1929 * of [columns] functions are used to generate the content of those columns.
1930 */
1931 void _writeColumns(StringBuffer buffer, List<HtmlGenerator> columns) {
1932 buffer
1933 .write('<table class="column"><tr class="column"><td class="column">');
1934 int count = columns.length;
1935 for (int i = 0; i < count; i++) {
1936 if (i > 0) {
1937 buffer.write('</td><td class="column">');
1938 }
1939 columns[i](buffer);
1940 }
1941 buffer.write('</td></tr></table>');
1942 }
1943
1944 /**
1945 * Write performance information about a specific completion request
1946 * to the given [buffer] object.
1947 */
1948 void _writeCompletionPerformanceDetail(StringBuffer buffer, int index) {
1949 CompletionDomainHandler handler = _completionDomainHandler;
1950 CompletionPerformance performance;
1951 if (handler != null) {
1952 List<CompletionPerformance> list = handler.performanceList;
1953 if (list != null && list.isNotEmpty) {
1954 performance = list[max(0, min(list.length - 1, index))];
1955 }
1956 }
1957 if (performance == null) {
1958 buffer.write('<h3>Completion Performance Detail</h3>');
1959 buffer.write('<p>No completions yet</p>');
1960 return;
1961 }
1962 buffer.write('<h3>Completion Performance Detail</h3>');
1963 buffer.write('<p>${performance.startTimeAndMs} for ${performance.source}');
1964 buffer.write('<table>');
1965 _writeRow(buffer, ['Elapsed', '', 'Operation'], header: true);
1966 performance.operations.forEach((OperationPerformance op) {
1967 String elapsed = op.elapsed != null ? op.elapsed.toString() : '???';
1968 _writeRow(buffer, [elapsed, '&nbsp;&nbsp;', op.name]);
1969 });
1970 buffer.write('</table>');
1971 buffer.write('<p><b>Compute Cache Performance</b>: ');
1972 if (handler.computeCachePerformance == null) {
1973 buffer.write('none');
1974 } else {
1975 int elapsed = handler.computeCachePerformance.elapsedInMilliseconds;
1976 Source source = handler.computeCachePerformance.source;
1977 buffer.write(' $elapsed ms for $source');
1978 }
1979 buffer.write('</p>');
1980 }
1981
1982 /**
1983 * Write a table showing summary information for the last several
1984 * completion requests to the given [buffer] object.
1985 */
1986 void _writeCompletionPerformanceList(StringBuffer buffer) {
1987 CompletionDomainHandler handler = _completionDomainHandler;
1988 buffer.write('<h3>Completion Performance List</h3>');
1989 if (handler == null) {
1990 return;
1991 }
1992 buffer.write('<table>');
1993 _writeRow(
1994 buffer,
1995 [
1996 'Start Time',
1997 '',
1998 'First (ms)',
1999 '',
2000 'Complete (ms)',
2001 '',
2002 '# Notifications',
2003 '',
2004 '# Suggestions',
2005 '',
2006 'Snippet'
2007 ],
2008 header: true);
2009 int index = 0;
2010 for (CompletionPerformance performance in handler.performanceList) {
2011 String link = makeLink(COMPLETION_PATH, {'index': '$index'},
2012 '${performance.startTimeAndMs}');
2013 _writeRow(buffer, [
2014 link,
2015 '&nbsp;&nbsp;',
2016 performance.firstNotificationInMilliseconds,
2017 '&nbsp;&nbsp;',
2018 performance.elapsedInMilliseconds,
2019 '&nbsp;&nbsp;',
2020 performance.notificationCount,
2021 '&nbsp;&nbsp;',
2022 performance.suggestionCount,
2023 '&nbsp;&nbsp;',
2024 HTML_ESCAPE.convert(performance.snippet)
2025 ]);
2026 ++index;
2027 }
2028
2029 buffer.write('</table>');
2030 buffer.write('''
2031 <p><strong>First (ms)</strong> - the number of milliseconds
2032 from when completion received the request until the first notification
2033 with completion results was queued for sending back to the client.
2034 <p><strong>Complete (ms)</strong> - the number of milliseconds
2035 from when completion received the request until the final notification
2036 with completion results was queued for sending back to the client.
2037 <p><strong># Notifications</strong> - the total number of notifications
2038 sent to the client with completion results for this request.
2039 <p><strong># Suggestions</strong> - the number of suggestions
2040 sent to the client in the first notification, followed by a comma,
2041 followed by the number of suggestions send to the client
2042 in the last notification. If there is only one notification,
2043 then there will be only one number in this column.''');
2044 }
2045
2046 /**
2047 * Write diagnostic information about the given [context] to the given
2048 * [buffer].
2049 */
2050 void _writeContextDiagnostics(StringBuffer buffer,
2051 InternalAnalysisContext context, String contextFilter) {
2052 AnalysisDriver driver = (context as AnalysisContextImpl).driver;
2053 List<WorkItem> workItems = driver.currentWorkOrder?.workItems;
2054
2055 buffer.write('<p>');
2056 buffer.write(makeLink(CONTEXT_VALIDATION_DIAGNOSTICS_PATH,
2057 {CONTEXT_QUERY_PARAM: contextFilter}, 'Run validation'));
2058 buffer.write('</p>');
2059
2060 buffer.write('<h3>Most Recently Perfomed Tasks</h3>');
2061 AnalysisTask.LAST_TASKS.forEach((String description) {
2062 buffer.write('<p>');
2063 buffer.write(description);
2064 buffer.write('</p>');
2065 });
2066
2067 void writeWorkItem(StringBuffer buffer, WorkItem item) {
2068 if (item == null) {
2069 buffer.write('none');
2070 } else {
2071 buffer.write(item.descriptor?.name);
2072 buffer.write(' computing ');
2073 buffer.write(item.spawningResult?.name);
2074 buffer.write(' for ');
2075 buffer.write(item.target);
2076 }
2077 }
2078
2079 buffer.write('<h3>Work Items</h3>');
2080 buffer.write('<p><b>Current:</b> ');
2081 writeWorkItem(buffer, driver.currentWorkOrder?.current);
2082 buffer.write('</p>');
2083 if (workItems != null) {
2084 workItems.reversed.forEach((WorkItem item) {
2085 buffer.write('<p>');
2086 writeWorkItem(buffer, item);
2087 buffer.write('</p>');
2088 });
2089 }
2090 }
2091
2092 /**
2093 * Write diagnostic information about the given [context] to the given
2094 * [buffer].
2095 */
2096 void _writeContextValidationDiagnostics(
2097 StringBuffer buffer, InternalAnalysisContext context) {
2098 Stopwatch stopwatch = new Stopwatch();
2099 stopwatch.start();
2100 ValidationResults results = new ValidationResults(context);
2101 stopwatch.stop();
2102
2103 buffer.write('<h3>Validation Results</h3>');
2104 buffer.write('<p>Re-analysis took ');
2105 buffer.write(stopwatch.elapsedMilliseconds);
2106 buffer.write(' ms</p>');
2107 results.writeOn(buffer);
2108 }
2109
2110 /**
2111 * Write the status of the diagnostic domain to the given [buffer].
2112 */
2113 void _writeDiagnosticStatus(StringBuffer buffer) {
2114 var request = new DiagnosticGetDiagnosticsParams().toRequest('0');
2115
2116 var stopwatch = new Stopwatch();
2117 stopwatch.start();
2118 var response = diagnosticHandler.handleRequest(request);
2119 stopwatch.stop();
2120
2121 int elapsedMs = stopwatch.elapsedMilliseconds;
2122 _diagnosticCallAverage.addSample(elapsedMs);
2123
2124 buffer.write('<h3>Timing</h3>');
2125
2126 buffer.write('<p>getDiagnostic (last call): ');
2127 buffer.write(elapsedMs);
2128 buffer.write(' (ms)</p>');
2129 buffer.write('<p>getDiagnostic (rolling average): ');
2130 buffer.write(_diagnosticCallAverage.value);
2131 buffer.write(' (ms)</p>&nbsp;');
2132
2133 Map json = response.toJson()[Response.RESULT];
2134 List contexts = json['contexts'];
2135 contexts.sort((first, second) => first['name'].compareTo(second['name']));
2136
2137 // Track visited libraries.
2138 Set<LibraryElement> libraries = new HashSet<LibraryElement>();
2139
2140 // Count SDK elements separately.
2141 ElementCounter sdkCounter = new ElementCounter();
2142
2143 for (var context in contexts) {
2144 buffer.write('<p><h3>');
2145 buffer.write(context['name']);
2146 buffer.write('</h3></p>');
2147 buffer.write('<p>explicitFileCount: ');
2148 buffer.write(context['explicitFileCount']);
2149 buffer.write('</p>');
2150 buffer.write('<p>implicitFileCount: ');
2151 buffer.write(context['implicitFileCount']);
2152 buffer.write('</p>');
2153 buffer.write('<p>workItemQueueLength: ');
2154 buffer.write(context['workItemQueueLength']);
2155 buffer.write('</p>');
2156
2157 AnalysisServer server = _server.analysisServer;
2158
2159 if (server != null) {
2160 Folder folder = _findFolder(server, context['name']);
2161 InternalAnalysisContext ac = _server.analysisServer.folderMap[folder];
2162 ElementCounter counter = new ElementCounter();
2163
2164 for (Source source in ac.librarySources) {
2165 LibraryElement libraryElement = ac.getLibraryElement(source);
2166 if (libraries.add(libraryElement)) {
2167 if (libraryElement != null) {
2168 if (libraryElement.isInSdk) {
2169 libraryElement.accept(sdkCounter);
2170 } else {
2171 libraryElement.accept(counter);
2172 }
2173 }
2174 }
2175 }
2176 buffer.write('<p>element count: ');
2177 buffer.write(counter.counts.values
2178 .fold<int>(0, (int prev, int element) => prev + element));
2179 buffer.write('</p>');
2180 buffer.write('<p> (w/docs): ');
2181 buffer.write(counter.elementsWithDocs);
2182 buffer.write('</p>');
2183 buffer.write('<p>total doc span: ');
2184 buffer.write(counter.totalDocSpan);
2185 buffer.write('</p>');
2186 }
2187 }
2188
2189 buffer.write('<p><h3>');
2190 buffer.write('SDK');
2191 buffer.write('</h3></p>');
2192 buffer.write('<p>element count: ');
2193 buffer.write(sdkCounter.counts.values
2194 .fold<int>(0, (int prev, int element) => prev + element));
2195 buffer.write('</p>');
2196 buffer.write('<p> (w/docs): ');
2197 buffer.write(sdkCounter.elementsWithDocs);
2198 buffer.write('</p>');
2199 buffer.write('<p>total doc span: ');
2200 buffer.write(sdkCounter.totalDocSpan);
2201 buffer.write('</p>');
2202 }
2203
2204 /**
2205 * Write the status of the edit domain (on the main status page) to the given
2206 * [buffer].
2207 */
2208 void _writeEditStatus(StringBuffer buffer) {
2209 buffer.write('<h3>Edit Domain</h3>');
2210 _writeTwoColumns(buffer, (StringBuffer buffer) {
2211 buffer.write('<p><b>Performance Data</b></p>');
2212 buffer.write('<p>');
2213 buffer.write(makeLink(COMPLETION_PATH, {}, 'Completion data'));
2214 buffer.write('</p>');
2215 }, (StringBuffer buffer) {});
2216 }
2217
2218 /**
2219 * Write a representation of the given [caughtException] to the given
2220 * [buffer]. If [isCause] is `true`, then the exception was a cause for
2221 * another exception.
2222 */
2223 void _writeException(StringBuffer buffer, CaughtException caughtException,
2224 {bool isCause: false}) {
2225 Object exception = caughtException.exception;
2226
2227 if (exception is AnalysisException) {
2228 buffer.write('<p>');
2229 if (isCause) {
2230 buffer.write('Caused by ');
2231 }
2232 buffer.write(exception.message);
2233 buffer.write('</p>');
2234 _writeStackTrace(buffer, caughtException.stackTrace);
2235 CaughtException cause = exception.cause;
2236 if (cause != null) {
2237 buffer.write('<blockquote>');
2238 _writeException(buffer, cause, isCause: true);
2239 buffer.write('</blockquote>');
2240 }
2241 } else {
2242 buffer.write('<p>');
2243 if (isCause) {
2244 buffer.write('Caused by ');
2245 }
2246 buffer.write(exception.toString());
2247 buffer.write('<p>');
2248 _writeStackTrace(buffer, caughtException.stackTrace);
2249 }
2250 }
2251
2252 /**
2253 * Write the status of the execution domain (on the main status page) to the
2254 * given [buffer].
2255 */
2256 void _writeExecutionStatus(StringBuffer buffer) {
2257 AnalysisServer analysisServer = _server.analysisServer;
2258 ExecutionDomainHandler handler = analysisServer.handlers.firstWhere(
2259 (RequestHandler handler) => handler is ExecutionDomainHandler,
2260 orElse: () => null);
2261 Set<ExecutionService> services = new Set<ExecutionService>();
2262 if (handler.onFileAnalyzed != null) {
2263 services.add(ExecutionService.LAUNCH_DATA);
2264 }
2265
2266 if (handler != null) {
2267 buffer.write('<h3>Execution Domain</h3>');
2268 _writeTwoColumns(buffer, (StringBuffer buffer) {
2269 _writeSubscriptionList(buffer, ExecutionService.VALUES, services);
2270 }, (StringBuffer buffer) {});
2271 }
2272 }
2273
2274 void _writeListItem(StringBuffer buffer, writer()) {
2275 buffer.write('<li>');
2276 writer();
2277 buffer.write('</li>');
2278 }
2279
2280 void _writeListOfStrings(
2281 StringBuffer buffer, String listName, Iterable<String> items) {
2282 List<String> itemList = items.toList();
2283 itemList.sort((String a, String b) {
2284 a = a.toLowerCase();
2285 b = b.toLowerCase();
2286 return a.compareTo(b);
2287 });
2288 buffer.write('List "$listName" containing ${itemList.length} entries:');
2289 buffer.write('<ul>');
2290 for (String member in itemList) {
2291 _writeListItem(buffer, () {
2292 buffer.write(member);
2293 });
2294 }
2295 buffer.write('</ul>');
2296 }
2297
2298 /**
2299 * Write to the given [buffer] a representation of the given [map] of strings
2300 * to strings.
2301 */
2302 void _writeMapOfStringToString(StringBuffer buffer, Map<String, String> map) {
2303 List<String> keys = map.keys.toList();
2304 keys.sort();
2305 int length = keys.length;
2306 buffer.write('{');
2307 for (int i = 0; i < length; i++) {
2308 buffer.write('<br>');
2309 String key = keys[i];
2310 if (i > 0) {
2311 buffer.write(', ');
2312 }
2313 buffer.write(key);
2314 buffer.write(' = ');
2315 buffer.write(map[key]);
2316 }
2317 buffer.write('<br>}');
2318 }
2319
2320 /**
2321 * Write a representation of an analysis option with the given [name] and
2322 * [value] to the given [buffer]. The option should be separated from other
2323 * options unless the [last] flag is true, indicating that this is the last
2324 * option in the list of options.
2325 */
2326 void _writeOption(StringBuffer buffer, String name, Object value,
2327 {bool last: false}) {
2328 buffer.write(name);
2329 buffer.write(' = ');
2330 buffer.write(value.toString());
2331 if (!last) {
2332 buffer.write('<br>');
2333 }
2334 }
2335
2336 /**
2337 * Write a standard HTML page to the given [buffer]. The page will have the
2338 * given [title] and a body that is generated by the given [body] generator.
2339 */
2340 void _writePage(StringBuffer buffer, String title, List<String> subtitles,
2341 HtmlGenerator body) {
2342 DateTime now = new DateTime.now();
2343 String date = "${now.month}/${now.day}/${now.year}";
2344 String time =
2345 "${now.hour}:${_twoDigit(now.minute)}:${_twoDigit(now.second)}.${now.mil lisecond}";
2346
2347 buffer.write('<!DOCTYPE html>');
2348 buffer.write('<html>');
2349 buffer.write('<head>');
2350 buffer.write('<meta charset="utf-8">');
2351 buffer.write(
2352 '<meta name="viewport" content="width=device-width, initial-scale=1.0">' );
2353 buffer.write('<title>$title</title>');
2354 buffer.write('<style>');
2355 buffer.write('a {color: #0000DD; text-decoration: none;}');
2356 buffer.write('a:link.error {background-color: #FFEEEE;}');
2357 buffer.write('a:visited.error {background-color: #FFEEEE;}');
2358 buffer.write('a:hover.error {background-color: #FFEEEE;}');
2359 buffer.write('a:active.error {background-color: #FFEEEE;}');
2360 buffer.write(
2361 'h3 {background-color: #DDDDDD; margin-top: 0em; margin-bottom: 0em;}');
2362 buffer.write('p {margin-top: 0.5em; margin-bottom: 0.5em;}');
2363 buffer.write(
2364 'p.commentary {margin-top: 1em; margin-bottom: 1em; margin-left: 2em; fo nt-style: italic;}');
2365 // response.write('span.error {text-decoration-line: underline; text-decorati on-color: red; text-decoration-style: wavy;}');
2366 buffer.write(
2367 'table.column {border: 0px solid black; width: 100%; table-layout: fixed ;}');
2368 buffer.write('td.column {vertical-align: top; width: 50%;}');
2369 buffer.write('td.right {text-align: right;}');
2370 buffer.write('th {text-align: left; vertical-align:top;}');
2371 buffer.write('tr {vertical-align:top;}');
2372 buffer.write('</style>');
2373 buffer.write('</head>');
2374
2375 buffer.write('<body>');
2376 buffer.write(
2377 '<h2>$title <small><small>(as of $time on $date)</small></small></h2>');
2378 if (subtitles != null && subtitles.isNotEmpty) {
2379 buffer.write('<blockquote>');
2380 bool first = true;
2381 for (String subtitle in subtitles) {
2382 if (first) {
2383 first = false;
2384 } else {
2385 buffer.write('<br>');
2386 }
2387 buffer.write('<b>');
2388 buffer.write(subtitle);
2389 buffer.write('</b>');
2390 }
2391 buffer.write('</blockquote>');
2392 }
2393 try {
2394 body(buffer);
2395 } catch (exception, stackTrace) {
2396 buffer.write('<h3>Exception while creating page</h3>');
2397 _writeException(buffer, new CaughtException(exception, stackTrace));
2398 }
2399 buffer.write('</body>');
2400 buffer.write('</html>');
2401 }
2402
2403 /**
2404 * Write the recent output section (on the main status page) to the given
2405 * [buffer] object.
2406 */
2407 void _writePluginStatus(StringBuffer buffer) {
2408 void writePlugin(Plugin plugin) {
2409 buffer.write(plugin.uniqueIdentifier);
2410 buffer.write(' (');
2411 buffer.write(plugin.runtimeType);
2412 buffer.write(')<br>');
2413 }
2414
2415 buffer.write('<h3>Plugin Status</h3><p>');
2416 writePlugin(AnalysisEngine.instance.enginePlugin);
2417 writePlugin(_server.serverPlugin);
2418 for (Plugin plugin in _server.analysisServer.userDefinedPlugins) {
2419 writePlugin(plugin);
2420 }
2421 buffer.write('<p>');
2422 }
2423
2424 /**
2425 * Write the recent output section (on the main status page) to the given
2426 * [buffer] object.
2427 */
2428 void _writeRecentOutput(StringBuffer buffer) {
2429 buffer.write('<h3>Recent Output</h3>');
2430 String output = HTML_ESCAPE.convert(_printBuffer.join('\n'));
2431 if (output.isEmpty) {
2432 buffer.write('<i>none</i>');
2433 } else {
2434 buffer.write('<pre>');
2435 buffer.write(output);
2436 buffer.write('</pre>');
2437 }
2438 }
2439
2440 void _writeResponse(HttpRequest request, HtmlGenerator writePage) {
2441 HttpResponse response = request.response;
2442 response.statusCode = HttpStatus.OK;
2443 response.headers.contentType = _htmlContent;
2444 try {
2445 StringBuffer buffer = new StringBuffer();
2446 try {
2447 writePage(buffer);
2448 } catch (exception, stackTrace) {
2449 buffer.clear();
2450 _writePage(buffer, 'Internal Exception', [], (StringBuffer buffer) {
2451 _writeException(buffer, new CaughtException(exception, stackTrace));
2452 });
2453 }
2454 response.write(buffer.toString());
2455 } finally {
2456 response.close();
2457 }
2458 }
2459
2460 /**
2461 * Write a single row within a table to the given [buffer]. The row will have
2462 * one cell for each of the [columns], and will be a header row if [header] is
2463 * `true`.
2464 */
2465 void _writeRow(StringBuffer buffer, List<Object> columns,
2466 {bool header: false, List<String> classes}) {
2467 buffer.write('<tr>');
2468 int count = columns.length;
2469 int maxClassIndex = classes == null ? 0 : classes.length - 1;
2470 for (int i = 0; i < count; i++) {
2471 String classAttribute = '';
2472 if (classes != null) {
2473 String className = classes[min(i, maxClassIndex)];
2474 if (className != null) {
2475 classAttribute = ' class="$className"';
2476 }
2477 }
2478 if (header) {
2479 buffer.write('<th$classAttribute>');
2480 } else {
2481 buffer.write('<td$classAttribute>');
2482 }
2483 buffer.write(columns[i]);
2484 if (header) {
2485 buffer.write('</th>');
2486 } else {
2487 buffer.write('</td>');
2488 }
2489 }
2490 buffer.write('</tr>');
2491 }
2492
2493 /**
2494 * Write the status of the service domain (on the main status page) to the
2495 * given [response] object.
2496 */
2497 bool _writeServerStatus(StringBuffer buffer) {
2498 AnalysisServer analysisServer = _server.analysisServer;
2499 Set<ServerService> services = analysisServer.serverServices;
2500
2501 buffer.write('<h3>Server Domain</h3>');
2502 _writeTwoColumns(buffer, (StringBuffer buffer) {
2503 if (analysisServer == null) {
2504 buffer.write('Status: <span style="color:red">Not running</span>');
2505 return;
2506 }
2507 buffer.write('<p>');
2508 buffer.write('Status: Running<br>');
2509 buffer.write('New analysis driver: ');
2510 buffer.write(analysisServer.options.enableNewAnalysisDriver);
2511 buffer.write('<br>');
2512 buffer.write('Instrumentation: ');
2513 if (AnalysisEngine.instance.instrumentationService.isActive) {
2514 buffer.write('<span style="color:red">Active</span>');
2515 } else {
2516 buffer.write('Inactive');
2517 }
2518 buffer.write('<br>');
2519 buffer.write('Server version: ');
2520 buffer.write(AnalysisServer.VERSION);
2521 buffer.write('<br>');
2522 buffer.write('SDK: ');
2523 buffer.write(Platform.version);
2524 buffer.write('<br>');
2525 buffer.write('Process ID: ');
2526 buffer.write(pid);
2527 buffer.write('</p>');
2528
2529 buffer.write('<p><b>Performance Data</b></p>');
2530 buffer.write('<p>');
2531 buffer.write(makeLink(
2532 COMMUNICATION_PERFORMANCE_PATH, {}, 'Communication performance'));
2533 buffer.write('</p>');
2534 buffer.write('<p>');
2535 buffer.write(makeLink(DIAGNOSTIC_PATH, {}, 'General diagnostics'));
2536 buffer.write('</p>');
2537 buffer.write('<p>');
2538 buffer.write(makeLink(MEMORY_USE_PATH, {}, 'Memory usage'));
2539 buffer.write(' <small>(long running)</small></p>');
2540 }, (StringBuffer buffer) {
2541 _writeSubscriptionList(buffer, ServerService.VALUES, services);
2542 });
2543 return analysisServer != null;
2544 }
2545
2546 /**
2547 * Write a representation of the given [stackTrace] to the given [buffer].
2548 */
2549 void _writeStackTrace(StringBuffer buffer, StackTrace stackTrace) {
2550 if (stackTrace != null) {
2551 String trace = stackTrace.toString().replaceAll('#', '<br>#');
2552 if (trace.startsWith('<br>#')) {
2553 trace = trace.substring(4);
2554 }
2555 buffer.write('<p>');
2556 buffer.write(trace);
2557 buffer.write('</p>');
2558 }
2559 }
2560
2561 /**
2562 * Given a [service] that could be subscribed to and a set of the services
2563 * that are actually subscribed to ([subscribedServices]), write a
2564 * representation of the service to the given [buffer].
2565 */
2566 void _writeSubscriptionInList(
2567 StringBuffer buffer, Enum service, Set<Enum> subscribedServices) {
2568 if (subscribedServices.contains(service)) {
2569 buffer.write('<code>+ </code>');
2570 } else {
2571 buffer.write('<code>- </code>');
2572 }
2573 buffer.write(service.name);
2574 buffer.write('<br>');
2575 }
2576
2577 /**
2578 * Given a [service] that could be subscribed to and a set of paths that are
2579 * subscribed to the services ([subscribedPaths]), write a representation of
2580 * the service to the given [buffer].
2581 */
2582 void _writeSubscriptionInMap(
2583 StringBuffer buffer, Enum service, Set<String> subscribedPaths) {
2584 buffer.write('<p>');
2585 buffer.write(service.name);
2586 buffer.write('</p>');
2587 if (subscribedPaths == null || subscribedPaths.isEmpty) {
2588 buffer.write('none');
2589 } else {
2590 List<String> paths = subscribedPaths.toList();
2591 paths.sort();
2592 for (String path in paths) {
2593 buffer.write('<p>');
2594 buffer.write(path);
2595 buffer.write('</p>');
2596 }
2597 }
2598 }
2599
2600 /**
2601 * Given a list containing all of the services that can be subscribed to in a
2602 * single domain ([allServices]) and a set of the services that are actually
2603 * subscribed to ([subscribedServices]), write a representation of the
2604 * subscriptions to the given [buffer].
2605 */
2606 void _writeSubscriptionList(StringBuffer buffer, List<Enum> allServices,
2607 Set<Enum> subscribedServices) {
2608 buffer.write('<p><b>Subscriptions</b></p>');
2609 buffer.write('<p>');
2610 for (Enum service in allServices) {
2611 _writeSubscriptionInList(buffer, service, subscribedServices);
2612 }
2613 buffer.write('</p>');
2614 }
2615
2616 /**
2617 * Given a list containing all of the services that can be subscribed to in a
2618 * single domain ([allServices]) and a set of the services that are actually
2619 * subscribed to ([subscribedServices]), write a representation of the
2620 * subscriptions to the given [buffer].
2621 */
2622 void _writeSubscriptionMap(StringBuffer buffer, List<Enum> allServices,
2623 Map<Enum, Set<String>> subscribedServices) {
2624 buffer.write('<p><b>Subscriptions</b></p>');
2625 for (Enum service in allServices) {
2626 _writeSubscriptionInMap(buffer, service, subscribedServices[service]);
2627 }
2628 }
2629
2630 /**
2631 * Write the targeted results returned by iterating over the [results] to the
2632 * given [buffer]. The list will have the given [title] written before it.
2633 */
2634 void _writeTargetedResults(
2635 StringBuffer buffer, String title, Iterable<TargetedResult> results) {
2636 List<TargetedResult> sortedResults = results.toList();
2637 sortedResults.sort((TargetedResult first, TargetedResult second) {
2638 int nameOrder =
2639 first.result.toString().compareTo(second.result.toString());
2640 if (nameOrder != 0) {
2641 return nameOrder;
2642 }
2643 return first.target.toString().compareTo(second.target.toString());
2644 });
2645
2646 buffer.write('<p>');
2647 buffer.write(title);
2648 buffer.write('</p><blockquote>');
2649 if (results.isEmpty) {
2650 buffer.write('nothing');
2651 } else {
2652 for (TargetedResult result in sortedResults) {
2653 buffer.write('<p>');
2654 buffer.write(result.result.toString());
2655 buffer.write(' of ');
2656 buffer.write(result.target.toString());
2657 buffer.write('<p>');
2658 }
2659 }
2660 buffer.write('</blockquote>');
2661 }
2662
2663 /**
2664 * Write two columns of information to the given [buffer], where the
2665 * [leftColumn] and [rightColumn] functions are used to generate the content
2666 * of those columns.
2667 */
2668 void _writeTwoColumns(StringBuffer buffer, HtmlGenerator leftColumn,
2669 HtmlGenerator rightColumn) {
2670 buffer
2671 .write('<table class="column"><tr class="column"><td class="column">');
2672 leftColumn(buffer);
2673 buffer.write('</td><td class="column">');
2674 rightColumn(buffer);
2675 buffer.write('</td></tr></table>');
2676 }
2677
2678 /**
2679 * Render the given [value] as HTML and append it to the given [buffer]. The
2680 * [linkParameters] will be used if the value is too large to be displayed on
2681 * the current page and needs to be linked to a separate page.
2682 */
2683 void _writeValueAsHtml(
2684 StringBuffer buffer, Object value, Map<String, String> linkParameters) {
2685 if (value == null) {
2686 buffer.write('<i>null</i>');
2687 } else if (value is String) {
2688 buffer.write('<pre>${HTML_ESCAPE.convert(value)}</pre>');
2689 } else if (value is List) {
2690 buffer.write('List containing ${value.length} entries');
2691 buffer.write('<ul>');
2692 for (var entry in value) {
2693 _writeListItem(buffer, () {
2694 _writeValueAsHtml(buffer, entry, linkParameters);
2695 });
2696 }
2697 buffer.write('</ul>');
2698 } else if (value is AstNode) {
2699 String link =
2700 makeLink(AST_PATH, linkParameters, value.runtimeType.toString());
2701 buffer.write('<i>$link</i>');
2702 } else if (value is Element) {
2703 String link =
2704 makeLink(ELEMENT_PATH, linkParameters, value.runtimeType.toString());
2705 buffer.write('<i>$link</i>');
2706 } else if (value is UsedLocalElements) {
2707 buffer.write('<ul>');
2708 _writeListItem(buffer, () {
2709 HashSet<Element> elements = value.elements;
2710 buffer.write('List "elements" containing ${elements.length} entries');
2711 buffer.write('<ul>');
2712 for (Element element in elements) {
2713 _writeListItem(buffer, () {
2714 String elementStr = HTML_ESCAPE.convert(element.toString());
2715 buffer.write('<i>${element.runtimeType}</i> $elementStr');
2716 });
2717 }
2718 buffer.write('</ul>');
2719 });
2720 _writeListItem(buffer, () {
2721 _writeListOfStrings(buffer, 'members', value.members);
2722 });
2723 _writeListItem(buffer, () {
2724 _writeListOfStrings(buffer, 'readMembers', value.readMembers);
2725 });
2726 buffer.write('</ul>');
2727 } else {
2728 buffer.write(HTML_ESCAPE.convert(value.toString()));
2729 buffer.write(' <i>(${value.runtimeType.toString()})</i>');
2730 }
2731 }
2732
2733 /**
2734 * Create a link to [path] with query parameters [params], with inner HTML
2735 * [innerHtml]. If [hasError] is `true`, then the link will have the class
2736 * 'error'.
2737 */
2738 static String makeLink(
2739 String path, Map<String, String> params, String innerHtml,
2740 [bool hasError = false]) {
2741 Uri uri = new Uri(path: path, queryParameters: params);
2742 String href = HTML_ESCAPE.convert(uri.toString());
2743 String classAttribute = hasError ? ' class="error"' : '';
2744 return '<a href="$href"$classAttribute>$innerHtml</a>';
2745 }
2746 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/lib/src/server/http_server.dart ('k') | pkg/analysis_server/lib/src/status/get_handler2.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698