blob: 3879d8894da8c20f766b8a02d3a898019c718e31 [file] [log] [blame]
// Copyright (c) 2021, 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:async';
import 'dart:developer';
import 'dart:io';
import 'package:front_end/src/api_unstable/util.dart';
import "vm_service_heap_helper.dart" as helper;
Future<void> main(List<String> args) async {
if (args.contains("--leak")) {
return doLeak();
}
List<helper.Interest> interests = <helper.Interest>[];
interests.add(
new helper.Interest(
Platform.script,
"LeakMe",
["unique"],
),
);
interests.add(
new helper.Interest(
Platform.script,
"LeakMe2",
["uniquePart1", "uniquePart2"],
),
);
LeakFinderTest heapHelper = new LeakFinderTest(
interests: interests,
prettyPrints: [
new helper.Interest(
Platform.script,
"LeakMe",
["unique", "forPrettyPrinting"],
),
new helper.Interest(
Platform.script,
"LeakMe2",
["uniquePart1", "uniquePart2", "forPrettyPrinting"],
),
],
throwOnPossibleLeak: false,
);
await heapHelper.start(
[
"--enable-asserts",
Platform.script.toString(),
"--leak",
],
stderrReceiver: (s) {},
stdoutReceiver: (s) {},
);
List<String> expectedData = [
'1: no leak',
'2: 2: [LeakMe[unique: "a", forPrettyPrinting: "1"], '
'LeakMe[unique: "a", forPrettyPrinting: "3"]]',
'3: 2: [LeakMe[unique: "a", forPrettyPrinting: "1"], '
'LeakMe[unique: "a", forPrettyPrinting: "3"]]',
'3: 2: [LeakMe[unique: "b", forPrettyPrinting: "2"], '
'LeakMe[unique: "b", forPrettyPrinting: "4"]]',
'4: no leak',
'5: no leak',
'6: 2: ['
'LeakMe2[uniquePart1: "a", uniquePart2: "a", forPrettyPrinting: "1"], '
'LeakMe2[uniquePart1: "a", uniquePart2: "a", forPrettyPrinting: "4"]'
']',
];
List<String> leakData = await heapHelper.completer.future;
if (!equalLists(expectedData, leakData)) {
throw "Expected and actual not equal:\n\n"
"- ${expectedData.join("\n- ")}\n\n"
"vs\n\n"
"- ${leakData.join("\n- ")}";
}
print("Done!");
}
void doLeak() {
{
LeakMe a = new LeakMe("a", "1");
LeakMe b = new LeakMe("b", "2");
// Expect no leaks.
debugger();
LeakMe a2 = new LeakMe("a", "3");
// Expect one leak: We find leaks for class `LeakMe` based on the first
// field (`unique`). Now we have two objects with the same data ("a") for
// this field: `a` and `a2`. That's a leak as we've defined it.
debugger();
LeakMe b2 = new LeakMe("b", "4");
// Expect two leaks: We find leaks for class `LeakMe` based on the first
// field (`unique`). Now we have two objects with data "a" (`a` and `a2`)
// and two objects with data "b" (`b` and `b2`).
debugger();
print("$a, $b, $a2, $b2");
}
{
LeakMe2 a = new LeakMe2("a", "a", "1");
LeakMe2 b = new LeakMe2("b", "b", "2");
// Expect no leaks.
debugger();
LeakMe2 a2 = new LeakMe2("a", "foo", "3");
// Expect no leak.
debugger();
LeakMe2 a3 = new LeakMe2("a", "a", "4");
// Expect one leak: We find leaks for class `LeakMe2` based on the first
// field AND the second field (`uniquePart1` and `uniquePart2`). Now we have
// two objects with the same data in both fields ("a" and "a" for part 1 and
// part 2) namely the objects saved in variables `a` and `a3`.
// Notice how the object in variable `a2` did not introduce a leak even
// though part 1 match ("a") as part 2 doesn't ("a" vs "foo").
debugger();
print("$a, $b, $a2, $a3");
}
}
class LeakMe {
final String unique;
final String forPrettyPrinting;
LeakMe(this.unique, this.forPrettyPrinting);
}
class LeakMe2 {
final String uniquePart1;
final String uniquePart2;
final String forPrettyPrinting;
LeakMe2(this.uniquePart1, this.uniquePart2, this.forPrettyPrinting);
}
class LeakFinderTest extends helper.VMServiceHeapHelperSpecificExactLeakFinder {
List<String> leakData = [];
@override
int iterationNumber = -1;
Completer<List<String>> completer = new Completer<List<String>>();
LeakFinderTest({
required List<helper.Interest> interests,
required List<helper.Interest> prettyPrints,
required bool throwOnPossibleLeak,
}) : super(
interests: interests,
prettyPrints: prettyPrints,
throwOnPossibleLeak: throwOnPossibleLeak);
@override
void processExited(int exitCode) {
print("Process exited!");
leakData.sort();
completer.complete(leakData);
}
@override
void leakDetected(String duplicate, int count, List<String> prettyPrints) {
prettyPrints.sort();
leakData.add("$iterationNumber: $count: $prettyPrints");
}
@override
void noLeakDetected() {
leakData.add("$iterationNumber: no leak");
}
@override
bool shouldDoAnotherIteration(int iterationNumber) {
this.iterationNumber = iterationNumber;
return iterationNumber <= 6;
}
}