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

Unified Diff: pkg/analysis_server/lib/src/status/diagnostics.dart

Issue 2933753002: Run the sorter to reduce code churn (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analysis_server/lib/src/constants.dart ('k') | pkg/analysis_server/lib/src/status/pages.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analysis_server/lib/src/status/diagnostics.dart
diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart
index 5455a10fc88fe32b0d54a900c8ecbfb5f4d6d416..ef961c033841bd025ec52a9728d96123d448aa54 100644
--- a/pkg/analysis_server/lib/src/status/diagnostics.dart
+++ b/pkg/analysis_server/lib/src/status/diagnostics.dart
@@ -131,6 +131,18 @@ td.pre {
}
''';
+String get _sdkVersion {
+ String version = Platform.version;
+ if (version.contains(' ')) {
+ version = version.substring(0, version.indexOf(' '));
+ }
+ return version;
+}
+
+String writeOption(String name, dynamic value) {
+ return '$name: <code>$value</code><br> ';
+}
+
class AstPage extends DiagnosticPageWithNav {
String _description;
@@ -141,17 +153,6 @@ class AstPage extends DiagnosticPageWithNav {
String get description => _description ?? super.description;
@override
- Future<Null> generatePage(Map<String, String> params) async {
- try {
- String path = params['file'];
- _description = path == null ? null : 'The AST for $path.';
- await super.generatePage(params);
- } finally {
- _description = null;
- }
- }
-
- @override
Future<Null> generateContent(Map<String, String> params) async {
String path = params['file'];
if (path == null) {
@@ -174,421 +175,594 @@ class AstPage extends DiagnosticPageWithNav {
AstWriter writer = new AstWriter(buf);
result.unit.accept(writer);
}
-}
-class DiagnosticsSite extends Site implements AbstractGetHandler {
- /// An object that can handle either a WebSocket connection or a connection
- /// to the client over stdio.
- SocketServer socketServer;
-
- /// The last few lines printed.
- List<String> lastPrintedLines = <String>[];
+ @override
+ Future<Null> generatePage(Map<String, String> params) async {
+ try {
+ String path = params['file'];
+ _description = path == null ? null : 'The AST for $path.';
+ await super.generatePage(params);
+ } finally {
+ _description = null;
+ }
+ }
+}
- DiagnosticsSite(this.socketServer, this.lastPrintedLines)
- : super('Analysis Server') {
- pages.add(new CompletionPage(this));
- pages.add(new CommunicationsPage(this));
- pages.add(new ContextsPage(this));
- pages.add(new ExceptionsPage(this));
- pages.add(new InstrumentationPage(this));
- pages.add(new OverlaysPage(this));
- pages.add(new PluginsPage(this));
- pages.add(new ProfilePage(this));
- pages.add(new SubscriptionsPage(this));
+class CommunicationsPage extends DiagnosticPageWithNav {
+ CommunicationsPage(DiagnosticsSite site)
+ : super(site, 'communications', 'Communications',
+ description:
+ 'Latency statistics for analysis server communications.');
- ProcessProfiler profiler = ProcessProfiler.getProfilerForPlatform();
- if (profiler != null) {
- pages.add(new MemoryAndCpuPage(this, profiler));
+ @override
+ void generateContent(Map<String, String> params) {
+ void writeRow(List<String> data, {List<String> classes}) {
+ buf.write("<tr>");
+ for (int i = 0; i < data.length; i++) {
+ String c = classes == null ? null : classes[i];
+ if (c != null) {
+ buf.write('<td class="$c">${escape(data[i])}</td>');
+ } else {
+ buf.write('<td>${escape(data[i])}</td>');
+ }
+ }
+ buf.writeln("</tr>");
}
- pages.sort(((Page a, Page b) =>
- a.title.toLowerCase().compareTo(b.title.toLowerCase())));
-
- // Add the status page at the beginning.
- pages.insert(0, new StatusPage(this));
+ buf.writeln('<div class="columns">');
- // Add non-nav pages.
- pages.add(new FeedbackPage(this));
+ ServerPerformance perf = server.performanceAfterStartup;
+ if (perf != null) {
+ buf.writeln('<div class="column one-half">');
+ h3('Current');
- secondaryPages.add(new AstPage(this));
- secondaryPages.add(new ElementModelPage(this));
- }
+ int requestCount = perf.requestCount;
+ int averageLatency =
+ requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
+ int maximumLatency = perf.maxLatency;
+ double slowRequestPercent =
+ requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
- String get customCss => kCustomCss;
+ buf.write('<table>');
+ writeRow([printInteger(requestCount), 'requests'],
+ classes: ["right", null]);
+ writeRow([printMilliseconds(averageLatency), 'average latency'],
+ classes: ["right", null]);
+ writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
+ classes: ["right", null]);
+ writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
+ classes: ["right", null]);
+ buf.write('</table>');
- Page createUnknownPage(String unknownPath) =>
- new NotFoundPage(this, unknownPath);
+ String time = server.uptime.toString();
+ if (time.contains('.')) {
+ time = time.substring(0, time.indexOf('.'));
+ }
+ buf.writeln(writeOption('Uptime', time));
- Page createExceptionPage(String message, StackTrace trace) =>
- new ExceptionPage(this, message, trace);
-}
+ buf.write('</div>');
+ }
-/// A page with a proscriptive notion of layout.
-abstract class DiagnosticPage extends Page {
- final Site site;
+ buf.writeln('<div class="column one-half">');
+ h3('Startup');
+ perf = server.performanceDuringStartup;
- DiagnosticPage(this.site, String id, String title, {String description})
- : super(id, title, description: description);
+ int requestCount = perf.requestCount;
+ int averageLatency =
+ requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
+ int maximumLatency = perf.maxLatency;
+ double slowRequestPercent =
+ requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
- AnalysisServer get server =>
- (site as DiagnosticsSite).socketServer.analysisServer;
+ buf.write('<table>');
+ writeRow([printInteger(requestCount), 'requests'],
+ classes: ["right", null]);
+ writeRow([printMilliseconds(averageLatency), 'average latency'],
+ classes: ["right", null]);
+ writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
+ classes: ["right", null]);
+ writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
+ classes: ["right", null]);
+ buf.write('</table>');
- Future<Null> generatePage(Map<String, String> params) async {
- buf.writeln('<!DOCTYPE html><html lang="en">');
- buf.write('<head>');
- buf.write('<meta charset="utf-8">');
- buf.write('<meta name="viewport" content="width=device-width, '
- 'initial-scale=1.0">');
- buf.writeln('<title>${site.title}</title>');
- buf.writeln('<link rel="stylesheet" '
- 'href="https://cdnjs.cloudflare.com/ajax/libs/Primer/6.0.0/build.css">');
- buf.writeln('<link rel="stylesheet" '
- 'href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octicons.css">');
- buf.writeln('<script type="text/javascript" '
- 'src="https://www.gstatic.com/charts/loader.js"></script>');
- buf.writeln('<style>${site.customCss}</style>');
- buf.writeln('</head>');
+ if (server.performanceAfterStartup != null) {
+ int startupTime =
+ server.performanceAfterStartup.startTime - perf.startTime;
+ buf.writeln(
+ writeOption('Initial analysis time', printMilliseconds(startupTime)));
+ }
+ buf.write('</div>');
- buf.writeln('<body>');
- generateHeader();
- buf.writeln('<div class="container">');
- await generateContainer(params);
- generateFooter();
- buf.writeln('</div>'); // div.container
- buf.writeln('</body>');
- buf.writeln('</html>');
+ buf.write('</div>');
}
+}
- void generateHeader() {
- buf.writeln('''
- <header class="masthead">
- <div class="container">
- <span class="masthead-logo">
- <span class="mega-octicon octicon-database"></span>
- ${site.title} Diagnostics
- </span>
+class CompletionPage extends DiagnosticPageWithNav {
+ CompletionPage(DiagnosticsSite site)
+ : super(site, 'completion', 'Code Completion',
+ description: 'Latency statistics for code completion.');
- <nav class="masthead-nav">
- <a href="/status" ${isNavPage ? ' class="active"' : ''}>Diagnostics</a>
- <a href="/feedback" ${isCurrentPage('/feedback') ? ' class="active"' : ''}>Feedback</a>
- <a href="https://www.dartlang.org/tools/analyzer" target="_blank">Docs</a>
- <a href="https://htmlpreview.github.io/?https://github.com/dart-lang/sdk/blob/master/pkg/analysis_server/doc/api.html" target="_blank">Spec</a>
- </nav>
- </div>
- </header>
-''');
- }
+ @override
+ void generateContent(Map<String, String> params) {
+ CompletionDomainHandler completionDomain = server.handlers
+ .firstWhere((handler) => handler is CompletionDomainHandler);
- Future<Null> generateContainer(Map<String, String> params) async {
- buf.writeln('<div class="columns docs-layout">');
- buf.writeln('<div class="three-fourths column markdown-body">');
- h1(title, classes: 'page-title');
- await asyncDiv(() async {
- p(description);
- await generateContent(params);
- }, classes: 'markdown-body');
- buf.writeln('</div>');
- buf.writeln('</div>');
- }
+ List<CompletionPerformance> completions =
+ completionDomain.performanceList.items.toList();
- void generateContent(Map<String, String> params);
+ if (completions.isEmpty) {
+ blankslate('No completions recorded.');
+ return;
+ }
- void generateFooter() {
+ int fastCount =
+ completions.where((c) => c.elapsedInMilliseconds <= 100).length;
+ p('${completions.length} results; ${printPercentage(fastCount / completions.length)} within 100ms.');
+
+ // draw a chart
+ buf.writeln(
+ '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
+ StringBuffer rowData = new StringBuffer();
+ for (int i = completions.length - 1; i >= 0; i--) {
+ // [' ', 101.5]
+ if (rowData.isNotEmpty) {
+ rowData.write(',');
+ }
+ rowData.write("[' ', ${completions[i].elapsedInMilliseconds}]");
+ }
buf.writeln('''
- <footer class="footer">
- Dart ${site.title} <span style="float:right">SDK ${_sdkVersion}</span>
- </footer>
+ <script type="text/javascript">
+ google.charts.load('current', {'packages':['bar']});
+ google.charts.setOnLoadCallback(drawChart);
+ function drawChart() {
+ var data = google.visualization.arrayToDataTable([
+ ['Completions', 'Time'],
+ $rowData
+ ]);
+ var options = { bars: 'vertical', vAxis: {format: 'decimal'}, height: 300 };
+ var chart = new google.charts.Bar(document.getElementById('chart-div'));
+ chart.draw(data, google.charts.Bar.convertOptions(options));
+ }
+ </script>
''');
- }
-
- bool get isNavPage => false;
-}
-abstract class DiagnosticPageWithNav extends DiagnosticPage {
- DiagnosticPageWithNav(Site site, String id, String title,
- {String description})
- : super(site, id, title, description: description);
+ // emit the data as a table
+ buf.writeln('<table>');
+ buf.writeln(
+ '<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>');
+ for (CompletionPerformance completion in completions) {
+ buf.writeln('<tr>'
+ '<td class="pre right">${printMilliseconds(completion.elapsedInMilliseconds)}</td>'
+ '<td class="right">${completion.suggestionCount}</td>'
+ '<td>${escape(completion.source.shortName)}</td>'
+ '<td><code>${escape(completion.snippet)}</code></td>'
+ '</tr>');
+ }
+ buf.writeln('</table>');
+ }
+}
- Future<Null> generateContainer(Map<String, String> params) async {
- buf.writeln('<div class="columns docs-layout">');
+class ContextsPage extends DiagnosticPageWithNav {
+ ContextsPage(DiagnosticsSite site)
+ : super(site, 'contexts', 'Contexts',
+ description:
+ 'An analysis context defines the options and the set of sources being analyzed.');
- buf.writeln('<div class="one-fifth column">');
- buf.writeln('<nav class="menu docs-menu">');
- for (Page page in site.pages.where((p) => p is DiagnosticPageWithNav)) {
- buf.write('<a class="menu-item ${page == this ? ' selected' : ''}" '
- 'href="${page.path}">${escape(page.title)}');
- String detail = (page as DiagnosticPageWithNav).navDetail;
- if (detail != null) {
- buf.write('<span class="counter">$detail</span>');
+ String get navDetail => printInteger(server.driverMap.length);
+
+ String describe(AnalysisOptionsImpl options) {
+ StringBuffer b = new StringBuffer();
+
+ b.write(
+ writeOption('Analyze function bodies', options.analyzeFunctionBodies));
+ b.write(writeOption('Enable asserts in initializer lists',
+ options.enableAssertInitializer));
+ b.write(writeOption(
+ 'Enable strict call checks', options.enableStrictCallChecks));
+ b.write(writeOption('Enable super mixins', options.enableSuperMixins));
+ b.write(writeOption('Generate dart2js hints', options.dart2jsHint));
+ b.write(writeOption(
+ 'Generate errors in implicit files', options.generateImplicitErrors));
+ b.write(
+ writeOption('Generate errors in SDK files', options.generateSdkErrors));
+ b.write(writeOption('Generate hints', options.hint));
+ b.write(writeOption('Incremental resolution', options.incremental));
+ b.write(writeOption(
+ 'Incremental resolution with API changes', options.incrementalApi));
+ b.write(writeOption('Preserve comments', options.preserveComments));
+ b.write(writeOption('Strong mode', options.strongMode));
+ b.write(writeOption('Strong mode hints', options.strongModeHints));
+
+ return b.toString();
+ }
+
+ @override
+ void generateContent(Map<String, String> params) {
+ Map<Folder, AnalysisDriver> driverMap = server.driverMap;
+ if (driverMap.isEmpty) {
+ blankslate('No contexts.');
+ return;
+ }
+
+ String contextPath = params['context'];
+ List<Folder> folders = driverMap.keys.toList();
+ folders
+ .sort((first, second) => first.shortName.compareTo(second.shortName));
+ Folder folder =
+ folders.firstWhere((f) => f.path == contextPath, orElse: () => null);
+
+ if (folder == null) {
+ folder = folders.first;
+ contextPath = folder.path;
+ }
+
+ AnalysisDriver driver = driverMap[folder];
+
+ buf.writeln('<div class="tabnav">');
+ buf.writeln('<nav class="tabnav-tabs">');
+ for (Folder f in folders) {
+ if (f == folder) {
+ buf.writeln(
+ '<a class="tabnav-tab selected">${escape(f.shortName)}</a>');
+ } else {
+ String p = '$path?context=${Uri.encodeQueryComponent(f.path)}';
+ buf.writeln(
+ '<a href="$p" class="tabnav-tab">${escape(f.shortName)}</a>');
}
- buf.writeln('</a>');
}
buf.writeln('</nav>');
buf.writeln('</div>');
- buf.writeln('<div class="four-fifths column markdown-body">');
- h1(title, classes: 'page-title');
- await asyncDiv(() async {
- p(description);
- await generateContent(params);
- }, classes: 'markdown-body');
- buf.writeln('</div>');
+ buf.writeln(writeOption('Context location', escape(contextPath)));
+ buf.writeln(writeOption('Analysis options path',
+ escape(driver.contextRoot.optionsFilePath ?? 'none')));
+
+ buf.writeln('<div class="columns">');
+ buf.writeln('<div class="column one-half">');
+ h3('Analysis options');
+ p(describe(driver.analysisOptions), raw: true);
+ buf.writeln(
+ writeOption('Has .packages file', folder.getChild('.packages').exists));
+ buf.writeln(writeOption(
+ 'Has pubspec.yaml file', folder.getChild('pubspec.yaml').exists));
buf.writeln('</div>');
- }
- String get navDetail => null;
+ buf.writeln('<div class="column one-half">');
+ DartSdk sdk = driver?.sourceFactory?.dartSdk;
+ AnalysisOptionsImpl sdkOptions = sdk?.context?.analysisOptions;
+ if (sdkOptions != null) {
+ h3('SDK analysis options');
+ p(describe(sdkOptions), raw: true);
- bool get isNavPage => true;
-}
+ if (sdk is FolderBasedDartSdk) {
+ p(writeOption('Use summaries', sdk.useSummary), raw: true);
+ }
+ }
+ buf.writeln('</div>');
-class ElementModelPage extends DiagnosticPageWithNav {
- String _description;
+ buf.writeln('</div>');
- ElementModelPage(DiagnosticsSite site)
- : super(site, 'element', 'Element model',
- description: 'The element model for a file.');
+ h3('Lints');
+ p(driver.analysisOptions.lintRules.map((l) => l.name).join(', '));
- @override
- String get description => _description ?? super.description;
+ h3('Error processors');
+ p(driver.analysisOptions.errorProcessors
+ .map((e) => e.description)
+ .join(', '));
- @override
- Future<Null> generatePage(Map<String, String> params) async {
- try {
- String path = params['file'];
- _description = path == null ? null : 'The element model for $path.';
- await super.generatePage(params);
- } finally {
- _description = null;
- }
- }
+ List<String> priorityFiles = driver.priorityFiles;
+ List<String> addedFiles = driver.addedFiles.toList();
+ List<String> implicitFiles =
+ driver.knownFiles.difference(driver.addedFiles).toList();
+ addedFiles.sort();
+ implicitFiles.sort();
- @override
- Future<Null> generateContent(Map<String, String> params) async {
- String path = params['file'];
- if (path == null) {
- p('No file path provided.');
- return;
+ String lenCounter(List list) {
+ return '<span class="counter" style="float: right;">${list.length}</span>';
}
- AnalysisDriver driver = server.getAnalysisDriver(path);
- if (driver == null) {
- p('The file <code>${escape(path)}</code> is not being analyzed.',
- raw: true);
- return;
+
+ h3('Context files');
+
+ void writeFile(String file) {
+ String astPath = '/ast?file=${Uri.encodeQueryComponent(file)}';
+ String elementPath = '/element?file=${Uri.encodeQueryComponent(file)}';
+
+ buf.write(file);
+ buf.write(' (');
+ buf.writeln('<a href="$astPath">ast</a>');
+ buf.write(' ');
+ buf.writeln('<a href="$elementPath">element</a>');
+ buf.write(')');
}
- AnalysisResult result = await driver.getResult(path);
- if (result == null) {
- p('An element model could not be produced for the file <code>${escape(path)}</code>.',
- raw: true);
- return;
+
+ h4('Priority files ${lenCounter(priorityFiles)}', raw: true);
+ ul(priorityFiles, writeFile, classes: 'scroll-table');
+
+ h4('Added files ${lenCounter(addedFiles)}', raw: true);
+ ul(addedFiles, writeFile, classes: 'scroll-table');
+
+ h4('ImplicitFiles files ${lenCounter(implicitFiles)}', raw: true);
+ ul(implicitFiles, writeFile, classes: 'scroll-table');
+
+ SourceFactory sourceFactory = driver.sourceFactory;
+ if (sourceFactory is SourceFactoryImpl) {
+ h3('Resolvers');
+ for (UriResolver resolver in sourceFactory.resolvers) {
+ h4(resolver.runtimeType.toString());
+ buf.write('<p class="scroll-table">');
+ if (resolver is DartUriResolver) {
+ DartSdk sdk = resolver.dartSdk;
+ buf.write(' (sdk = ');
+ buf.write(sdk.runtimeType);
+ if (sdk is FolderBasedDartSdk) {
+ buf.write(' (path = ');
+ buf.write(sdk.directory.path);
+ buf.write(')');
+ } else if (sdk is EmbedderSdk) {
+ buf.write(' (map = ');
+ writeMap(sdk.urlMappings);
+ buf.write(')');
+ }
+ buf.write(')');
+ } else if (resolver is SdkExtUriResolver) {
+ buf.write(' (map = ');
+ writeMap(resolver.urlMappings);
+ buf.write(')');
+ } else if (resolver is PackageMapUriResolver) {
+ writeMap(resolver.packageMap);
+ }
+ buf.write('</p>');
+ }
}
+ }
- ElementWriter writer = new ElementWriter(buf);
- result.unit.element.accept(writer);
+ void writeList<E>(List<E> list) {
+ buf.writeln('[${list.join(', ')}]');
}
-}
-class NotFoundPage extends DiagnosticPage {
- final String path;
+ void writeMap<V>(Map<String, V> map) {
+ List<String> keys = map.keys.toList();
+ keys.sort();
+ int length = keys.length;
+ buf.write('{');
+ for (int i = 0; i < length; i++) {
+ buf.write('<br>');
+ String key = keys[i];
+ V value = map[key];
+ buf.write(key);
+ buf.write(' = ');
+ if (value is List) {
+ writeList(value);
+ } else {
+ buf.write(value);
+ }
+ buf.write(',');
+ }
+ buf.write('<br>}');
+ }
+}
- NotFoundPage(Site site, this.path)
- : super(site, '', '404 Not found', description: "'$path' not found.");
+/// A page with a proscriptive notion of layout.
+abstract class DiagnosticPage extends Page {
+ final Site site;
- void generateContent(Map<String, String> params) {}
-}
+ DiagnosticPage(this.site, String id, String title, {String description})
+ : super(id, title, description: description);
-class ExceptionPage extends DiagnosticPage {
- final StackTrace trace;
+ bool get isNavPage => false;
- ExceptionPage(Site site, String message, this.trace)
- : super(site, '', '500 Oops', description: message);
+ AnalysisServer get server =>
+ (site as DiagnosticsSite).socketServer.analysisServer;
- void generateContent(Map<String, String> params) {
- p(trace.toString(), style: 'white-space: pre');
+ Future<Null> generateContainer(Map<String, String> params) async {
+ buf.writeln('<div class="columns docs-layout">');
+ buf.writeln('<div class="three-fourths column markdown-body">');
+ h1(title, classes: 'page-title');
+ await asyncDiv(() async {
+ p(description);
+ await generateContent(params);
+ }, classes: 'markdown-body');
+ buf.writeln('</div>');
+ buf.writeln('</div>');
}
-}
-class FeedbackPage extends DiagnosticPage {
- FeedbackPage(DiagnosticsSite site)
- : super(site, 'feedback', 'Feedback',
- description: 'Providing feedback and filing issues.');
+ void generateContent(Map<String, String> params);
- @override
- void generateContent(Map<String, String> params) {
- final String issuesUrl = 'https://github.com/dart-lang/sdk/issues';
- p(
- 'To file issues or feature requests, see our '
- '<a href="$issuesUrl">bug tracker</a>. When filing an issue, please describe:',
- raw: true,
- );
- ul([
- 'what you were doing',
- 'what occured',
- 'what you think the expected behavior should have been',
- ], (line) => buf.writeln(line));
+ void generateFooter() {
+ buf.writeln('''
+ <footer class="footer">
+ Dart ${site.title} <span style="float:right">SDK ${_sdkVersion}</span>
+ </footer>
+''');
+ }
- List<String> ideInfo = [];
- if (server.options.clientId != null) {
- ideInfo.add(server.options.clientId);
- }
- if (server.options.clientVersion != null) {
- ideInfo.add(server.options.clientVersion);
- }
- String ideText = ideInfo.map((str) => '<code>$str</code>').join(', ');
+ void generateHeader() {
+ buf.writeln('''
+ <header class="masthead">
+ <div class="container">
+ <span class="masthead-logo">
+ <span class="mega-octicon octicon-database"></span>
+ ${site.title} Diagnostics
+ </span>
- p('Other data to include:');
- ul([
- "the IDE you are using and it's version${ideText.isEmpty ? '' : ' ($ideText)'}",
- 'the Dart SDK version (<code>${escape(_sdkVersion)}</code>)',
- 'your operating system (<code>${escape(Platform.operatingSystem)}</code>)',
- ], (line) => buf.writeln(line));
+ <nav class="masthead-nav">
+ <a href="/status" ${isNavPage ? ' class="active"' : ''}>Diagnostics</a>
+ <a href="/feedback" ${isCurrentPage('/feedback') ? ' class="active"' : ''}>Feedback</a>
+ <a href="https://www.dartlang.org/tools/analyzer" target="_blank">Docs</a>
+ <a href="https://htmlpreview.github.io/?https://github.com/dart-lang/sdk/blob/master/pkg/analysis_server/doc/api.html" target="_blank">Spec</a>
+ </nav>
+ </div>
+ </header>
+''');
+ }
- p('Thanks!');
+ Future<Null> generatePage(Map<String, String> params) async {
+ buf.writeln('<!DOCTYPE html><html lang="en">');
+ buf.write('<head>');
+ buf.write('<meta charset="utf-8">');
+ buf.write('<meta name="viewport" content="width=device-width, '
+ 'initial-scale=1.0">');
+ buf.writeln('<title>${site.title}</title>');
+ buf.writeln('<link rel="stylesheet" '
+ 'href="https://cdnjs.cloudflare.com/ajax/libs/Primer/6.0.0/build.css">');
+ buf.writeln('<link rel="stylesheet" '
+ 'href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octicons.css">');
+ buf.writeln('<script type="text/javascript" '
+ 'src="https://www.gstatic.com/charts/loader.js"></script>');
+ buf.writeln('<style>${site.customCss}</style>');
+ buf.writeln('</head>');
+
+ buf.writeln('<body>');
+ generateHeader();
+ buf.writeln('<div class="container">');
+ await generateContainer(params);
+ generateFooter();
+ buf.writeln('</div>'); // div.container
+ buf.writeln('</body>');
+ buf.writeln('</html>');
}
}
-class StatusPage extends DiagnosticPageWithNav {
- StatusPage(DiagnosticsSite site)
- : super(site, 'status', 'Status',
- description:
- 'General status and diagnostics for the analysis server.');
+abstract class DiagnosticPageWithNav extends DiagnosticPage {
+ DiagnosticPageWithNav(Site site, String id, String title,
+ {String description})
+ : super(site, id, title, description: description);
- @override
- void generateContent(Map<String, String> params) {
- buf.writeln('<div class="columns">');
+ bool get isNavPage => true;
- buf.writeln('<div class="column one-half">');
- h3('Status');
- buf.writeln(writeOption(
- 'New analysis driver enabled', server.options.enableNewAnalysisDriver));
- buf.writeln(writeOption('Instrumentation enabled',
- AnalysisEngine.instance.instrumentationService.isActive));
- buf.writeln(writeOption('Server process ID', pid));
- buf.writeln('</div>');
+ String get navDetail => null;
- buf.writeln('<div class="column one-half">');
- h3('Versions');
- buf.writeln(writeOption('Analysis server version', AnalysisServer.VERSION));
- buf.writeln(writeOption('Dart SDK', Platform.version));
+ Future<Null> generateContainer(Map<String, String> params) async {
+ buf.writeln('<div class="columns docs-layout">');
+
+ buf.writeln('<div class="one-fifth column">');
+ buf.writeln('<nav class="menu docs-menu">');
+ for (Page page in site.pages.where((p) => p is DiagnosticPageWithNav)) {
+ buf.write('<a class="menu-item ${page == this ? ' selected' : ''}" '
+ 'href="${page.path}">${escape(page.title)}');
+ String detail = (page as DiagnosticPageWithNav).navDetail;
+ if (detail != null) {
+ buf.write('<span class="counter">$detail</span>');
+ }
+ buf.writeln('</a>');
+ }
+ buf.writeln('</nav>');
buf.writeln('</div>');
+ buf.writeln('<div class="four-fifths column markdown-body">');
+ h1(title, classes: 'page-title');
+ await asyncDiv(() async {
+ p(description);
+ await generateContent(params);
+ }, classes: 'markdown-body');
buf.writeln('</div>');
- List<String> lines = (site as DiagnosticsSite).lastPrintedLines;
- if (lines.isNotEmpty) {
- h3('Debug output');
- p(lines.join('\n'), style: 'white-space: pre');
- }
+ buf.writeln('</div>');
}
}
-class InstrumentationPage extends DiagnosticPageWithNav {
- InstrumentationPage(DiagnosticsSite site)
- : super(site, 'instrumentation', 'Instrumentation',
- description:
- 'Verbose instrumentation data from the analysis server.');
+class DiagnosticsSite extends Site implements AbstractGetHandler {
+ /// An object that can handle either a WebSocket connection or a connection
+ /// to the client over stdio.
+ SocketServer socketServer;
- @override
- void generateContent(Map<String, String> params) {
- p(
- 'Instrumentation can be enabled by starting the analysis server with the '
- '<code>--instrumentation-log-file=path/to/file</code> flag.',
- raw: true);
+ /// The last few lines printed.
+ List<String> lastPrintedLines = <String>[];
+
+ DiagnosticsSite(this.socketServer, this.lastPrintedLines)
+ : super('Analysis Server') {
+ pages.add(new CompletionPage(this));
+ pages.add(new CommunicationsPage(this));
+ pages.add(new ContextsPage(this));
+ pages.add(new ExceptionsPage(this));
+ pages.add(new InstrumentationPage(this));
+ pages.add(new OverlaysPage(this));
+ pages.add(new PluginsPage(this));
+ pages.add(new ProfilePage(this));
+ pages.add(new SubscriptionsPage(this));
- if (!AnalysisEngine.instance.instrumentationService.isActive) {
- blankslate('Instrumentation not active.');
- return;
+ ProcessProfiler profiler = ProcessProfiler.getProfilerForPlatform();
+ if (profiler != null) {
+ pages.add(new MemoryAndCpuPage(this, profiler));
}
- h3('Instrumentation');
+ pages.sort(((Page a, Page b) =>
+ a.title.toLowerCase().compareTo(b.title.toLowerCase())));
- p('Instrumentation active.');
+ // Add the status page at the beginning.
+ pages.insert(0, new StatusPage(this));
- InstrumentationServer instrumentation =
- AnalysisEngine.instance.instrumentationService.instrumentationServer;
- String description = instrumentation.describe;
- HtmlEscape htmlEscape = new HtmlEscape(HtmlEscapeMode.ELEMENT);
- description = htmlEscape.convert(description);
- // Convert http(s): references to hyperlinks.
- final RegExp urlRegExp = new RegExp(r'[http|https]+:\/*(\S+)');
- description = description.replaceAllMapped(urlRegExp, (Match match) {
- return '<a href="${match.group(0)}">${match.group(1)}</a>';
- });
- p(description.replaceAll('\n', '<br>'), raw: true);
+ // Add non-nav pages.
+ pages.add(new FeedbackPage(this));
+
+ secondaryPages.add(new AstPage(this));
+ secondaryPages.add(new ElementModelPage(this));
}
-}
-class ProfilePage extends DiagnosticPageWithNav {
- ProfilePage(DiagnosticsSite site)
- : super(site, 'profile', 'Profiling Info',
- description: 'Profiling performance tag data.');
+ String get customCss => kCustomCss;
- @override
- void generateContent(Map<String, String> params) {
- // prepare sorted tags
- List<PerformanceTag> tags = PerformanceTag.all.toList();
- tags.remove(ServerPerformanceStatistics.idle);
- tags.remove(PerformanceTag.unknown);
- tags.removeWhere((tag) => tag.elapsedMs == 0);
- tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
+ Page createExceptionPage(String message, StackTrace trace) =>
+ new ExceptionPage(this, message, trace);
- // draw a pie chart
- String rowData =
- tags.map((tag) => "['${tag.label}', ${tag.elapsedMs}]").join(',');
- buf.writeln(
- '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
- buf.writeln('''
- <script type="text/javascript">
- google.charts.load('current', {'packages':['corechart']});
- google.charts.setOnLoadCallback(drawChart);
+ Page createUnknownPage(String unknownPath) =>
+ new NotFoundPage(this, unknownPath);
+}
- function drawChart() {
- var data = new google.visualization.DataTable();
- data.addColumn('string', 'Tag');
- data.addColumn('number', 'Time (ms)');
- data.addRows([$rowData]);
- var options = {'title': 'Performance Tag Data', 'width': 700, 'height': 300};
- var chart = new google.visualization.PieChart(document.getElementById('chart-div'));
- chart.draw(data, options);
- }
- </script>
-''');
+class ElementModelPage extends DiagnosticPageWithNav {
+ String _description;
- // print total time
- int totalTime =
- tags.fold<int>(0, (int a, PerformanceTag tag) => a + tag.elapsedMs);
- p('Total measured time: ${printMilliseconds(totalTime)}');
+ ElementModelPage(DiagnosticsSite site)
+ : super(site, 'element', 'Element model',
+ description: 'The element model for a file.');
- // write out a table
- void _writeRow(List<String> data, {bool header: false}) {
- buf.write('<tr>');
- if (header) {
- for (String d in data) {
- buf.write('<th>$d</th>');
- }
- } else {
- buf.write('<td>${data[0]}</td>');
+ @override
+ String get description => _description ?? super.description;
- for (String d in data.sublist(1)) {
- buf.write('<td class="right">$d</td>');
- }
- }
- buf.writeln('</tr>');
+ @override
+ Future<Null> generateContent(Map<String, String> params) async {
+ String path = params['file'];
+ if (path == null) {
+ p('No file path provided.');
+ return;
+ }
+ AnalysisDriver driver = server.getAnalysisDriver(path);
+ if (driver == null) {
+ p('The file <code>${escape(path)}</code> is not being analyzed.',
+ raw: true);
+ return;
+ }
+ AnalysisResult result = await driver.getResult(path);
+ if (result == null) {
+ p('An element model could not be produced for the file <code>${escape(path)}</code>.',
+ raw: true);
+ return;
}
- buf.write('<table>');
- _writeRow(['Tag name', 'Time (in ms)', 'Percent'], header: true);
- void writeRow(PerformanceTag tag) {
- double percent = tag.elapsedMs / totalTime;
- _writeRow([
- tag.label,
- printMilliseconds(tag.elapsedMs),
- printPercentage(percent)
- ]);
+ ElementWriter writer = new ElementWriter(buf);
+ result.unit.element.accept(writer);
+ }
+
+ @override
+ Future<Null> generatePage(Map<String, String> params) async {
+ try {
+ String path = params['file'];
+ _description = path == null ? null : 'The element model for $path.';
+ await super.generatePage(params);
+ } finally {
+ _description = null;
}
+ }
+}
- tags.forEach(writeRow);
- buf.write('</table>');
+class ExceptionPage extends DiagnosticPage {
+ final StackTrace trace;
+
+ ExceptionPage(Site site, String message, this.trace)
+ : super(site, '', '500 Oops', description: message);
+
+ void generateContent(Map<String, String> params) {
+ p(trace.toString(), style: 'white-space: pre');
}
}
@@ -597,6 +771,8 @@ class ExceptionsPage extends DiagnosticPageWithNav {
: super(site, 'exceptions', 'Exceptions',
description: 'Exceptions from the analysis server.');
+ Iterable<ServerException> get exceptions => server.exceptions.items;
+
String get navDetail => printInteger(exceptions.length);
@override
@@ -614,208 +790,80 @@ class ExceptionsPage extends DiagnosticPageWithNav {
}
}
}
-
- Iterable<ServerException> get exceptions => server.exceptions.items;
}
-class ContextsPage extends DiagnosticPageWithNav {
- ContextsPage(DiagnosticsSite site)
- : super(site, 'contexts', 'Contexts',
- description:
- 'An analysis context defines the options and the set of sources being analyzed.');
-
- String get navDetail => printInteger(server.driverMap.length);
+class FeedbackPage extends DiagnosticPage {
+ FeedbackPage(DiagnosticsSite site)
+ : super(site, 'feedback', 'Feedback',
+ description: 'Providing feedback and filing issues.');
@override
void generateContent(Map<String, String> params) {
- Map<Folder, AnalysisDriver> driverMap = server.driverMap;
- if (driverMap.isEmpty) {
- blankslate('No contexts.');
- return;
- }
-
- String contextPath = params['context'];
- List<Folder> folders = driverMap.keys.toList();
- folders
- .sort((first, second) => first.shortName.compareTo(second.shortName));
- Folder folder =
- folders.firstWhere((f) => f.path == contextPath, orElse: () => null);
-
- if (folder == null) {
- folder = folders.first;
- contextPath = folder.path;
- }
-
- AnalysisDriver driver = driverMap[folder];
-
- buf.writeln('<div class="tabnav">');
- buf.writeln('<nav class="tabnav-tabs">');
- for (Folder f in folders) {
- if (f == folder) {
- buf.writeln(
- '<a class="tabnav-tab selected">${escape(f.shortName)}</a>');
- } else {
- String p = '$path?context=${Uri.encodeQueryComponent(f.path)}';
- buf.writeln(
- '<a href="$p" class="tabnav-tab">${escape(f.shortName)}</a>');
- }
- }
- buf.writeln('</nav>');
- buf.writeln('</div>');
-
- buf.writeln(writeOption('Context location', escape(contextPath)));
- buf.writeln(writeOption('Analysis options path',
- escape(driver.contextRoot.optionsFilePath ?? 'none')));
-
- buf.writeln('<div class="columns">');
-
- buf.writeln('<div class="column one-half">');
- h3('Analysis options');
- p(describe(driver.analysisOptions), raw: true);
- buf.writeln(
- writeOption('Has .packages file', folder.getChild('.packages').exists));
- buf.writeln(writeOption(
- 'Has pubspec.yaml file', folder.getChild('pubspec.yaml').exists));
- buf.writeln('</div>');
-
- buf.writeln('<div class="column one-half">');
- DartSdk sdk = driver?.sourceFactory?.dartSdk;
- AnalysisOptionsImpl sdkOptions = sdk?.context?.analysisOptions;
- if (sdkOptions != null) {
- h3('SDK analysis options');
- p(describe(sdkOptions), raw: true);
-
- if (sdk is FolderBasedDartSdk) {
- p(writeOption('Use summaries', sdk.useSummary), raw: true);
- }
- }
- buf.writeln('</div>');
-
- buf.writeln('</div>');
-
- h3('Lints');
- p(driver.analysisOptions.lintRules.map((l) => l.name).join(', '));
-
- h3('Error processors');
- p(driver.analysisOptions.errorProcessors
- .map((e) => e.description)
- .join(', '));
-
- List<String> priorityFiles = driver.priorityFiles;
- List<String> addedFiles = driver.addedFiles.toList();
- List<String> implicitFiles =
- driver.knownFiles.difference(driver.addedFiles).toList();
- addedFiles.sort();
- implicitFiles.sort();
+ final String issuesUrl = 'https://github.com/dart-lang/sdk/issues';
+ p(
+ 'To file issues or feature requests, see our '
+ '<a href="$issuesUrl">bug tracker</a>. When filing an issue, please describe:',
+ raw: true,
+ );
+ ul([
+ 'what you were doing',
+ 'what occured',
+ 'what you think the expected behavior should have been',
+ ], (line) => buf.writeln(line));
- String lenCounter(List list) {
- return '<span class="counter" style="float: right;">${list.length}</span>';
+ List<String> ideInfo = [];
+ if (server.options.clientId != null) {
+ ideInfo.add(server.options.clientId);
}
-
- h3('Context files');
-
- void writeFile(String file) {
- String astPath = '/ast?file=${Uri.encodeQueryComponent(file)}';
- String elementPath = '/element?file=${Uri.encodeQueryComponent(file)}';
-
- buf.write(file);
- buf.write(' (');
- buf.writeln('<a href="$astPath">ast</a>');
- buf.write(' ');
- buf.writeln('<a href="$elementPath">element</a>');
- buf.write(')');
+ if (server.options.clientVersion != null) {
+ ideInfo.add(server.options.clientVersion);
}
+ String ideText = ideInfo.map((str) => '<code>$str</code>').join(', ');
- h4('Priority files ${lenCounter(priorityFiles)}', raw: true);
- ul(priorityFiles, writeFile, classes: 'scroll-table');
-
- h4('Added files ${lenCounter(addedFiles)}', raw: true);
- ul(addedFiles, writeFile, classes: 'scroll-table');
-
- h4('ImplicitFiles files ${lenCounter(implicitFiles)}', raw: true);
- ul(implicitFiles, writeFile, classes: 'scroll-table');
+ p('Other data to include:');
+ ul([
+ "the IDE you are using and it's version${ideText.isEmpty ? '' : ' ($ideText)'}",
+ 'the Dart SDK version (<code>${escape(_sdkVersion)}</code>)',
+ 'your operating system (<code>${escape(Platform.operatingSystem)}</code>)',
+ ], (line) => buf.writeln(line));
- SourceFactory sourceFactory = driver.sourceFactory;
- if (sourceFactory is SourceFactoryImpl) {
- h3('Resolvers');
- for (UriResolver resolver in sourceFactory.resolvers) {
- h4(resolver.runtimeType.toString());
- buf.write('<p class="scroll-table">');
- if (resolver is DartUriResolver) {
- DartSdk sdk = resolver.dartSdk;
- buf.write(' (sdk = ');
- buf.write(sdk.runtimeType);
- if (sdk is FolderBasedDartSdk) {
- buf.write(' (path = ');
- buf.write(sdk.directory.path);
- buf.write(')');
- } else if (sdk is EmbedderSdk) {
- buf.write(' (map = ');
- writeMap(sdk.urlMappings);
- buf.write(')');
- }
- buf.write(')');
- } else if (resolver is SdkExtUriResolver) {
- buf.write(' (map = ');
- writeMap(resolver.urlMappings);
- buf.write(')');
- } else if (resolver is PackageMapUriResolver) {
- writeMap(resolver.packageMap);
- }
- buf.write('</p>');
- }
- }
+ p('Thanks!');
}
+}
- String describe(AnalysisOptionsImpl options) {
- StringBuffer b = new StringBuffer();
+class InstrumentationPage extends DiagnosticPageWithNav {
+ InstrumentationPage(DiagnosticsSite site)
+ : super(site, 'instrumentation', 'Instrumentation',
+ description:
+ 'Verbose instrumentation data from the analysis server.');
- b.write(
- writeOption('Analyze function bodies', options.analyzeFunctionBodies));
- b.write(writeOption('Enable asserts in initializer lists',
- options.enableAssertInitializer));
- b.write(writeOption(
- 'Enable strict call checks', options.enableStrictCallChecks));
- b.write(writeOption('Enable super mixins', options.enableSuperMixins));
- b.write(writeOption('Generate dart2js hints', options.dart2jsHint));
- b.write(writeOption(
- 'Generate errors in implicit files', options.generateImplicitErrors));
- b.write(
- writeOption('Generate errors in SDK files', options.generateSdkErrors));
- b.write(writeOption('Generate hints', options.hint));
- b.write(writeOption('Incremental resolution', options.incremental));
- b.write(writeOption(
- 'Incremental resolution with API changes', options.incrementalApi));
- b.write(writeOption('Preserve comments', options.preserveComments));
- b.write(writeOption('Strong mode', options.strongMode));
- b.write(writeOption('Strong mode hints', options.strongModeHints));
+ @override
+ void generateContent(Map<String, String> params) {
+ p(
+ 'Instrumentation can be enabled by starting the analysis server with the '
+ '<code>--instrumentation-log-file=path/to/file</code> flag.',
+ raw: true);
- return b.toString();
- }
+ if (!AnalysisEngine.instance.instrumentationService.isActive) {
+ blankslate('Instrumentation not active.');
+ return;
+ }
- void writeList<E>(List<E> list) {
- buf.writeln('[${list.join(', ')}]');
- }
+ h3('Instrumentation');
- void writeMap<V>(Map<String, V> map) {
- List<String> keys = map.keys.toList();
- keys.sort();
- int length = keys.length;
- buf.write('{');
- for (int i = 0; i < length; i++) {
- buf.write('<br>');
- String key = keys[i];
- V value = map[key];
- buf.write(key);
- buf.write(' = ');
- if (value is List) {
- writeList(value);
- } else {
- buf.write(value);
- }
- buf.write(',');
- }
- buf.write('<br>}');
+ p('Instrumentation active.');
+
+ InstrumentationServer instrumentation =
+ AnalysisEngine.instance.instrumentationService.instrumentationServer;
+ String description = instrumentation.describe;
+ HtmlEscape htmlEscape = new HtmlEscape(HtmlEscapeMode.ELEMENT);
+ description = htmlEscape.convert(description);
+ // Convert http(s): references to hyperlinks.
+ final RegExp urlRegExp = new RegExp(r'[http|https]+:\/*(\S+)');
+ description = description.replaceAllMapped(urlRegExp, (Match match) {
+ return '<a href="${match.group(0)}">${match.group(1)}</a>';
+ });
+ p(description.replaceAll('\n', '<br>'), raw: true);
}
}
@@ -826,6 +874,11 @@ class MemoryAndCpuPage extends DiagnosticPageWithNav {
: super(site, 'memory', 'Memory and CPU Usage',
description: 'Memory and CPU usage for the analysis server.');
+ DiagnosticDomainHandler get diagnosticDomain {
+ return server.handlers
+ .firstWhere((handler) => handler is DiagnosticDomainHandler);
+ }
+
@override
void generateContent(Map<String, String> params) {
UsageInfo usage = profiler.getProcessUsageSync(pid);
@@ -838,11 +891,15 @@ class MemoryAndCpuPage extends DiagnosticPageWithNav {
p('Error retreiving the memory and cpu usage information.');
}
}
+}
- DiagnosticDomainHandler get diagnosticDomain {
- return server.handlers
- .firstWhere((handler) => handler is DiagnosticDomainHandler);
- }
+class NotFoundPage extends DiagnosticPage {
+ final String path;
+
+ NotFoundPage(Site site, this.path)
+ : super(site, '', '404 Not found', description: "'$path' not found.");
+
+ void generateContent(Map<String, String> params) {}
}
class OverlaysPage extends DiagnosticPageWithNav {
@@ -867,56 +924,166 @@ class OverlaysPage extends DiagnosticPageWithNav {
p('<code>${escape(overlayPath)}</code> not found.', raw: true);
}
- return;
- }
+ return;
+ }
+
+ if (paths.isEmpty) {
+ blankslate('No overlays.');
+ } else {
+ String lenCounter(List list) {
+ return '<span class="counter" style="float: right;">${list.length}</span>';
+ }
+
+ h3('Overlays ${lenCounter(paths)}', raw: true);
+ ul(paths, (String overlayPath) {
+ String uri = '$path?overlay=${Uri.encodeQueryComponent(overlayPath)}';
+ buf.writeln('<a href="$uri">${escape(overlayPath)}</a>');
+ });
+ }
+ }
+}
+
+class PluginsPage extends DiagnosticPageWithNav {
+ PluginsPage(DiagnosticsSite site)
+ : super(site, 'plugins', 'Plugins', description: 'Plugins in use.');
+
+ @override
+ void generateContent(Map<String, String> params) {
+ h3('Analysis plugins');
+ List<PluginInfo> analysisPlugins = server.pluginManager.plugins;
+
+ if (analysisPlugins.isEmpty) {
+ blankslate('No analysis plugins active.');
+ } else {
+ ul(analysisPlugins, (PluginInfo p) {
+ buf.writeln('${p.data.name} ${p.pluginId} (${p.data.version})');
+ });
+ }
+
+ h3('Analyzer plugins');
+ void writePlugin(Plugin plugin) {
+ buf.write(plugin.uniqueIdentifier);
+ buf.write(' (');
+ buf.write(plugin.runtimeType);
+ buf.write(')');
+ }
+
+ List<Plugin> plugins = [
+ AnalysisEngine.instance.enginePlugin,
+ server.serverPlugin
+ ];
+ plugins.addAll(server.userDefinedPlugins);
+ ul(plugins, writePlugin);
+ }
+}
+
+// TODO(devoncarew): Show the last x requests and responses.
+class ProfilePage extends DiagnosticPageWithNav {
+ ProfilePage(DiagnosticsSite site)
+ : super(site, 'profile', 'Profiling Info',
+ description: 'Profiling performance tag data.');
+
+ @override
+ void generateContent(Map<String, String> params) {
+ // prepare sorted tags
+ List<PerformanceTag> tags = PerformanceTag.all.toList();
+ tags.remove(ServerPerformanceStatistics.idle);
+ tags.remove(PerformanceTag.unknown);
+ tags.removeWhere((tag) => tag.elapsedMs == 0);
+ tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
+
+ // draw a pie chart
+ String rowData =
+ tags.map((tag) => "['${tag.label}', ${tag.elapsedMs}]").join(',');
+ buf.writeln(
+ '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
+ buf.writeln('''
+ <script type="text/javascript">
+ google.charts.load('current', {'packages':['corechart']});
+ google.charts.setOnLoadCallback(drawChart);
+
+ function drawChart() {
+ var data = new google.visualization.DataTable();
+ data.addColumn('string', 'Tag');
+ data.addColumn('number', 'Time (ms)');
+ data.addRows([$rowData]);
+ var options = {'title': 'Performance Tag Data', 'width': 700, 'height': 300};
+ var chart = new google.visualization.PieChart(document.getElementById('chart-div'));
+ chart.draw(data, options);
+ }
+ </script>
+''');
+
+ // print total time
+ int totalTime =
+ tags.fold<int>(0, (int a, PerformanceTag tag) => a + tag.elapsedMs);
+ p('Total measured time: ${printMilliseconds(totalTime)}');
+
+ // write out a table
+ void _writeRow(List<String> data, {bool header: false}) {
+ buf.write('<tr>');
+ if (header) {
+ for (String d in data) {
+ buf.write('<th>$d</th>');
+ }
+ } else {
+ buf.write('<td>${data[0]}</td>');
- if (paths.isEmpty) {
- blankslate('No overlays.');
- } else {
- String lenCounter(List list) {
- return '<span class="counter" style="float: right;">${list.length}</span>';
+ for (String d in data.sublist(1)) {
+ buf.write('<td class="right">$d</td>');
+ }
}
+ buf.writeln('</tr>');
+ }
- h3('Overlays ${lenCounter(paths)}', raw: true);
- ul(paths, (String overlayPath) {
- String uri = '$path?overlay=${Uri.encodeQueryComponent(overlayPath)}';
- buf.writeln('<a href="$uri">${escape(overlayPath)}</a>');
- });
+ buf.write('<table>');
+ _writeRow(['Tag name', 'Time (in ms)', 'Percent'], header: true);
+ void writeRow(PerformanceTag tag) {
+ double percent = tag.elapsedMs / totalTime;
+ _writeRow([
+ tag.label,
+ printMilliseconds(tag.elapsedMs),
+ printPercentage(percent)
+ ]);
}
+
+ tags.forEach(writeRow);
+ buf.write('</table>');
}
}
-class PluginsPage extends DiagnosticPageWithNav {
- PluginsPage(DiagnosticsSite site)
- : super(site, 'plugins', 'Plugins', description: 'Plugins in use.');
+class StatusPage extends DiagnosticPageWithNav {
+ StatusPage(DiagnosticsSite site)
+ : super(site, 'status', 'Status',
+ description:
+ 'General status and diagnostics for the analysis server.');
@override
void generateContent(Map<String, String> params) {
- h3('Analysis plugins');
- List<PluginInfo> analysisPlugins = server.pluginManager.plugins;
+ buf.writeln('<div class="columns">');
- if (analysisPlugins.isEmpty) {
- blankslate('No analysis plugins active.');
- } else {
- ul(analysisPlugins, (PluginInfo p) {
- buf.writeln('${p.data.name} ${p.pluginId} (${p.data.version})');
- });
- }
+ buf.writeln('<div class="column one-half">');
+ h3('Status');
+ buf.writeln(writeOption(
+ 'New analysis driver enabled', server.options.enableNewAnalysisDriver));
+ buf.writeln(writeOption('Instrumentation enabled',
+ AnalysisEngine.instance.instrumentationService.isActive));
+ buf.writeln(writeOption('Server process ID', pid));
+ buf.writeln('</div>');
- h3('Analyzer plugins');
- void writePlugin(Plugin plugin) {
- buf.write(plugin.uniqueIdentifier);
- buf.write(' (');
- buf.write(plugin.runtimeType);
- buf.write(')');
- }
+ buf.writeln('<div class="column one-half">');
+ h3('Versions');
+ buf.writeln(writeOption('Analysis server version', AnalysisServer.VERSION));
+ buf.writeln(writeOption('Dart SDK', Platform.version));
+ buf.writeln('</div>');
- List<Plugin> plugins = [
- AnalysisEngine.instance.enginePlugin,
- server.serverPlugin
- ];
- plugins.addAll(server.userDefinedPlugins);
- ul(plugins, writePlugin);
+ buf.writeln('</div>');
+
+ List<String> lines = (site as DiagnosticsSite).lastPrintedLines;
+ if (lines.isNotEmpty) {
+ h3('Debug output');
+ p(lines.join('\n'), style: 'white-space: pre');
+ }
}
}
@@ -961,170 +1128,3 @@ class SubscriptionsPage extends DiagnosticPageWithNav {
});
}
}
-
-class CompletionPage extends DiagnosticPageWithNav {
- CompletionPage(DiagnosticsSite site)
- : super(site, 'completion', 'Code Completion',
- description: 'Latency statistics for code completion.');
-
- @override
- void generateContent(Map<String, String> params) {
- CompletionDomainHandler completionDomain = server.handlers
- .firstWhere((handler) => handler is CompletionDomainHandler);
-
- List<CompletionPerformance> completions =
- completionDomain.performanceList.items.toList();
-
- if (completions.isEmpty) {
- blankslate('No completions recorded.');
- return;
- }
-
- int fastCount =
- completions.where((c) => c.elapsedInMilliseconds <= 100).length;
- p('${completions.length} results; ${printPercentage(fastCount / completions.length)} within 100ms.');
-
- // draw a chart
- buf.writeln(
- '<div id="chart-div" style="width: 700px; height: 300px;"></div>');
- StringBuffer rowData = new StringBuffer();
- for (int i = completions.length - 1; i >= 0; i--) {
- // [' ', 101.5]
- if (rowData.isNotEmpty) {
- rowData.write(',');
- }
- rowData.write("[' ', ${completions[i].elapsedInMilliseconds}]");
- }
- buf.writeln('''
- <script type="text/javascript">
- google.charts.load('current', {'packages':['bar']});
- google.charts.setOnLoadCallback(drawChart);
- function drawChart() {
- var data = google.visualization.arrayToDataTable([
- ['Completions', 'Time'],
- $rowData
- ]);
- var options = { bars: 'vertical', vAxis: {format: 'decimal'}, height: 300 };
- var chart = new google.charts.Bar(document.getElementById('chart-div'));
- chart.draw(data, google.charts.Bar.convertOptions(options));
- }
- </script>
-''');
-
- // emit the data as a table
- buf.writeln('<table>');
- buf.writeln(
- '<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>');
- for (CompletionPerformance completion in completions) {
- buf.writeln('<tr>'
- '<td class="pre right">${printMilliseconds(completion.elapsedInMilliseconds)}</td>'
- '<td class="right">${completion.suggestionCount}</td>'
- '<td>${escape(completion.source.shortName)}</td>'
- '<td><code>${escape(completion.snippet)}</code></td>'
- '</tr>');
- }
- buf.writeln('</table>');
- }
-}
-
-// TODO(devoncarew): Show the last x requests and responses.
-class CommunicationsPage extends DiagnosticPageWithNav {
- CommunicationsPage(DiagnosticsSite site)
- : super(site, 'communications', 'Communications',
- description:
- 'Latency statistics for analysis server communications.');
-
- @override
- void generateContent(Map<String, String> params) {
- void writeRow(List<String> data, {List<String> classes}) {
- buf.write("<tr>");
- for (int i = 0; i < data.length; i++) {
- String c = classes == null ? null : classes[i];
- if (c != null) {
- buf.write('<td class="$c">${escape(data[i])}</td>');
- } else {
- buf.write('<td>${escape(data[i])}</td>');
- }
- }
- buf.writeln("</tr>");
- }
-
- buf.writeln('<div class="columns">');
-
- ServerPerformance perf = server.performanceAfterStartup;
- if (perf != null) {
- buf.writeln('<div class="column one-half">');
- h3('Current');
-
- int requestCount = perf.requestCount;
- int averageLatency =
- requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
- int maximumLatency = perf.maxLatency;
- double slowRequestPercent =
- requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
-
- buf.write('<table>');
- writeRow([printInteger(requestCount), 'requests'],
- classes: ["right", null]);
- writeRow([printMilliseconds(averageLatency), 'average latency'],
- classes: ["right", null]);
- writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
- classes: ["right", null]);
- writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
- classes: ["right", null]);
- buf.write('</table>');
-
- String time = server.uptime.toString();
- if (time.contains('.')) {
- time = time.substring(0, time.indexOf('.'));
- }
- buf.writeln(writeOption('Uptime', time));
-
- buf.write('</div>');
- }
-
- buf.writeln('<div class="column one-half">');
- h3('Startup');
- perf = server.performanceDuringStartup;
-
- int requestCount = perf.requestCount;
- int averageLatency =
- requestCount > 0 ? (perf.requestLatency ~/ requestCount) : 0;
- int maximumLatency = perf.maxLatency;
- double slowRequestPercent =
- requestCount > 0 ? (perf.slowRequestCount / requestCount) : 0.0;
-
- buf.write('<table>');
- writeRow([printInteger(requestCount), 'requests'],
- classes: ["right", null]);
- writeRow([printMilliseconds(averageLatency), 'average latency'],
- classes: ["right", null]);
- writeRow([printMilliseconds(maximumLatency), 'maximum latency'],
- classes: ["right", null]);
- writeRow([printPercentage(slowRequestPercent), '> 150 ms latency'],
- classes: ["right", null]);
- buf.write('</table>');
-
- if (server.performanceAfterStartup != null) {
- int startupTime =
- server.performanceAfterStartup.startTime - perf.startTime;
- buf.writeln(
- writeOption('Initial analysis time', printMilliseconds(startupTime)));
- }
- buf.write('</div>');
-
- buf.write('</div>');
- }
-}
-
-String writeOption(String name, dynamic value) {
- return '$name: <code>$value</code><br> ';
-}
-
-String get _sdkVersion {
- String version = Platform.version;
- if (version.contains(' ')) {
- version = version.substring(0, version.indexOf(' '));
- }
- return version;
-}
« no previous file with comments | « pkg/analysis_server/lib/src/constants.dart ('k') | pkg/analysis_server/lib/src/status/pages.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698