blob: 515246a1150ab37f8c9cb3d734bd97043e55f878 [file] [log] [blame]
// Copyright (c) 2023, 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 'package:intl/intl.dart';
import 'package:path/path.dart' as path;
/// A record of a taken snapshot.
class SnapshotEvent {
SnapshotEvent(
this.fileName, {
required this.snapshotNumber,
required this.rss,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
final String fileName;
final int snapshotNumber;
final int rss;
final DateTime timestamp;
}
/// A callback that is called when a snapshot is taken.
typedef SnapshotCallback = void Function(SnapshotEvent event);
/// A record of a memory usage event.
class MemoryUsageEvent {
MemoryUsageEvent({
required this.delta,
required DateTime? previousEventTime,
required this.rss,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now() {
if (previousEventTime == null) {
period = null;
} else {
period = this.timestamp.difference(previousEventTime);
}
}
/// Difference with previouse rss value.
///
/// Equals to [null] for first event.
final int? delta;
/// Time since previous event.
///
/// Equals to [null] for first event.
late Duration? period;
/// RSS memory usage.
final int rss;
///
final DateTime timestamp;
}
/// A callback that is called for memory usage event.
typedef UsageCallback = void Function(MemoryUsageEvent event);
/// Configures memory usage tracking.
///
/// Set [interval] to customize how often to verify memory usage.
class UsageTrackingConfig {
const UsageTrackingConfig({
this.usageEventsConfig,
this.autoSnapshottingConfig,
this.interval = const Duration(seconds: 1),
});
/// Configuration for snapshotting.
final AutoSnapshottingConfig? autoSnapshottingConfig;
/// Configuration for usage events.
final UsageEventsConfig? usageEventsConfig;
/// How often to verify memory usage.
final Duration interval;
bool get isNoOp =>
autoSnapshottingConfig == null && usageEventsConfig == null;
@override
String toString() {
return 'interval: $interval\n'
'$usageEventsConfig\n'
'$autoSnapshottingConfig';
}
}
/// Configures memory usage tracking.
///
/// [onUsageEvent] will be triggered when rss value changes
/// more then by [deltaMb] since previous [onUsageEvent].
/// First [onUsageEvent] will be triggered immediately.
class UsageEventsConfig {
const UsageEventsConfig(
this.onUsageEvent, {
this.deltaMb = 128,
});
/// A callback that is called when a snapshot is taken.
final UsageCallback onUsageEvent;
/// Change in memory usage to trigger [onUsageEvent].
final int deltaMb;
@override
String toString() {
final formatter = NumberFormat('#,###,000');
return 'usageEvent.deltaMb: ${formatter.format(deltaMb)}';
}
}
/// Configures auto-snapshotting, based on the value of `ProcessInfo.currentRss` (dart:io).
///
/// Automatic snapshots will begin to be taken when the rss value exceeds [thresholdMb].
/// The snapshots will be saved to [directory].
///
/// The snapshots will be re-taken when the value
/// increases more than by [increaseMb] since previous snapshot,
/// until the size of [directory] exceeds
/// [directorySizeLimitMb].
///
/// The [onSnapshot] callback is called when a snapshot is taken.
///
/// Set [minDelayBetweenSnapshots] to make sure snapshots do not trigger each other.
class AutoSnapshottingConfig {
AutoSnapshottingConfig({
this.thresholdMb = 1024, // 1Gb
this.increaseMb = 512, // 0.5Gb
this.directory = 'dart_memory_snapshots',
this.directorySizeLimitMb = 10240, // 10Gb
this.minDelayBetweenSnapshots = const Duration(seconds: 10),
this.onSnapshot,
}) {
if (minDelayBetweenSnapshots <= Duration.zero) {
throw ArgumentError.value(
minDelayBetweenSnapshots,
'minDelayBetweenSnapshots',
'must be positive',
);
}
}
/// The rss value in Mb that will trigger the first snapshot.
final int thresholdMb;
/// The value by which the rss value should increase, since
/// previous snapshot, to take another snapshot.
///
/// If [increaseMb] is null, only one snapshot will be taken.
final int? increaseMb;
/// The directory where snapshots will be saved.
///
/// If the directory does not exist, it will be created.
///
/// If the path is relative, it will be relative to the current working directory.
final String directory;
/// The size limit for the [directory] in Mb.
///
/// The directory size will be checked before a snapshot is taken and saved,
/// so the directory may exceed the size specified by [directorySizeLimitMb]
/// depending on the size of the snapshot.
final int directorySizeLimitMb;
/// How long to wait after taking a snapshot before taking another one.
final Duration minDelayBetweenSnapshots;
/// A callback that is called when a snapshot is taken.
final SnapshotCallback? onSnapshot;
/// The absolute path to the [directory].
String get directoryAbsolute => path.absolute(directory);
@override
String toString() {
final formatter = NumberFormat('#,###,000');
return 'thresholdMb: ${formatter.format(thresholdMb)}\n'
'autosnapshot.increaseMb: ${increaseMb == null ? 'null' : formatter.format(increaseMb)}\n'
'directorySizeLimitMb: ${formatter.format(directorySizeLimitMb)}\n'
'directory: $directory\n'
'directoryAbsolute: $directoryAbsolute\n'
'minDelayBetweenSnapshots: $minDelayBetweenSnapshots';
}
}