| // Copyright (c) 2025, 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. | |
| import 'dart:convert'; | |
| import 'dart:io'; | |
| import 'language_server_benchmark.dart'; | |
| String formatDuration(Duration duration) { | |
| int seconds = duration.inSeconds; | |
| int ms = duration.inMicroseconds - seconds * Duration.microsecondsPerSecond; | |
| return '$seconds.${ms.toString().padLeft(6, '0')}'; | |
| } | |
| String formatKb(int kb) { | |
| if (kb > 1024) { | |
| return '${kb ~/ 1024} MB'; | |
| } else { | |
| return '$kb KB'; | |
| } | |
| } | |
| Future<void> runHelper<E, F, G>( | |
| List<String> args, | |
| DartLanguageServerBenchmark Function( | |
| List<String> args, | |
| Uri rootUri, | |
| Uri cacheFolder, | |
| E runDetails, | |
| ) | |
| benchmarkCreator, | |
| E Function( | |
| Uri packageDirUri, | |
| Uri outerDirForAdditionalData, | |
| int size, | |
| F? extraIterationData, | |
| List<String> args, { | |
| required G? extraInformation, | |
| }) | |
| createDataAndCreateRunDetails, { | |
| required bool runAsLsp, | |
| List<int> sizeOptions = const [16, 32, 64, 128, 256, 512, 1024], | |
| required List<F?> Function(List<String> args) extraIterations, | |
| G? extraInformation, | |
| }) async { | |
| int verbosity = 0; | |
| bool jsonOutput = false; | |
| for (String arg in args) { | |
| if (arg.startsWith('--sizes=')) { | |
| sizeOptions = arg | |
| .substring('--sizes='.length) | |
| .split(',') | |
| .map(int.parse) | |
| .toList(); | |
| } else if (arg.startsWith('--verbosity=')) { | |
| verbosity = int.parse(arg.substring('--verbosity='.length)); | |
| } else if (arg == '--json') { | |
| jsonOutput = true; | |
| } | |
| } | |
| if (jsonOutput) { | |
| verbosity = -1; | |
| } | |
| StringBuffer sb = StringBuffer(); | |
| Map<String, int> jsonData = {}; | |
| for (F? extraIteration in extraIterations(args)) { | |
| for (int size in sizeOptions) { | |
| var caption = 'size $size / $extraIteration'; | |
| if (extraIteration == null) { | |
| caption = 'size $size'; | |
| } | |
| try { | |
| Directory tmpDir = Directory.systemTemp.createTempSync( | |
| 'analysisServer_benchmark', | |
| ); | |
| try { | |
| Directory cacheDir = Directory.fromUri(tmpDir.uri.resolve('cache/')) | |
| ..createSync(recursive: true); | |
| Directory dartDir = Directory.fromUri(tmpDir.uri.resolve('dart/')) | |
| ..createSync(recursive: true); | |
| var runDetails = createDataAndCreateRunDetails( | |
| dartDir.uri, | |
| tmpDir.uri, | |
| size, | |
| extraIteration, | |
| args, | |
| extraInformation: extraInformation, | |
| ); | |
| var benchmark = benchmarkCreator( | |
| args, | |
| dartDir.uri, | |
| cacheDir.uri, | |
| runDetails, | |
| ); | |
| try { | |
| benchmark.verbosity = verbosity; | |
| await benchmark.run(); | |
| } finally { | |
| benchmark.exit(); | |
| } | |
| if (jsonOutput) { | |
| for (var durationInfo in benchmark.durationInfo) { | |
| var key = '$caption: ${durationInfo.name} (ms)'; | |
| if (jsonData.containsKey(key)) { | |
| throw 'Already contains data for $key'; | |
| } | |
| jsonData[key] = durationInfo.duration.inMilliseconds; | |
| } | |
| for (var memoryInfo in benchmark.memoryInfo) { | |
| jsonData['$caption: ${memoryInfo.name} (kb)'] = memoryInfo.kb; | |
| } | |
| } else { | |
| if (verbosity >= 0) print('===================='); | |
| if (verbosity >= 0) print('$caption:'); | |
| sb.writeln('$caption:'); | |
| for (var durationInfo in benchmark.durationInfo) { | |
| if (verbosity >= 0) { | |
| print( | |
| '${durationInfo.name}: ' | |
| '${formatDuration(durationInfo.duration)}', | |
| ); | |
| } | |
| sb.writeln( | |
| '${durationInfo.name}: ' | |
| '${formatDuration(durationInfo.duration)}', | |
| ); | |
| } | |
| for (var memoryInfo in benchmark.memoryInfo) { | |
| if (verbosity >= 0) { | |
| print( | |
| '${memoryInfo.name}: ' | |
| '${formatKb(memoryInfo.kb)}', | |
| ); | |
| } | |
| sb.writeln( | |
| '${memoryInfo.name}: ' | |
| '${formatKb(memoryInfo.kb)}', | |
| ); | |
| } | |
| if (verbosity >= 0) print('===================='); | |
| sb.writeln(); | |
| } | |
| } finally { | |
| try { | |
| tmpDir.deleteSync(recursive: true); | |
| } catch (e) { | |
| // Wait a little and retry. | |
| sleep(const Duration(milliseconds: 42)); | |
| try { | |
| tmpDir.deleteSync(recursive: true); | |
| } catch (e) { | |
| if (verbosity >= 0) print('Warning: $e'); | |
| } | |
| } | |
| } | |
| } catch (e) { | |
| stderr.writeln('Error while processing $caption: $e'); | |
| } | |
| } | |
| } | |
| if (jsonOutput) { | |
| print(json.encode(jsonData)); | |
| } else { | |
| print('=================================='); | |
| print(sb.toString().trim()); | |
| print('=================================='); | |
| } | |
| } |