blob: 732b5f760c01bc7e1cdded1b22f80e82d5f49c31 [file] [edit]
// Copyright (c) 2020, 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:io';
import '../profile.dart';
import '../source_code.dart';
import 'formatter_options.dart';
/// The kind of summary shown after all formatting is complete.
final class Summary {
static const Summary none = Summary._();
/// Creates a Summary that tracks how many files were formatted and the total
/// time.
static Summary line() => _LineSummary();
/// Creates a Summary that captures profiling information.
///
/// Mostly for internal use.
static Summary profile() => _ProfileSummary();
const Summary._();
/// Called when [file] is about to be formatted.
///
/// If stdin is being formatted, then [file] is `null`.
void beforeFile(File? file, String displayPath) {}
/// Describe the processed file at [path] whose formatted result is [output].
///
/// If the contents of the file are the same as the formatted output,
/// [changed] will be false.
///
/// If stdin is being formatted, then [file] is `null`.
void afterFile(
FormatterOptions options,
File? file,
String displayPath,
SourceCode output, {
required bool changed,
}) {}
void show() {}
/// Adds worker telemetry data to the summary.
void addTelemetry(
({Duration readTime, Duration formatTime, Duration idleTime}) telemetry,
) {}
}
/// Tracks how many files were formatted and the total time.
final class _LineSummary extends Summary {
final DateTime _start = DateTime.now();
/// The number of processed files.
int _files = 0;
/// The number of changed files.
int _changed = 0;
_LineSummary() : super._();
/// Describe the processed file at [path] whose formatted result is [output].
///
/// If the contents of the file are the same as the formatted output,
/// [changed] will be false.
@override
void afterFile(
FormatterOptions options,
File? file,
String displayPath,
SourceCode output, {
required bool changed,
}) {
_files++;
if (changed) _changed++;
}
/// Show the times for the slowest files to format.
@override
void show() {
var elapsed = DateTime.now().difference(_start);
var time = (elapsed.inMilliseconds / 1000).toStringAsFixed(2);
if (_files == 0) {
print('Formatted no files in $time seconds.');
} else if (_files == 1) {
print('Formatted $_files file ($_changed changed) in $time seconds.');
} else {
print('Formatted $_files files ($_changed changed) in $time seconds.');
}
}
}
/// Reports how long it took for format each file.
final class _ProfileSummary implements Summary {
/// The files that have been started but have not completed yet.
///
/// Maps a file label to the time that it started being formatted.
final Map<String, DateTime> _ongoing = {};
/// The elapsed time it took to format each completed file.
final Map<String, Duration> _elapsed = {};
/// The number of files that completed so fast that they aren't worth
/// tracking.
int _elided = 0;
Duration _readTime = Duration.zero;
Duration _formatTime = Duration.zero;
Duration _idleTime = Duration.zero;
@override
void addTelemetry(
({Duration readTime, Duration formatTime, Duration idleTime}) telemetry,
) {
_readTime += telemetry.readTime;
_formatTime += telemetry.formatTime;
_idleTime += telemetry.idleTime;
}
/// Show the times for the slowest files to format.
@override
void show() {
// Everything should be done.
assert(_ongoing.isEmpty);
var files = _elapsed.keys.toList();
files.sort((a, b) => _elapsed[b]!.compareTo(_elapsed[a]!));
for (var file in files) {
print('${_elapsed[file]}: $file');
}
if (_elided >= 1) {
var s = _elided > 1 ? 's' : '';
print('...$_elided more file$s each took less than 10ms.');
}
Profile.report();
print('');
print('Worker Telemetry:');
String format(int n) {
var str = n.toString();
var reg = RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))');
return str.replaceAllMapped(reg, (Match m) => '${m[1]},');
}
var readStr = format(_readTime.inMilliseconds).padLeft(10);
var formatStr = format(_formatTime.inMilliseconds).padLeft(10);
var idleStr = format(_idleTime.inMilliseconds).padLeft(10);
var rssStr = format(ProcessInfo.currentRss ~/ 1024).padLeft(10);
print(' Waiting for reads: ${readStr}ms');
print(' Running formatter: ${formatStr}ms');
print(' Waiting for work: ${idleStr}ms');
print(' Memory usage (RSS): ${rssStr}KB');
}
/// Called when [file] is about to be formatted.
@override
void beforeFile(File? file, String displayPath) {
_ongoing[displayPath] = DateTime.now();
}
/// Describe the processed file at [path] whose formatted result is [output].
///
/// If the contents of the file are the same as the formatted output,
/// [changed] will be false.
@override
void afterFile(
FormatterOptions options,
File? file,
String displayPath,
SourceCode output, {
required bool changed,
}) {
var elapsed = DateTime.now().difference(_ongoing.remove(displayPath)!);
if (elapsed.inMilliseconds >= 10) {
_elapsed[displayPath] = elapsed;
} else {
_elided++;
}
}
}