Index: tools/gardening/bin/find_timeouts.dart |
diff --git a/tools/gardening/bin/find_timeouts.dart b/tools/gardening/bin/find_timeouts.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e333955f75f1df6b6870312092b8bd372e6d0b11 |
--- /dev/null |
+++ b/tools/gardening/bin/find_timeouts.dart |
@@ -0,0 +1,139 @@ |
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+/// Scans past dart2js-windows test steps for timeouts and reports the |
+/// frequency of each test that has timed out. |
+ |
+import 'dart:async'; |
+import 'dart:io'; |
+import 'package:args/args.dart'; |
+import 'package:gardening/src/buildbot_data.dart'; |
+import 'package:gardening/src/buildbot_loading.dart'; |
+import 'package:gardening/src/buildbot_structures.dart'; |
+import 'package:gardening/src/logdog.dart' as logdog; |
+import 'package:gardening/src/util.dart'; |
+ |
+main(List<String> args) async { |
+ ArgParser argParser = createArgParser(); |
+ argParser.addFlag('logdog', |
+ negatable: false, help: "Pull test results from logdog."); |
+ argParser.addOption('start', |
+ defaultsTo: '-2', |
+ help: "Start pulling from the specified <build-number>.\n" |
+ "Use negative numbers for the most recent builds;\n" |
+ "for instance -2 for the second-to-last build.'"); |
+ ArgResults argResults = argParser.parse(args); |
+ processArgResults(argResults); |
+ List<String> arguments = argResults.rest; |
+ if (arguments.length > 1) { |
+ print('Usage: find_timeouts.dart [options] [<count>]'); |
+ print('Where <count> is the number of old builds that are scanned'); |
+ print('and options are:'); |
+ print(argParser.usage); |
+ exit(1); |
+ } |
+ int buildNumberCount = 10; |
+ if (arguments.length > 0) { |
+ buildNumberCount = int.parse(arguments[0]); |
+ } |
+ int buildNumberOffset = int.parse(argResults['start']); |
+ |
+ bool useLogDog = argResults['logdog']; |
+ |
+ HttpClient client = new HttpClient(); |
+ BuildGroup group = |
+ buildGroups.firstWhere((g) => g.groupName == 'dart2js-windows'); |
+ Map<String, List<Timeout>> timeouts = <String, List<Timeout>>{}; |
+ for (BuildSubgroup subgroup in group.subgroups) { |
+ if (useLogDog) { |
+ await readLogDogResults(subgroup, timeouts, |
+ buildNumberOffset: buildNumberOffset, |
+ buildNumberCount: buildNumberCount); |
+ } else { |
+ await readBuildBotResults(client, subgroup, timeouts, |
+ buildNumberOffset: buildNumberOffset, |
+ buildNumberCount: buildNumberCount); |
+ } |
+ } |
+ |
+ List<String> sorted = timeouts.keys.toList() |
+ ..sort((a, b) { |
+ return -timeouts[a].length.compareTo(timeouts[b].length); |
+ }); |
+ |
+ sorted.forEach((String testName) { |
+ List<Timeout> list = timeouts[testName]; |
+ print('${padLeft('${list.length}', 4)} $testName'); |
+ for (Timeout timeout in list) { |
+ print(' - ${timeout.buildUri.botName}/${timeout.buildUri.stepName} ' |
+ '${timeout.timeout.id} (${timeout.buildNumber})'); |
+ } |
+ }); |
+ |
+ client.close(); |
+} |
+ |
+Future readLogDogResults( |
+ BuildSubgroup subgroup, Map<String, List<Timeout>> timeouts, |
+ {int buildNumberOffset, int buildNumberCount}) async { |
+ Map<String, String> subgroupPaths = subgroup.logDogPaths; |
+ for (String shardName in subgroupPaths.keys) { |
+ String subgroupPath = subgroupPaths[shardName]; |
+ List<int> buildNumbers = <int>[]; |
+ for (String line in logdog.ls(subgroupPath).split('\n')) { |
+ line = line.trim(); |
+ if (line.isNotEmpty) { |
+ buildNumbers.add(int.parse(line)); |
+ } |
+ } |
+ buildNumbers.sort((a, b) => -a.compareTo(b)); |
+ int buildNumberIndex; |
+ if (buildNumberOffset < 0) { |
+ buildNumberIndex = -buildNumberOffset - 1; |
+ } else { |
+ buildNumberIndex = buildNumbers.firstWhere((n) => n <= buildNumberOffset, |
+ orElse: () => buildNumbers.length); |
+ } |
+ for (int i = 0; i < buildNumberCount; i++) { |
+ if (buildNumberIndex + i < buildNumbers.length) { |
+ int buildNumber = buildNumbers[buildNumberIndex + i]; |
+ for (BuildUri buildUri |
+ in subgroup.createShardUris(shardName, buildNumber)) { |
+ BuildResult result = await readLogDogResult(buildUri); |
+ for (TestFailure timeout in result.timeouts) { |
+ timeouts.putIfAbsent(timeout.id.testName, () => <Timeout>[]).add( |
+ new Timeout(subgroup, result.buildNumber, buildUri, timeout)); |
+ } |
+ buildUri = buildUri.prev(); |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
+Future readBuildBotResults(HttpClient client, BuildSubgroup subgroup, |
+ Map<String, List<Timeout>> timeouts, |
+ {int buildNumberOffset, int buildNumberCount}) async { |
+ List<BuildUri> buildUris = subgroup.createUris(buildNumberOffset); |
+ for (BuildUri buildUri in buildUris) { |
+ for (int i = 0; i < buildNumberCount; i++) { |
+ BuildResult result = await readBuildResult(client, buildUri); |
+ for (TestFailure timeout in result.timeouts) { |
+ timeouts |
+ .putIfAbsent(timeout.id.testName, () => <Timeout>[]) |
+ .add(new Timeout(subgroup, result.buildNumber, buildUri, timeout)); |
+ } |
+ buildUri = result.buildUri.prev(); |
+ } |
+ } |
+} |
+ |
+class Timeout { |
+ final BuildSubgroup subgroup; |
+ final int buildNumber; |
+ final BuildUri buildUri; |
+ final TestFailure timeout; |
+ |
+ Timeout(this.subgroup, this.buildNumber, this.buildUri, this.timeout); |
+} |