Skip to content

Commit e5253f1

Browse files
jensjohaCommit Queue
authored andcommitted
[analyzer] Stable analysis tool
Tool the - in my testing at least - is very stable in terms of instruction counts used as reported by `perf stat`. Only requires small changes to the analyzer and doesn't affect the part that's covered by api.txt. Running it on an extracted dill (with an aot-compiled tool) I get numbers like this: ``` perf stat -e task-clock:u,cycles:u,instructions:u,branch-misses:u -r3 out/ReleaseX64/dart-sdk/bin/dartaotruntime --deterministic pkg/analyzer/tool/stable_analysis.aot /tmp/extracted_compile_dart_compile ``` ``` [...] 50,853,198,745 instructions:u # 1.18 insn per cycle ( +- 0.00% ) [...] ``` Running it 10 times (without `-r3`) I get this: ``` 50,853,237,614 instructions:u # 1.18 insn per cycle 50,853,477,778 instructions:u # 1.19 insn per cycle 50,852,534,741 instructions:u # 1.17 insn per cycle 50,852,728,437 instructions:u # 1.18 insn per cycle 50,853,298,284 instructions:u # 1.18 insn per cycle 50,853,151,431 instructions:u # 1.17 insn per cycle 50,853,754,852 instructions:u # 1.18 insn per cycle 50,852,509,185 instructions:u # 1.17 insn per cycle 50,853,878,495 instructions:u # 1.18 insn per cycle 50,853,437,912 instructions:u # 1.18 insn per cycle ``` where the difference between the smallest and the largest number is <0.0027% Change-Id: I84d616201520a50904854cac06958faf6330468b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/449760 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent 3b7cd31 commit e5253f1

File tree

2 files changed

+113
-3
lines changed

2 files changed

+113
-3
lines changed

pkg/analyzer/lib/src/dart/analysis/driver.dart

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,15 @@ class AnalysisDriver {
589589

590590
void afterPerformWork() {}
591591

592+
@visibleForTesting
593+
ResolvedLibraryResultImpl? analyzeFileForTesting(String path) {
594+
return scheduler.accumulatedPerformance.run('analyzeFileForTesting', (
595+
performance,
596+
) {
597+
return _analyzeFileImpl(path: path, performance: performance);
598+
});
599+
}
600+
592601
/// Return a [Future] that completes after pending file changes are applied,
593602
/// so that [currentSession] can be used to compute results.
594603
///
@@ -1307,7 +1316,7 @@ class AnalysisDriver {
13071316
});
13081317
}
13091318

1310-
void _analyzeFileImpl({
1319+
ResolvedLibraryResultImpl? _analyzeFileImpl({
13111320
required String path,
13121321
required OperationPerformanceImpl performance,
13131322
}) {
@@ -1331,7 +1340,7 @@ class AnalysisDriver {
13311340
);
13321341

13331342
if (_completeRequestsIfMissingSdkLibrary(library)) {
1334-
return;
1343+
return null;
13351344
}
13361345

13371346
performance.run('libraryContext', (performance) {
@@ -1491,7 +1500,7 @@ class AnalysisDriver {
14911500

14921501
// Return the result, full or partial.
14931502
_logger.writeln('Computed new analysis result.');
1494-
// return result;
1503+
return libraryResult;
14951504
} catch (exception, stackTrace) {
14961505
var contextKey = _storeExceptionContext(
14971506
path,
@@ -1522,6 +1531,8 @@ class AnalysisDriver {
15221531
completeWithError(_requestedLibraries.remove(library.file.path));
15231532
_clearLibraryContextAfterException();
15241533
}
1534+
1535+
return null;
15251536
});
15261537
}
15271538

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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 'dart:io';
6+
7+
import 'package:analyzer/error/error.dart';
8+
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
9+
import 'package:analyzer/src/dart/analysis/driver.dart';
10+
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
11+
import 'package:analyzer/src/dart/analysis/file_state.dart';
12+
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
13+
import 'package:linter/src/rules.dart' as linter;
14+
15+
void main(List<String> args) {
16+
String? dirPath;
17+
bool registerLints = true;
18+
bool dumpPerformanceNumbers = false;
19+
20+
for (String arg in args) {
21+
if (arg == "--no-lints") {
22+
registerLints = false;
23+
} else if (arg == "--dump-performance") {
24+
dumpPerformanceNumbers = true;
25+
} else if (!arg.startsWith("--") && dirPath == null) {
26+
dirPath = arg;
27+
} else {
28+
throw "Unknown argument: $arg";
29+
}
30+
}
31+
32+
if (registerLints) {
33+
linter.registerLintRules();
34+
}
35+
if (dirPath == null) throw "Needs a directory to work on.";
36+
Directory dir = Directory(dirPath);
37+
List<String> filesToAnalyze = [];
38+
for (FileSystemEntity entity in dir.listSync(
39+
recursive: true,
40+
followLinks: false,
41+
)) {
42+
if (entity is File && entity.path.endsWith('.dart')) {
43+
filesToAnalyze.add(entity.path);
44+
}
45+
}
46+
47+
// Creating the Scheduler here, and not starting it, means it's not going to
48+
// get started. It's good for stability :).
49+
// To make sure the 'start' method is overwritten below.
50+
PerformanceLog performanceLog = PerformanceLog(null);
51+
AnalysisDriverScheduler scheduler = BypassedAnalysisDriverScheduler(
52+
performanceLog,
53+
);
54+
AnalysisContextCollectionImpl collection = AnalysisContextCollectionImpl(
55+
includedPaths: [dir.path],
56+
scheduler: scheduler,
57+
);
58+
DriverBasedAnalysisContext context = collection.contexts[0];
59+
AnalysisDriver analysisDriver = context.driver;
60+
List<String> libPaths = [];
61+
analysisDriver.scheduler.accumulatedPerformance.run('getFileForPath', (_) {
62+
for (var path in filesToAnalyze) {
63+
var file = analysisDriver.fsState.getFileForPath(path);
64+
var kind = file.kind;
65+
if (kind is LibraryFileKind) {
66+
libPaths.add(path);
67+
}
68+
}
69+
});
70+
for (var path in libPaths) {
71+
// ignore: invalid_use_of_visible_for_testing_member
72+
var library = analysisDriver.analyzeFileForTesting(path);
73+
if (library == null) {
74+
throw 'Got null for $path';
75+
} else {
76+
for (var unit in library.units) {
77+
for (var diagnostic in unit.diagnostics) {
78+
if (diagnostic.diagnosticCode.type == DiagnosticType.TODO) continue;
79+
print(diagnostic);
80+
}
81+
}
82+
}
83+
}
84+
85+
if (dumpPerformanceNumbers) {
86+
var buffer = StringBuffer();
87+
analysisDriver.scheduler.accumulatedPerformance.write(buffer: buffer);
88+
print(buffer);
89+
}
90+
}
91+
92+
class BypassedAnalysisDriverScheduler extends AnalysisDriverScheduler {
93+
BypassedAnalysisDriverScheduler(super.logger);
94+
95+
@override
96+
void start() {
97+
throw 'This scheduler should not be started.';
98+
}
99+
}

0 commit comments

Comments
 (0)