[vm] Report a coverage tick for the function as a whole.

This ensures leaf functions have some coverage data.

Bug: https://github.com/dart-lang/sdk/issues/31215
Change-Id: I6efc2e1c002e26cd0483dce5a8a1c2b6d8958d13
Reviewed-on: https://dart-review.googlesource.com/16684
Reviewed-by: Zach Anderson <zra@google.com>
This commit is contained in:
Ryan Macnak 2017-10-26 16:50:30 +00:00
parent d21a98a104
commit 3d409223a3
4 changed files with 153 additions and 32 deletions

View file

@ -0,0 +1,115 @@
// 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.
// VMOptions=--error_on_bad_type --error_on_bad_override
import 'package:observatory/service_io.dart';
import 'package:unittest/unittest.dart';
import 'test_helper.dart';
import 'service_test_common.dart';
import 'dart:developer';
String leafFunction() {
return "some constant";
}
void testFunction() {
debugger();
leafFunction();
debugger();
}
bool allRangesCompiled(coverage) {
for (int i = 0; i < coverage['ranges'].length; i++) {
if (!coverage['ranges'][i]['compiled']) {
return false;
}
}
return true;
}
var tests = [
hasStoppedAtBreakpoint,
(Isolate isolate) async {
var stack = await isolate.getStack();
// Make sure we are in the right place.
expect(stack.type, equals('Stack'));
expect(stack['frames'].length, greaterThanOrEqualTo(1));
expect(stack['frames'][0].function.name, equals('testFunction'));
var root = isolate.rootLibrary;
await root.load();
var func = root.functions.singleWhere((f) => f.name == 'leafFunction');
await func.load();
var expectedRange = {
'scriptIndex': 0,
'startPos': ifKernel(456, 26),
'endPos': ifKernel(499, 37),
'compiled': true,
'coverage': {
'hits': ifKernel([], []),
'misses': ifKernel([456], [26])
}
};
var params = {
'reports': ['Coverage'],
'scriptId': func.location.script.id,
'tokenPos': func.location.tokenPos,
'endTokenPos': func.location.endTokenPos,
'forceCompile': true
};
var report = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
expect(report['type'], equals('SourceReport'));
expect(report['ranges'].length, 1);
expect(report['ranges'][0], equals(expectedRange));
expect(report['scripts'].length, 1);
expect(report['scripts'][0]['uri'],
endsWith('coverage_leaf_function_test.dart'));
},
resumeIsolate,
hasStoppedAtBreakpoint,
(Isolate isolate) async {
var stack = await isolate.getStack();
// Make sure we are in the right place.
expect(stack.type, equals('Stack'));
expect(stack['frames'].length, greaterThanOrEqualTo(1));
expect(stack['frames'][0].function.name, equals('testFunction'));
var root = isolate.rootLibrary;
await root.load();
var func = root.functions.singleWhere((f) => f.name == 'leafFunction');
await func.load();
var expectedRange = {
'scriptIndex': 0,
'startPos': ifKernel(456, 26),
'endPos': ifKernel(499, 37),
'compiled': true,
'coverage': {
'hits': ifKernel([456], [26]),
'misses': ifKernel([], [])
}
};
var params = {
'reports': ['Coverage'],
'scriptId': func.location.script.id,
'tokenPos': func.location.tokenPos,
'endTokenPos': func.location.endTokenPos,
'forceCompile': true
};
var report = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
expect(report['type'], equals('SourceReport'));
expect(report['ranges'].length, 1);
expect(report['ranges'][0], equals(expectedRange));
expect(report['scripts'].length, 1);
expect(report['scripts'][0]['uri'],
endsWith('coverage_leaf_function_test.dart'));
},
];
main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);

View file

@ -65,7 +65,7 @@ var tests = [
'endPos': ifKernel(633, 88),
'compiled': true,
'coverage': {
'hits': ifKernel([539, 590, 619], [54, 72, 82]),
'hits': ifKernel([501, 539, 590, 619], [39, 54, 72, 82]),
'misses': ifKernel([552], [60])
}
};

View file

@ -211,6 +211,12 @@ void SourceReport::PrintCoverageData(JSONObject* jsobj,
coverage[i] = kCoverageNone;
}
if (function.usage_counter() > 0) {
coverage[0] = kCoverageHit;
} else {
coverage[0] = kCoverageMiss;
}
PcDescriptors::Iterator iter(
descriptors,
RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall);

View file

@ -40,9 +40,9 @@ TEST_CASE(SourceReport_Coverage_NoCalls) {
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":"
// One compiled range, no hits or misses.
// One compiled range, one hit at function declaration.
"[{\"scriptIndex\":0,\"startPos\":0,\"endPos\":5,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}}],"
"\"coverage\":{\"hits\":[0],\"misses\":[]}}],"
// One script in the script table.
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
@ -77,16 +77,16 @@ TEST_CASE(SourceReport_Coverage_SimpleCall) {
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
// One range compiled with no hits or misses (helper0).
// One range compiled with one hit at function declaration (helper0).
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
// One range not compiled (helper1).
"{\"scriptIndex\":0,\"startPos\":6,\"endPos\":10,\"compiled\":false},"
// One range with a hit and a miss (main).
// One range with two hits and a miss (main).
"{\"scriptIndex\":0,\"startPos\":12,\"endPos\":39,\"compiled\":true,"
"\"coverage\":{\"hits\":[23],\"misses\":[32]}}],"
"\"coverage\":{\"hits\":[12,23],\"misses\":[32]}}],"
// Only one script in the script table.
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
@ -121,17 +121,17 @@ TEST_CASE(SourceReport_Coverage_ForceCompile) {
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
// One range compiled with no hits or misses (helper0).
// One range compiled with one hit at function declaration (helper0).
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
// This range is compiled even though it wasn't called (helper1).
"{\"scriptIndex\":0,\"startPos\":6,\"endPos\":10,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[],\"misses\":[6]}},"
// One range with a hit and a miss (main).
// One range with two hits and a miss (main).
"{\"scriptIndex\":0,\"startPos\":12,\"endPos\":39,\"compiled\":true,"
"\"coverage\":{\"hits\":[23],\"misses\":[32]}}],"
"\"coverage\":{\"hits\":[12,23],\"misses\":[32]}}],"
// Only one script in the script table.
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
@ -169,11 +169,11 @@ TEST_CASE(SourceReport_Coverage_UnusedClass_NoForceCompile) {
// helper0 is compiled.
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
// One range with a hit (main).
// One range with two hits (main).
"{\"scriptIndex\":0,\"startPos\":22,\"endPos\":32,\"compiled\":true,"
"\"coverage\":{\"hits\":[27],\"misses\":[]}}],"
"\"coverage\":{\"hits\":[22,27],\"misses\":[]}}],"
// Only one script in the script table.
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
@ -208,15 +208,15 @@ TEST_CASE(SourceReport_Coverage_UnusedClass_ForceCompile) {
// UnusedClass.helper1 is compiled.
"{\"scriptIndex\":0,\"startPos\":10,\"endPos\":18,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[14]}},"
"\"coverage\":{\"hits\":[],\"misses\":[10,14]}},"
// helper0 is compiled.
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
// One range with a hit (main).
// One range with two hits (main).
"{\"scriptIndex\":0,\"startPos\":22,\"endPos\":32,\"compiled\":true,"
"\"coverage\":{\"hits\":[27],\"misses\":[]}}],"
"\"coverage\":{\"hits\":[22,27],\"misses\":[]}}],"
// Only one script in the script table.
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
@ -258,11 +258,11 @@ TEST_CASE(SourceReport_Coverage_UnusedClass_ForceCompileError) {
// helper0 is compiled.
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
// One range with a hit (main).
// One range with two hits (main).
"{\"scriptIndex\":0,\"startPos\":22,\"endPos\":32,\"compiled\":true,"
"\"coverage\":{\"hits\":[27],\"misses\":[]}}],"
"\"coverage\":{\"hits\":[22,27],\"misses\":[]}}],"
// Only one script in the script table.
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
@ -303,18 +303,18 @@ TEST_CASE(SourceReport_Coverage_NestedFunctions) {
// One range compiled with one hit (helper0).
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":22,\"compiled\":true,"
"\"coverage\":{\"hits\":[18],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0,18],\"misses\":[]}},"
// One range not compiled (helper1).
"{\"scriptIndex\":0,\"startPos\":24,\"endPos\":28,\"compiled\":false},"
// One range with a hit and a miss (main).
// One range with two hits and a miss (main).
"{\"scriptIndex\":0,\"startPos\":30,\"endPos\":57,\"compiled\":true,"
"\"coverage\":{\"hits\":[41],\"misses\":[50]}},"
"\"coverage\":{\"hits\":[30,41],\"misses\":[50]}},"
// Nested range compiled (nestedHelper0).
"{\"scriptIndex\":0,\"startPos\":5,\"endPos\":9,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[5],\"misses\":[]}},"
// Nested range not compiled (nestedHelper1).
"{\"scriptIndex\":0,\"startPos\":11,\"endPos\":15,\"compiled\":false}],"
@ -361,11 +361,11 @@ TEST_CASE(SourceReport_Coverage_RestrictedRange) {
// One range compiled with one hit (helper0).
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":22,\"compiled\":true,"
"\"coverage\":{\"hits\":[18],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0,18],\"misses\":[]}},"
// Nested range compiled (nestedHelper0).
"{\"scriptIndex\":0,\"startPos\":5,\"endPos\":9,\"compiled\":true,"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[5],\"misses\":[]}},"
// Nested range not compiled (nestedHelper1).
"{\"scriptIndex\":0,\"startPos\":11,\"endPos\":15,\"compiled\":false}],"
@ -406,7 +406,7 @@ TEST_CASE(SourceReport_Coverage_AllFunctions) {
// Make sure that the main function was found.
EXPECT_SUBSTRING(
"\"startPos\":12,\"endPos\":39,\"compiled\":true,"
"\"coverage\":{\"hits\":[23],\"misses\":[32]}",
"\"coverage\":{\"hits\":[12,23],\"misses\":[32]}",
result);
// More than one script is referenced in the report.
@ -448,7 +448,7 @@ TEST_CASE(SourceReport_Coverage_AllFunctions_ForceCompile) {
// Make sure that the main function was found.
EXPECT_SUBSTRING(
"\"startPos\":12,\"endPos\":39,\"compiled\":true,"
"\"coverage\":{\"hits\":[23],\"misses\":[32]}",
"\"coverage\":{\"hits\":[12,23],\"misses\":[32]}",
result);
// More than one script is referenced in the report.
@ -603,7 +603,7 @@ TEST_CASE(SourceReport_MultipleReports) {
// One range compiled with no callsites (helper0).
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true,"
"\"callSites\":[],"
"\"coverage\":{\"hits\":[],\"misses\":[]}},"
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
// One range not compiled (helper1).
"{\"scriptIndex\":0,\"startPos\":6,\"endPos\":10,\"compiled\":false},"
@ -617,7 +617,7 @@ TEST_CASE(SourceReport_MultipleReports) {
"\"id\":\"\",\"name\":\"\",\"uri\":\"test-lib\"},"
"\"_kind\":\"RegularFunction\",\"static\":true,\"const\":false,"
"\"_intrinsic\":false,\"_native\":false},\"count\":1}]}],"
"\"coverage\":{\"hits\":[17],\"misses\":[]}}],"
"\"coverage\":{\"hits\":[12,17],\"misses\":[]}}],"
// One script in the script table.
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","