v8/test/unittests/heap/cppgc/stats-collector-scopes-unittest.cc
Omer Katz adda4c5f98 cppgc: Add UMA support
This CL introduces cppgc::HistogramRecorder api which is similar to the
v8::metrics::Recorder api and is used by cppgc to report histogram
samples to embedders. Embedders should implement the api if they want to
collect histograms and provide an instance of it on heap creation.

CppHeap uses an adaptor class that implements the HistogramRecorder api
and is used to forward the relevant info to the relevant
v8::metrics::Recorder.

The api used 3 data structures: 2 for incremental steps that need to be
reported as they come (marking and sweeping) and 1 for the end of a GC
cycle that aggregates statistics over the entire cycle.
The data structure only provide the "raw" samples (e.g. atomic mark
time, incremental mark time, etc...). The embedder is expected to
compute aggregate histogram on its own (e.g. overall marking time).

Bug: chromium:1056170
Change-Id: If63ef50a29a21594f654edb83084598980d221ce
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2642258
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72256}
2021-01-22 15:04:35 +00:00

308 lines
11 KiB
C++

// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if CPPGC_IS_STANDALONE
#include "src/heap/cppgc/stats-collector.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
namespace {
class DelegatingTracingControllerImpl : public TracingController {
public:
virtual uint64_t AddTraceEvent(
char phase, const uint8_t* category_enabled_flag, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
const char** arg_names, const uint8_t* arg_types,
const uint64_t* arg_values,
std::unique_ptr<ConvertableToTraceFormat>* arg_convertables,
unsigned int flags) {
if (!check_expectations) return 0;
static char phases[2] = {'B', 'E'};
EXPECT_EQ(phases[AddTraceEvent_callcount], phase);
EXPECT_TRUE(*category_enabled_flag);
if (expected_name) {
EXPECT_EQ(0, strcmp(expected_name, name));
}
stored_num_args += num_args;
for (int i = 0; i < num_args; ++i) {
stored_arg_names.push_back(arg_names[i]);
stored_arg_types.push_back(arg_types[i]);
stored_arg_values.push_back(arg_values[i]);
}
AddTraceEvent_callcount++;
return 0;
}
static bool check_expectations;
static size_t AddTraceEvent_callcount;
static const char* expected_name;
static int32_t stored_num_args;
static std::vector<std::string> stored_arg_names;
static std::vector<uint8_t> stored_arg_types;
static std::vector<uint64_t> stored_arg_values;
};
bool DelegatingTracingControllerImpl::check_expectations = false;
size_t DelegatingTracingControllerImpl::AddTraceEvent_callcount = 0u;
const char* DelegatingTracingControllerImpl::expected_name = nullptr;
int32_t DelegatingTracingControllerImpl::stored_num_args = 0;
std::vector<std::string> DelegatingTracingControllerImpl::stored_arg_names;
std::vector<uint8_t> DelegatingTracingControllerImpl::stored_arg_types;
std::vector<uint64_t> DelegatingTracingControllerImpl::stored_arg_values;
class V8_NODISCARD CppgcTracingScopesTest : public testing::TestWithHeap {
using Config = Marker::MarkingConfig;
public:
CppgcTracingScopesTest() {
SetTracingController(std::make_unique<DelegatingTracingControllerImpl>());
}
void StartGC() {
Config config = {Config::CollectionType::kMajor,
Config::StackState::kNoHeapPointers,
Config::MarkingType::kIncremental};
GetMarkerRef() = MarkerFactory::CreateAndStartMarking<Marker>(
Heap::From(GetHeap())->AsBase(), GetPlatformHandle().get(), config);
DelegatingTracingControllerImpl::check_expectations = true;
}
void EndGC() {
DelegatingTracingControllerImpl::check_expectations = false;
GetMarkerRef()->FinishMarking(Config::StackState::kNoHeapPointers);
GetMarkerRef().reset();
Heap::From(GetHeap())->stats_collector()->NotifySweepingCompleted();
}
void ResetDelegatingTracingController(const char* expected_name = nullptr) {
DelegatingTracingControllerImpl::AddTraceEvent_callcount = 0u;
DelegatingTracingControllerImpl::stored_num_args = 0;
DelegatingTracingControllerImpl::stored_arg_names.clear();
DelegatingTracingControllerImpl::stored_arg_types.clear();
DelegatingTracingControllerImpl::stored_arg_values.clear();
DelegatingTracingControllerImpl::expected_name = expected_name;
}
void FindArgument(std::string name, uint8_t type, uint64_t value) {
int i = 0;
for (; i < DelegatingTracingControllerImpl::stored_num_args; ++i) {
if (name.compare(DelegatingTracingControllerImpl::stored_arg_names[i]) ==
0)
break;
}
EXPECT_LT(i, DelegatingTracingControllerImpl::stored_num_args);
EXPECT_EQ(type, DelegatingTracingControllerImpl::stored_arg_types[i]);
EXPECT_EQ(value, DelegatingTracingControllerImpl::stored_arg_values[i]);
}
};
} // namespace
TEST_F(CppgcTracingScopesTest, DisabledScope) {
StartGC();
ResetDelegatingTracingController();
{
StatsCollector::DisabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist);
}
EXPECT_EQ(0u, DelegatingTracingControllerImpl::AddTraceEvent_callcount);
EndGC();
}
TEST_F(CppgcTracingScopesTest, EnabledScope) {
{
StartGC();
ResetDelegatingTracingController("CppGC.MarkProcessMarkingWorklist");
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist);
}
EXPECT_EQ(2u, DelegatingTracingControllerImpl::AddTraceEvent_callcount);
EndGC();
}
{
StartGC();
ResetDelegatingTracingController("CppGC.MarkProcessWriteBarrierWorklist");
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()),
StatsCollector::kMarkProcessWriteBarrierWorklist);
}
EXPECT_EQ(2u, DelegatingTracingControllerImpl::AddTraceEvent_callcount);
EndGC();
}
}
TEST_F(CppgcTracingScopesTest, EnabledScopeWithArgs) {
// Scopes always add 2 arguments: epoch and is_forced_gc.
{
StartGC();
ResetDelegatingTracingController();
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist);
}
EXPECT_EQ(2, DelegatingTracingControllerImpl::stored_num_args);
EndGC();
}
{
StartGC();
ResetDelegatingTracingController();
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist,
"arg1", 1);
}
EXPECT_EQ(3, DelegatingTracingControllerImpl::stored_num_args);
EndGC();
}
{
StartGC();
ResetDelegatingTracingController();
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist,
"arg1", 1, "arg2", 2);
}
EXPECT_EQ(4, DelegatingTracingControllerImpl::stored_num_args);
EndGC();
}
}
TEST_F(CppgcTracingScopesTest, CheckScopeArgs) {
{
StartGC();
ResetDelegatingTracingController();
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist,
"uint_arg", 13u, "bool_arg", false);
}
FindArgument("uint_arg", TRACE_VALUE_TYPE_UINT, 13);
FindArgument("bool_arg", TRACE_VALUE_TYPE_BOOL, false);
EndGC();
}
{
StartGC();
ResetDelegatingTracingController();
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist,
"neg_int_arg", -5, "pos_int_arg", 7);
}
FindArgument("neg_int_arg", TRACE_VALUE_TYPE_INT, -5);
FindArgument("pos_int_arg", TRACE_VALUE_TYPE_INT, 7);
EndGC();
}
{
StartGC();
ResetDelegatingTracingController();
double double_value = 1.2;
const char* string_value = "test";
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()), StatsCollector::kMarkProcessMarkingWorklist,
"string_arg", string_value, "double_arg", double_value);
}
FindArgument("string_arg", TRACE_VALUE_TYPE_STRING,
reinterpret_cast<uint64_t>(string_value));
FindArgument("double_arg", TRACE_VALUE_TYPE_DOUBLE,
*reinterpret_cast<uint64_t*>(&double_value));
EndGC();
}
}
TEST_F(CppgcTracingScopesTest, InitalScopesAreZero) {
StatsCollector* stats_collector = Heap::From(GetHeap())->stats_collector();
stats_collector->NotifyMarkingStarted(
GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
stats_collector->NotifyMarkingCompleted(0);
stats_collector->NotifySweepingCompleted();
const StatsCollector::Event& event =
stats_collector->GetPreviousEventForTesting();
for (int i = 0; i < StatsCollector::kNumHistogramScopeIds; ++i) {
EXPECT_TRUE(event.scope_data[i].IsZero());
}
for (int i = 0; i < StatsCollector::kNumHistogramConcurrentScopeIds; ++i) {
EXPECT_EQ(0, event.concurrent_scope_data[i]);
}
}
TEST_F(CppgcTracingScopesTest, TestIndividualScopes) {
for (int scope_id = 0; scope_id < StatsCollector::kNumHistogramScopeIds;
++scope_id) {
StatsCollector* stats_collector = Heap::From(GetHeap())->stats_collector();
stats_collector->NotifyMarkingStarted(
GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
DelegatingTracingControllerImpl::check_expectations = false;
{
StatsCollector::EnabledScope scope(
*Heap::From(GetHeap()),
static_cast<StatsCollector::ScopeId>(scope_id));
v8::base::TimeTicks time = v8::base::TimeTicks::Now();
while (time == v8::base::TimeTicks::Now()) {
// Force time to progress before destroying scope.
}
}
stats_collector->NotifyMarkingCompleted(0);
stats_collector->NotifySweepingCompleted();
const StatsCollector::Event& event =
stats_collector->GetPreviousEventForTesting();
for (int i = 0; i < StatsCollector::kNumHistogramScopeIds; ++i) {
if (i == scope_id)
EXPECT_LT(v8::base::TimeDelta(), event.scope_data[i]);
else
EXPECT_TRUE(event.scope_data[i].IsZero());
}
for (int i = 0; i < StatsCollector::kNumHistogramConcurrentScopeIds; ++i) {
EXPECT_EQ(0, event.concurrent_scope_data[i]);
}
}
}
TEST_F(CppgcTracingScopesTest, TestIndividualConcurrentScopes) {
for (int scope_id = 0;
scope_id < StatsCollector::kNumHistogramConcurrentScopeIds; ++scope_id) {
StatsCollector* stats_collector = Heap::From(GetHeap())->stats_collector();
stats_collector->NotifyMarkingStarted(
GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
DelegatingTracingControllerImpl::check_expectations = false;
{
StatsCollector::EnabledConcurrentScope scope(
*Heap::From(GetHeap()),
static_cast<StatsCollector::ConcurrentScopeId>(scope_id));
v8::base::TimeTicks time = v8::base::TimeTicks::Now();
while (time == v8::base::TimeTicks::Now()) {
// Force time to progress before destroying scope.
}
}
stats_collector->NotifyMarkingCompleted(0);
stats_collector->NotifySweepingCompleted();
const StatsCollector::Event& event =
stats_collector->GetPreviousEventForTesting();
for (int i = 0; i < StatsCollector::kNumHistogramScopeIds; ++i) {
EXPECT_TRUE(event.scope_data[i].IsZero());
}
for (int i = 0; i < StatsCollector::kNumHistogramConcurrentScopeIds; ++i) {
if (i == scope_id)
EXPECT_LT(0, event.concurrent_scope_data[i]);
else
EXPECT_EQ(0, event.concurrent_scope_data[i]);
}
}
}
} // namespace internal
} // namespace cppgc
#endif // CPPGC_IS_STANDALONE