Skip to content

Commit 76be1bc

Browse files
pqCommit Queue
authored andcommitted
[analytics] add getFixes timings to diagnostics/reports
See: #60258 Change-Id: I756cb04d3968ca02dbef332f45a6fbea5b1b8c3f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414180 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 5883289 commit 76be1bc

File tree

11 files changed

+195
-11
lines changed

11 files changed

+195
-11
lines changed

pkg/analysis_server/lib/src/analysis_server.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import 'package:analysis_server/src/server/message_scheduler.dart';
3232
import 'package:analysis_server/src/server/performance.dart';
3333
import 'package:analysis_server/src/services/completion/completion_performance.dart';
3434
import 'package:analysis_server/src/services/correction/assist_internal.dart';
35+
import 'package:analysis_server/src/services/correction/fix_performance.dart';
3536
import 'package:analysis_server/src/services/correction/namespace.dart';
3637
import 'package:analysis_server/src/services/dart_tooling_daemon/dtd_services.dart';
3738
import 'package:analysis_server/src/services/pub/pub_api.dart';
@@ -1219,6 +1220,11 @@ class ServerRecentPerformance {
12191220
final RecentBuffer<CompletionPerformance> completion =
12201221
RecentBuffer<CompletionPerformance>(performanceListMaxLength);
12211222

1223+
/// A list of performance measurements for the latest requests to get fixes
1224+
/// up to [performanceListMaxLength] measurements.
1225+
final RecentBuffer<GetFixesPerformance> getFixes =
1226+
RecentBuffer<GetFixesPerformance>(performanceListMaxLength);
1227+
12221228
/// A [RecentBuffer] for performance information about the most recent
12231229
/// requests.
12241230
final RecentBuffer<RequestPerformance> requests = RecentBuffer(

pkg/analysis_server/lib/src/handler/legacy/edit_get_fixes.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:analysis_server/src/protocol_server.dart';
1111
import 'package:analysis_server/src/request_handler_mixin.dart';
1212
import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
1313
import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart';
14+
import 'package:analysis_server/src/services/correction/fix_performance.dart';
1415
import 'package:analysis_server_plugin/edit/fix/dart_fix_context.dart';
1516
import 'package:analysis_server_plugin/edit/fix/fix.dart';
1617
import 'package:analysis_server_plugin/src/correction/dart_change_workspace.dart';
@@ -201,7 +202,18 @@ class EditGetFixesHandler extends LegacyHandler
201202

202203
List<Fix> fixes;
203204
try {
204-
fixes = await computeFixes(context);
205+
var peformanceTracker = FixPerformance();
206+
fixes = await computeFixes(context, performance: peformanceTracker);
207+
208+
server.recentPerformance.getFixes.add(
209+
GetFixesPerformance(
210+
performance: performance,
211+
path: file,
212+
content: unitResult.content,
213+
offset: offset,
214+
requestLatency: peformanceTracker.computeTime!.inMilliseconds,
215+
),
216+
);
205217
} on InconsistentAnalysisException {
206218
fixes = [];
207219
} catch (exception, stackTrace) {

pkg/analysis_server/lib/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:analyzer/error/error.dart';
1717
import 'package:analyzer/file_system/file_system.dart';
1818
import 'package:analyzer/source/line_info.dart';
1919
import 'package:analyzer/src/dart/analysis/results.dart' as engine;
20+
import 'package:analyzer/src/util/performance/operation_performance.dart';
2021
import 'package:meta/meta.dart';
2122

2223
typedef CodeActionWithPriority = ({CodeAction action, int priority});
@@ -171,7 +172,9 @@ abstract class AbstractCodeActionsProducer
171172

172173
Future<List<CodeActionWithPriority>> getAssistActions();
173174

174-
Future<List<CodeActionWithPriority>> getFixActions();
175+
Future<List<CodeActionWithPriority>> getFixActions(
176+
OperationPerformance? performance,
177+
);
175178

176179
Future<List<Either2<CodeAction, Command>>> getRefactorActions();
177180

pkg/analysis_server/lib/src/lsp/handlers/code_actions/analysis_options.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:analyzer/source/line_info.dart';
1212
import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
1313
import 'package:analyzer/src/generated/source.dart' show SourceFactory;
1414
import 'package:analyzer/src/task/options.dart';
15+
import 'package:analyzer/src/util/performance/operation_performance.dart';
1516
import 'package:analyzer/src/workspace/pub.dart';
1617
import 'package:yaml/yaml.dart';
1718

@@ -35,7 +36,9 @@ class AnalysisOptionsCodeActionsProducer extends AbstractCodeActionsProducer {
3536
Future<List<CodeActionWithPriority>> getAssistActions() async => [];
3637

3738
@override
38-
Future<List<CodeActionWithPriority>> getFixActions() async {
39+
Future<List<CodeActionWithPriority>> getFixActions(
40+
OperationPerformance? performance,
41+
) async {
3942
var session = await server.getAnalysisSession(path);
4043
if (session == null) {
4144
return [];

pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:analysis_server/src/protocol_server.dart'
1212
hide AnalysisOptions, Position;
1313
import 'package:analysis_server/src/services/correction/assist.dart';
1414
import 'package:analysis_server/src/services/correction/assist_internal.dart';
15+
import 'package:analysis_server/src/services/correction/fix_performance.dart';
1516
import 'package:analysis_server/src/services/refactoring/framework/refactoring_context.dart';
1617
import 'package:analysis_server/src/services/refactoring/framework/refactoring_processor.dart';
1718
import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart';
@@ -23,6 +24,7 @@ import 'package:analyzer/dart/analysis/session.dart'
2324
show InconsistentAnalysisException;
2425
import 'package:analyzer/dart/element/element2.dart';
2526
import 'package:analyzer/src/dart/ast/utilities.dart';
27+
import 'package:analyzer/src/util/performance/operation_performance.dart';
2628

2729
/// Produces [CodeAction]s from Dart source commands, fixes, assists and
2830
/// refactors from the server.
@@ -151,7 +153,9 @@ class DartCodeActionsProducer extends AbstractCodeActionsProducer {
151153
}
152154

153155
@override
154-
Future<List<CodeActionWithPriority>> getFixActions() async {
156+
Future<List<CodeActionWithPriority>> getFixActions(
157+
OperationPerformance? performance,
158+
) async {
155159
// These fixes are only provided as literal CodeActions.
156160
if (!supportsLiterals) {
157161
return [];
@@ -181,7 +185,24 @@ class DartCodeActionsProducer extends AbstractCodeActionsProducer {
181185
unitResult: unitResult,
182186
error: error,
183187
);
184-
var fixes = await computeFixes(context);
188+
189+
// TODO(pq): move this timer UP?
190+
var peformanceTracker = FixPerformance();
191+
192+
var fixes = await computeFixes(context, performance: peformanceTracker);
193+
194+
if (performance != null) {
195+
server.recentPerformance.getFixes.add(
196+
GetFixesPerformance(
197+
performance: performance,
198+
path: path,
199+
content: unitResult.content,
200+
offset: offset,
201+
requestLatency: peformanceTracker.computeTime!.inMilliseconds,
202+
),
203+
);
204+
}
205+
185206
if (fixes.isNotEmpty) {
186207
var diagnostic = toDiagnostic(
187208
server.uriConverter,

pkg/analysis_server/lib/src/lsp/handlers/code_actions/plugins.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:analysis_server/lsp_protocol/protocol.dart';
88
import 'package:analysis_server/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart';
99
import 'package:analysis_server/src/lsp/mapping.dart';
1010
import 'package:analyzer/src/dart/analysis/driver.dart';
11+
import 'package:analyzer/src/util/performance/operation_performance.dart';
1112
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
1213
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
1314
import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
@@ -49,7 +50,9 @@ class PluginCodeActionsProducer extends AbstractCodeActionsProducer {
4950
}
5051

5152
@override
52-
Future<List<CodeActionWithPriority>> getFixActions() async {
53+
Future<List<CodeActionWithPriority>> getFixActions(
54+
OperationPerformance? performance,
55+
) async {
5356
// These fixes are only provided as literal CodeActions.
5457
if (!supportsLiterals) {
5558
return [];

pkg/analysis_server/lib/src/lsp/handlers/code_actions/pubspec.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generato
1010
import 'package:analyzer/source/file_source.dart';
1111
import 'package:analyzer/source/line_info.dart';
1212
import 'package:analyzer/src/pubspec/pubspec_validator.dart';
13+
import 'package:analyzer/src/util/performance/operation_performance.dart';
1314
import 'package:yaml/yaml.dart';
1415

1516
/// Produces [CodeAction]s from Pubspec fixes.
@@ -32,7 +33,9 @@ class PubspecCodeActionsProducer extends AbstractCodeActionsProducer {
3233
Future<List<CodeActionWithPriority>> getAssistActions() async => [];
3334

3435
@override
35-
Future<List<CodeActionWithPriority>> getFixActions() async {
36+
Future<List<CodeActionWithPriority>> getFixActions(
37+
OperationPerformance? performance,
38+
) async {
3639
var session = await server.getAnalysisSession(path);
3740
if (session == null) {
3841
return [];

pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class CodeActionHandler
237237
for (var computer in actionComputers)
238238
...await performance.runAsync(
239239
'${computer.name}.getFixActions',
240-
(_) => computer.getFixActions(),
240+
(_) => computer.getFixActions(message.performance),
241241
),
242242
]),
243243

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2025, 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 'package:analysis_server/src/server/performance.dart';
6+
7+
// TODO(pq): update to share w/ completion_performance
8+
/// Compute a string representing a get fixes request at the
9+
/// given source and location.
10+
///
11+
/// This string is useful for displaying to users in a diagnostic context.
12+
String _computeSourceSnippet(String contents, int offset) {
13+
if (offset < 0 || contents.length < offset) {
14+
return '???';
15+
}
16+
var start = offset;
17+
while (start > 0) {
18+
var ch = contents[start - 1];
19+
if (ch == '\r' || ch == '\n') {
20+
break;
21+
}
22+
--start;
23+
}
24+
var end = offset;
25+
while (end < contents.length) {
26+
var ch = contents[end];
27+
if (ch == '\r' || ch == '\n') {
28+
break;
29+
}
30+
++end;
31+
}
32+
var prefix = contents.substring(start, offset);
33+
var suffix = contents.substring(offset, end);
34+
return '$prefix^$suffix';
35+
}
36+
37+
/// Overall performance of a request for quick fixes operation.
38+
class GetFixesPerformance extends RequestPerformance {
39+
final String path;
40+
final String snippet;
41+
42+
GetFixesPerformance({
43+
required super.performance,
44+
required this.path,
45+
super.requestLatency,
46+
required String content,
47+
required int offset,
48+
}) : snippet = _computeSourceSnippet(content, offset),
49+
super(operation: 'GetFixes');
50+
51+
int get elapsedInMilliseconds => performance.elapsed.inMilliseconds;
52+
}

pkg/analysis_server/lib/src/status/diagnostics.dart

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:analysis_server/src/plugin/plugin_manager.dart';
1818
import 'package:analysis_server/src/server/http_server.dart';
1919
import 'package:analysis_server/src/server/performance.dart';
2020
import 'package:analysis_server/src/services/completion/completion_performance.dart';
21+
import 'package:analysis_server/src/services/correction/fix_performance.dart';
2122
import 'package:analysis_server/src/socket_server.dart';
2223
import 'package:analysis_server/src/status/ast_writer.dart';
2324
import 'package:analysis_server/src/status/element_writer.dart';
@@ -597,6 +598,10 @@ class CollectReportPage extends DiagnosticPage {
597598
server.recentPerformance.completion.items.toList(),
598599
'Completion',
599600
);
601+
collectPerformance(
602+
server.recentPerformance.getFixes.items.toList(),
603+
'GetFixes',
604+
);
600605
collectPerformance(
601606
server.recentPerformance.requests.items.toList(),
602607
'Requests',
@@ -1312,6 +1317,7 @@ class DiagnosticsSite extends Site implements AbstractHttpHandler {
13121317
pages.add(PluginsPage(this, server));
13131318
}
13141319
pages.add(CompletionPage(this));
1320+
pages.add(GetFixesPage(this));
13151321
if (server is LegacyAnalysisServer) {
13161322
pages.add(SubscriptionsPage(this, server));
13171323
} else if (server is LspAnalysisServer) {
@@ -1532,6 +1538,71 @@ class FeedbackPage extends DiagnosticPage {
15321538
}
15331539
}
15341540

1541+
class GetFixesPage extends DiagnosticPageWithNav with PerformanceChartMixin {
1542+
GetFixesPage(DiagnosticsSite site)
1543+
: super(
1544+
site,
1545+
'getFixes',
1546+
'Get Fixes',
1547+
description: 'Latency statistics for getting fixes.',
1548+
);
1549+
1550+
path.Context get pathContext => server.resourceProvider.pathContext;
1551+
1552+
List<GetFixesPerformance> get performanceItems =>
1553+
server.recentPerformance.getFixes.items.toList();
1554+
1555+
@override
1556+
Future<void> generateContent(Map<String, String> params) async {
1557+
var requests = performanceItems;
1558+
1559+
if (requests.isEmpty) {
1560+
blankslate('No fix requests recorded.');
1561+
return;
1562+
}
1563+
1564+
var fastCount =
1565+
requests.where((c) => c.elapsedInMilliseconds <= 100).length;
1566+
p(
1567+
'${requests.length} results; ${printPercentage(fastCount / requests.length)} within 100ms.',
1568+
);
1569+
1570+
drawChart(requests);
1571+
1572+
// Emit the data as a table
1573+
buf.writeln('<table>');
1574+
buf.writeln('<tr><th>Time</th><th>Source</th><th>Snippet</th></tr>');
1575+
for (var request in requests) {
1576+
var shortName = pathContext.basename(request.path);
1577+
buf.writeln(
1578+
'<tr>'
1579+
'<td class="pre right">'
1580+
'${_formatTiming(request)}'
1581+
'</td>'
1582+
'<td>${escape(shortName)}</td>'
1583+
'<td><code>${escape(request.snippet)}</code></td>'
1584+
'</tr>',
1585+
);
1586+
}
1587+
buf.writeln('</table>');
1588+
}
1589+
1590+
String _formatTiming(GetFixesPerformance request) {
1591+
var buffer = StringBuffer();
1592+
buffer.write(printMilliseconds(request.elapsedInMilliseconds));
1593+
1594+
var latency = request.requestLatency;
1595+
if (latency != null) {
1596+
buffer
1597+
..write(' <small class="subtle" title="client-to-server latency">(+ ')
1598+
..write(printMilliseconds(latency))
1599+
..write(')</small>');
1600+
}
1601+
1602+
return buffer.toString();
1603+
}
1604+
}
1605+
15351606
class LspCapabilitiesPage extends DiagnosticPageWithNav {
15361607
@override
15371608
LspAnalysisServer server;

0 commit comments

Comments
 (0)