2016-05-27 16:37:42 +02:00
|
|
|
// Copyright 2014 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.
|
|
|
|
|
2022-04-19 09:00:36 +02:00
|
|
|
#include "src/heap/gc-tracer.h"
|
|
|
|
|
2016-05-27 16:37:42 +02:00
|
|
|
#include <cmath>
|
|
|
|
#include <limits>
|
2024-09-17 12:09:47 +02:00
|
|
|
#include <optional>
|
2016-05-27 16:37:42 +02:00
|
|
|
|
2018-03-07 08:54:53 +01:00
|
|
|
#include "src/base/platform/platform.h"
|
2019-08-01 08:38:30 +02:00
|
|
|
#include "src/common/globals.h"
|
|
|
|
#include "src/execution/isolate.h"
|
2025-01-30 16:35:04 +01:00
|
|
|
#include "src/flags/flags.h"
|
2022-04-19 09:00:36 +02:00
|
|
|
#include "src/heap/gc-tracer-inl.h"
|
2016-09-06 22:49:51 +02:00
|
|
|
#include "test/unittests/test-utils.h"
|
2016-05-27 16:37:42 +02:00
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
|
2023-10-05 08:46:07 +02:00
|
|
|
namespace v8::internal {
|
2016-05-27 16:37:42 +02:00
|
|
|
|
2019-05-28 08:46:21 -04:00
|
|
|
using GCTracerTest = TestWithContext;
|
2016-09-06 22:49:51 +02:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2023-12-22 09:02:51 +01:00
|
|
|
void SampleAllocation(GCTracer* tracer, base::TimeTicks time,
|
|
|
|
size_t per_space_counter_bytes) {
|
2019-08-01 08:38:30 +02:00
|
|
|
// Increment counters of all spaces.
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->SampleAllocation(time, per_space_counter_bytes,
|
2019-08-01 08:38:30 +02:00
|
|
|
per_space_counter_bytes, per_space_counter_bytes);
|
2016-09-06 22:49:51 +02:00
|
|
|
}
|
|
|
|
|
2022-04-12 11:10:15 +02:00
|
|
|
enum class StartTracingMode {
|
|
|
|
kAtomic,
|
|
|
|
kIncremental,
|
|
|
|
kIncrementalStart,
|
|
|
|
kIncrementalEnterPause,
|
|
|
|
};
|
|
|
|
|
|
|
|
void StartTracing(GCTracer* tracer, GarbageCollector collector,
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracingMode mode,
|
2024-09-17 12:09:47 +02:00
|
|
|
std::optional<base::TimeTicks> time = {}) {
|
2022-04-12 11:10:15 +02:00
|
|
|
DCHECK_IMPLIES(mode != StartTracingMode::kAtomic,
|
|
|
|
!Heap::IsYoungGenerationCollector(collector));
|
|
|
|
// Start the cycle for incremental marking.
|
|
|
|
if (mode == StartTracingMode::kIncremental ||
|
|
|
|
mode == StartTracingMode::kIncrementalStart) {
|
|
|
|
tracer->StartCycle(collector, GarbageCollectionReason::kTesting,
|
|
|
|
"collector unittest",
|
|
|
|
GCTracer::MarkingType::kIncremental);
|
|
|
|
}
|
|
|
|
// If just that was requested, no more to be done.
|
|
|
|
if (mode == StartTracingMode::kIncrementalStart) return;
|
|
|
|
// Else, we enter the observable pause.
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->StartObservablePause(time.value_or(base::TimeTicks::Now()));
|
2022-04-12 11:10:15 +02:00
|
|
|
// Start an atomic GC cycle.
|
|
|
|
if (mode == StartTracingMode::kAtomic) {
|
|
|
|
tracer->StartCycle(collector, GarbageCollectionReason::kTesting,
|
|
|
|
"collector unittest", GCTracer::MarkingType::kAtomic);
|
|
|
|
}
|
|
|
|
// We enter the atomic pause.
|
|
|
|
tracer->StartAtomicPause();
|
|
|
|
// Update the current event for an incremental GC cycle.
|
|
|
|
if (mode != StartTracingMode::kAtomic) {
|
|
|
|
tracer->UpdateCurrentEvent(GarbageCollectionReason::kTesting,
|
|
|
|
"collector unittest");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-29 08:03:15 +02:00
|
|
|
void StopTracing(Heap* heap, GarbageCollector collector,
|
2024-09-17 12:09:47 +02:00
|
|
|
std::optional<base::TimeTicks> time = {}) {
|
2025-04-29 08:03:15 +02:00
|
|
|
GCTracer* tracer = heap->tracer();
|
2022-04-12 11:10:15 +02:00
|
|
|
tracer->StopAtomicPause();
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->StopObservablePause(collector, time.value_or(base::TimeTicks::Now()));
|
2022-11-22 09:57:37 -05:00
|
|
|
switch (collector) {
|
|
|
|
case GarbageCollector::SCAVENGER:
|
|
|
|
tracer->StopYoungCycleIfNeeded();
|
|
|
|
break;
|
2023-10-05 08:46:07 +02:00
|
|
|
case GarbageCollector::MINOR_MARK_SWEEPER:
|
2022-11-22 09:57:37 -05:00
|
|
|
tracer->NotifyYoungSweepingCompleted();
|
|
|
|
break;
|
|
|
|
case GarbageCollector::MARK_COMPACTOR:
|
2025-04-29 08:03:15 +02:00
|
|
|
if (heap->cpp_heap()) {
|
|
|
|
using namespace cppgc::internal;
|
|
|
|
StatsCollector* stats_collector =
|
|
|
|
CppHeap::From(heap->cpp_heap())->stats_collector();
|
|
|
|
stats_collector->NotifyMarkingStarted(
|
|
|
|
CollectionType::kMajor, cppgc::Heap::MarkingType::kAtomic,
|
|
|
|
MarkingConfig::IsForcedGC::kNotForced);
|
|
|
|
stats_collector->NotifyMarkingCompleted(0);
|
|
|
|
stats_collector->NotifySweepingCompleted(
|
|
|
|
cppgc::Heap::SweepingType::kAtomic);
|
|
|
|
}
|
2022-11-22 09:57:37 -05:00
|
|
|
tracer->NotifyFullSweepingCompleted();
|
|
|
|
break;
|
2022-04-12 11:10:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-06 22:49:51 +02:00
|
|
|
} // namespace
|
|
|
|
|
2019-08-01 08:38:30 +02:00
|
|
|
TEST_F(GCTracerTest, PerGenerationAllocationThroughput) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2016-09-06 22:49:51 +02:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2023-12-22 09:02:51 +01:00
|
|
|
tracer->allocation_time_ = base::TimeTicks();
|
2016-09-06 22:49:51 +02:00
|
|
|
|
2019-08-01 08:38:30 +02:00
|
|
|
const int time1 = 100;
|
|
|
|
const size_t counter1 = 1000;
|
2023-12-22 09:02:51 +01:00
|
|
|
SampleAllocation(tracer, base::TimeTicks::FromMsTicksForTesting(time1),
|
|
|
|
counter1);
|
2019-08-01 08:38:30 +02:00
|
|
|
const int time2 = 200;
|
|
|
|
const size_t counter2 = 2000;
|
2023-12-22 09:02:51 +01:00
|
|
|
SampleAllocation(tracer, base::TimeTicks::FromMsTicksForTesting(time2),
|
|
|
|
counter2);
|
2025-04-29 08:03:15 +02:00
|
|
|
const size_t expected_throughput1 = counter2 / time2 * (1.0 - exp2(-2.0));
|
2019-08-01 08:38:30 +02:00
|
|
|
EXPECT_EQ(expected_throughput1,
|
|
|
|
static_cast<size_t>(
|
|
|
|
tracer->NewSpaceAllocationThroughputInBytesPerMillisecond()));
|
|
|
|
EXPECT_EQ(
|
|
|
|
expected_throughput1,
|
|
|
|
static_cast<size_t>(
|
|
|
|
tracer->OldGenerationAllocationThroughputInBytesPerMillisecond()));
|
|
|
|
EXPECT_EQ(expected_throughput1,
|
|
|
|
static_cast<size_t>(
|
|
|
|
tracer->EmbedderAllocationThroughputInBytesPerMillisecond()));
|
|
|
|
const int time3 = 1000;
|
|
|
|
const size_t counter3 = 30000;
|
2023-12-22 09:02:51 +01:00
|
|
|
SampleAllocation(tracer, base::TimeTicks::FromMsTicksForTesting(time3),
|
|
|
|
counter3);
|
2025-04-29 08:03:15 +02:00
|
|
|
const size_t expected_throughput2 =
|
|
|
|
(counter3 - counter2) / (time3 - time2) * (1.0 - exp2(-8.0)) +
|
|
|
|
exp2(-8.0) * expected_throughput1;
|
|
|
|
EXPECT_GE(expected_throughput2, expected_throughput1);
|
|
|
|
EXPECT_LE(expected_throughput2, (counter3 - counter2) / (time3 - time2));
|
2019-08-01 08:38:30 +02:00
|
|
|
EXPECT_EQ(expected_throughput2,
|
|
|
|
static_cast<size_t>(
|
|
|
|
tracer->NewSpaceAllocationThroughputInBytesPerMillisecond()));
|
|
|
|
EXPECT_EQ(
|
|
|
|
expected_throughput2,
|
|
|
|
static_cast<size_t>(
|
|
|
|
tracer->OldGenerationAllocationThroughputInBytesPerMillisecond()));
|
|
|
|
EXPECT_EQ(expected_throughput2,
|
|
|
|
static_cast<size_t>(
|
|
|
|
tracer->EmbedderAllocationThroughputInBytesPerMillisecond()));
|
2016-09-06 22:49:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(GCTracerTest, RegularScope) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2016-09-06 22:49:51 +02:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
|
|
|
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta(),
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_MARK]);
|
2022-04-12 11:10:15 +02:00
|
|
|
// Sample not added because the cycle has not started.
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_MARK,
|
|
|
|
base::TimeDelta::FromMilliseconds(10));
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
|
|
|
StartTracingMode::kAtomic);
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_MARK,
|
|
|
|
base::TimeDelta::FromMilliseconds(100));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MARK_COMPACTOR);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(100),
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_MARK]);
|
2016-09-06 22:49:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(GCTracerTest, IncrementalScope) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2016-09-06 22:49:51 +02:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
|
|
|
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta(),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_INCREMENTAL]);
|
2016-09-06 22:49:51 +02:00
|
|
|
// Sample is added because its ScopeId is listed as incremental sample.
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_INCREMENTAL,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeDelta::FromMilliseconds(100));
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
|
|
|
StartTracingMode::kIncremental);
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_INCREMENTAL,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeDelta::FromMilliseconds(100));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MARK_COMPACTOR);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(200),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_INCREMENTAL]);
|
2016-09-06 22:49:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(GCTracerTest, IncrementalMarkingDetails) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2016-09-06 22:49:51 +02:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
|
|
|
|
|
|
|
// Round 1.
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_INCREMENTAL,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeDelta::FromMilliseconds(50));
|
2016-12-23 16:30:57 +01:00
|
|
|
// Scavenger has no impact on incremental marking details.
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::SCAVENGER, StartTracingMode::kAtomic);
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::SCAVENGER);
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
|
|
|
StartTracingMode::kIncremental);
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_INCREMENTAL,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeDelta::FromMilliseconds(100));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MARK_COMPACTOR);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(100),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.incremental_scopes[GCTracer::Scope::MC_INCREMENTAL]
|
2023-10-05 08:46:07 +02:00
|
|
|
.longest_step);
|
2025-04-29 08:03:15 +02:00
|
|
|
EXPECT_EQ(2,
|
|
|
|
tracer->current_.incremental_scopes[GCTracer::Scope::MC_INCREMENTAL]
|
|
|
|
.steps);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(150),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.incremental_scopes[GCTracer::Scope::MC_INCREMENTAL]
|
2023-10-05 08:46:07 +02:00
|
|
|
.duration);
|
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(150),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_INCREMENTAL]);
|
2016-09-06 22:49:51 +02:00
|
|
|
|
2016-12-23 16:30:57 +01:00
|
|
|
// Round 2. Numbers should be reset.
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_INCREMENTAL,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeDelta::FromMilliseconds(13));
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_INCREMENTAL,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeDelta::FromMilliseconds(15));
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
|
|
|
StartTracingMode::kIncremental);
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_INCREMENTAL,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeDelta::FromMilliseconds(122));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MARK_COMPACTOR);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(122),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.incremental_scopes[GCTracer::Scope::MC_INCREMENTAL]
|
2023-10-05 08:46:07 +02:00
|
|
|
.longest_step);
|
2025-04-29 08:03:15 +02:00
|
|
|
EXPECT_EQ(3,
|
|
|
|
tracer->current_.incremental_scopes[GCTracer::Scope::MC_INCREMENTAL]
|
|
|
|
.steps);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(150),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.incremental_scopes[GCTracer::Scope::MC_INCREMENTAL]
|
2023-10-05 08:46:07 +02:00
|
|
|
.duration);
|
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(150),
|
2025-04-29 08:03:15 +02:00
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_INCREMENTAL]);
|
2016-12-23 16:30:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(GCTracerTest, IncrementalMarkingSpeed) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2016-12-23 16:30:57 +01:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->previous_mark_compact_end_time_ = base::TimeTicks();
|
2016-12-23 16:30:57 +01:00
|
|
|
|
|
|
|
// Round 1.
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracingMode::kIncrementalStart,
|
|
|
|
base::TimeTicks::FromMsTicksForTesting(0));
|
2016-12-23 16:30:57 +01:00
|
|
|
// 1000000 bytes in 100ms.
|
|
|
|
tracer->AddIncrementalMarkingStep(100, 1000000);
|
|
|
|
EXPECT_EQ(1000000 / 100,
|
|
|
|
tracer->IncrementalMarkingSpeedInBytesPerMillisecond());
|
|
|
|
// 1000000 bytes in 100ms.
|
|
|
|
tracer->AddIncrementalMarkingStep(100, 1000000);
|
|
|
|
EXPECT_EQ(1000000 / 100,
|
|
|
|
tracer->IncrementalMarkingSpeedInBytesPerMillisecond());
|
2025-01-30 16:35:04 +01:00
|
|
|
if (!v8_flags.separate_gc_phases) {
|
|
|
|
// Scavenger has no impact on incremental marking details.
|
|
|
|
StartTracing(tracer, GarbageCollector::SCAVENGER,
|
|
|
|
StartTracingMode::kAtomic);
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::SCAVENGER);
|
2025-01-30 16:35:04 +01:00
|
|
|
}
|
2016-12-23 16:30:57 +01:00
|
|
|
// 1000000 bytes in 100ms.
|
|
|
|
tracer->AddIncrementalMarkingStep(100, 1000000);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(300),
|
2024-03-30 09:54:35 +01:00
|
|
|
tracer->current_.incremental_marking_duration);
|
|
|
|
EXPECT_EQ(3000000u, tracer->current_.incremental_marking_bytes);
|
2016-12-23 16:30:57 +01:00
|
|
|
EXPECT_EQ(1000000 / 100,
|
|
|
|
tracer->IncrementalMarkingSpeedInBytesPerMillisecond());
|
|
|
|
// 1000000 bytes in 100ms.
|
|
|
|
tracer->AddIncrementalMarkingStep(100, 1000000);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(400),
|
2024-03-30 09:54:35 +01:00
|
|
|
tracer->current_.incremental_marking_duration);
|
|
|
|
EXPECT_EQ(4000000u, tracer->current_.incremental_marking_bytes);
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracingMode::kIncrementalEnterPause,
|
|
|
|
base::TimeTicks::FromMsTicksForTesting(500));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MARK_COMPACTOR,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeTicks::FromMsTicksForTesting(600));
|
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(400),
|
|
|
|
tracer->current_.incremental_marking_duration);
|
2017-02-14 11:27:26 +01:00
|
|
|
EXPECT_EQ(4000000u, tracer->current_.incremental_marking_bytes);
|
2016-12-23 16:30:57 +01:00
|
|
|
EXPECT_EQ(1000000 / 100,
|
|
|
|
tracer->IncrementalMarkingSpeedInBytesPerMillisecond());
|
|
|
|
|
|
|
|
// Round 2.
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracingMode::kIncrementalStart,
|
|
|
|
base::TimeTicks::FromMsTicksForTesting(700));
|
2016-12-23 16:30:57 +01:00
|
|
|
tracer->AddIncrementalMarkingStep(2000, 1000);
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracingMode::kIncrementalEnterPause,
|
|
|
|
base::TimeTicks::FromMsTicksForTesting(3000));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MARK_COMPACTOR,
|
2023-10-05 08:46:07 +02:00
|
|
|
base::TimeTicks::FromMsTicksForTesting(3100));
|
2016-12-23 16:30:57 +01:00
|
|
|
EXPECT_DOUBLE_EQ((4000000.0 / 400 + 1000.0 / 2000) / 2,
|
|
|
|
static_cast<double>(
|
|
|
|
tracer->IncrementalMarkingSpeedInBytesPerMillisecond()));
|
2016-09-06 22:49:51 +02:00
|
|
|
}
|
|
|
|
|
2018-05-31 11:11:57 +02:00
|
|
|
TEST_F(GCTracerTest, MutatorUtilization) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2018-05-31 11:11:57 +02:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->previous_mark_compact_end_time_ = base::TimeTicks();
|
2018-05-31 11:11:57 +02:00
|
|
|
|
|
|
|
// Mark-compact #1 ended at 200ms and took 100ms.
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->RecordMutatorUtilization(base::TimeTicks::FromMsTicksForTesting(200),
|
|
|
|
base::TimeDelta::FromMilliseconds(100));
|
|
|
|
// Average mark-compact time = 100ms.
|
|
|
|
// Average mutator time = 100ms.
|
|
|
|
EXPECT_DOUBLE_EQ(0.5, tracer->CurrentMarkCompactMutatorUtilization());
|
|
|
|
EXPECT_DOUBLE_EQ(0.5, tracer->AverageMarkCompactMutatorUtilization());
|
2018-05-31 11:11:57 +02:00
|
|
|
|
|
|
|
// Mark-compact #2 ended at 400ms and took 100ms.
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->RecordMutatorUtilization(base::TimeTicks::FromMsTicksForTesting(400),
|
|
|
|
base::TimeDelta::FromMilliseconds(100));
|
|
|
|
// Average mark-compact time = 100ms * 0.5 + 100ms * 0.5.
|
|
|
|
// Average mutator time = 100ms * 0.5 + 100ms * 0.5.
|
2018-05-31 11:11:57 +02:00
|
|
|
EXPECT_DOUBLE_EQ(0.5, tracer->CurrentMarkCompactMutatorUtilization());
|
|
|
|
EXPECT_DOUBLE_EQ(0.5, tracer->AverageMarkCompactMutatorUtilization());
|
|
|
|
|
|
|
|
// Mark-compact #3 ended at 600ms and took 200ms.
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->RecordMutatorUtilization(base::TimeTicks::FromMsTicksForTesting(600),
|
|
|
|
base::TimeDelta::FromMilliseconds(200));
|
|
|
|
// Average mark-compact time = 100ms * 0.5 + 200ms * 0.5.
|
|
|
|
// Average mutator time = 100ms * 0.5 + 0ms * 0.5.
|
2018-05-31 11:11:57 +02:00
|
|
|
EXPECT_DOUBLE_EQ(0.0, tracer->CurrentMarkCompactMutatorUtilization());
|
|
|
|
EXPECT_DOUBLE_EQ(50.0 / 200.0,
|
|
|
|
tracer->AverageMarkCompactMutatorUtilization());
|
|
|
|
|
|
|
|
// Mark-compact #4 ended at 800ms and took 0ms.
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->RecordMutatorUtilization(base::TimeTicks::FromMsTicksForTesting(800),
|
|
|
|
base::TimeDelta());
|
|
|
|
// Average mark-compact time = 150ms * 0.5 + 0ms * 0.5.
|
|
|
|
// Average mutator time = 50ms * 0.5 + 200ms * 0.5.
|
2018-05-31 11:11:57 +02:00
|
|
|
EXPECT_DOUBLE_EQ(1.0, tracer->CurrentMarkCompactMutatorUtilization());
|
|
|
|
EXPECT_DOUBLE_EQ(125.0 / 200.0,
|
|
|
|
tracer->AverageMarkCompactMutatorUtilization());
|
|
|
|
}
|
|
|
|
|
2018-03-07 08:54:53 +01:00
|
|
|
TEST_F(GCTracerTest, BackgroundScavengerScope) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2018-03-07 08:54:53 +01:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::SCAVENGER, StartTracingMode::kAtomic);
|
2022-04-19 09:00:36 +02:00
|
|
|
tracer->AddScopeSample(
|
2023-10-05 08:46:07 +02:00
|
|
|
GCTracer::Scope::SCAVENGER_BACKGROUND_SCAVENGE_PARALLEL,
|
|
|
|
base::TimeDelta::FromMilliseconds(10));
|
2022-04-19 09:00:36 +02:00
|
|
|
tracer->AddScopeSample(
|
2023-10-05 08:46:07 +02:00
|
|
|
GCTracer::Scope::SCAVENGER_BACKGROUND_SCAVENGE_PARALLEL,
|
|
|
|
base::TimeDelta::FromMilliseconds(1));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::SCAVENGER);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(
|
|
|
|
base::TimeDelta::FromMilliseconds(11),
|
|
|
|
tracer->current_
|
|
|
|
.scopes[GCTracer::Scope::SCAVENGER_BACKGROUND_SCAVENGE_PARALLEL]);
|
2018-03-07 08:54:53 +01:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:46:07 +02:00
|
|
|
TEST_F(GCTracerTest, BackgroundMinorMSScope) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2018-03-07 08:54:53 +01:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MINOR_MARK_SWEEPER,
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracingMode::kAtomic);
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MINOR_MS_BACKGROUND_MARKING,
|
|
|
|
base::TimeDelta::FromMilliseconds(10));
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MINOR_MS_BACKGROUND_MARKING,
|
|
|
|
base::TimeDelta::FromMilliseconds(1));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MINOR_MARK_SWEEPER);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(
|
|
|
|
base::TimeDelta::FromMilliseconds(11),
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MINOR_MS_BACKGROUND_MARKING]);
|
2018-03-07 08:54:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(GCTracerTest, BackgroundMajorMCScope) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2018-03-07 08:54:53 +01:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
|
|
|
StartTracingMode::kIncrementalStart);
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_MARKING,
|
|
|
|
base::TimeDelta::FromMilliseconds(100));
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_SWEEPING,
|
|
|
|
base::TimeDelta::FromMilliseconds(200));
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_MARKING,
|
|
|
|
base::TimeDelta::FromMilliseconds(10));
|
2025-01-30 16:35:04 +01:00
|
|
|
if (!v8_flags.separate_gc_phases) {
|
|
|
|
// Scavenger should not affect the major mark-compact scopes.
|
|
|
|
StartTracing(tracer, GarbageCollector::SCAVENGER,
|
|
|
|
StartTracingMode::kAtomic);
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::SCAVENGER);
|
2025-01-30 16:35:04 +01:00
|
|
|
}
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_SWEEPING,
|
|
|
|
base::TimeDelta::FromMilliseconds(20));
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_MARKING,
|
|
|
|
base::TimeDelta::FromMilliseconds(1));
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_SWEEPING,
|
|
|
|
base::TimeDelta::FromMilliseconds(2));
|
2022-04-12 11:10:15 +02:00
|
|
|
StartTracing(tracer, GarbageCollector::MARK_COMPACTOR,
|
2023-10-05 08:46:07 +02:00
|
|
|
StartTracingMode::kIncrementalEnterPause);
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_EVACUATE_COPY,
|
|
|
|
base::TimeDelta::FromMilliseconds(30));
|
|
|
|
tracer->AddScopeSample(GCTracer::Scope::MC_BACKGROUND_EVACUATE_COPY,
|
|
|
|
base::TimeDelta::FromMilliseconds(3));
|
2022-04-19 09:00:36 +02:00
|
|
|
tracer->AddScopeSample(
|
2023-10-05 08:46:07 +02:00
|
|
|
GCTracer::Scope::MC_BACKGROUND_EVACUATE_UPDATE_POINTERS,
|
|
|
|
base::TimeDelta::FromMilliseconds(40));
|
2022-04-19 09:00:36 +02:00
|
|
|
tracer->AddScopeSample(
|
2023-10-05 08:46:07 +02:00
|
|
|
GCTracer::Scope::MC_BACKGROUND_EVACUATE_UPDATE_POINTERS,
|
|
|
|
base::TimeDelta::FromMilliseconds(4));
|
2025-04-29 08:03:15 +02:00
|
|
|
StopTracing(i_isolate()->heap(), GarbageCollector::MARK_COMPACTOR);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(111),
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_BACKGROUND_MARKING]);
|
|
|
|
EXPECT_EQ(base::TimeDelta::FromMilliseconds(222),
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_BACKGROUND_SWEEPING]);
|
|
|
|
EXPECT_EQ(
|
|
|
|
base::TimeDelta::FromMilliseconds(33),
|
2018-03-07 08:54:53 +01:00
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_BACKGROUND_EVACUATE_COPY]);
|
2023-10-05 08:46:07 +02:00
|
|
|
EXPECT_EQ(
|
|
|
|
base::TimeDelta::FromMilliseconds(44),
|
|
|
|
tracer->current_
|
|
|
|
.scopes[GCTracer::Scope::MC_BACKGROUND_EVACUATE_UPDATE_POINTERS]);
|
2018-03-07 08:54:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class ThreadWithBackgroundScope final : public base::Thread {
|
|
|
|
public:
|
|
|
|
explicit ThreadWithBackgroundScope(GCTracer* tracer)
|
|
|
|
: Thread(Options("ThreadWithBackgroundScope")), tracer_(tracer) {}
|
|
|
|
void Run() override {
|
2021-02-24 14:47:06 +01:00
|
|
|
GCTracer::Scope scope(tracer_, GCTracer::Scope::MC_BACKGROUND_MARKING,
|
|
|
|
ThreadKind::kBackground);
|
2018-03-07 08:54:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
GCTracer* tracer_;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(GCTracerTest, MultithreadedBackgroundScope) {
|
2023-03-30 12:11:08 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2018-03-07 08:54:53 +01:00
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
ThreadWithBackgroundScope thread1(tracer);
|
|
|
|
ThreadWithBackgroundScope thread2(tracer);
|
|
|
|
tracer->ResetForTesting();
|
2019-09-24 11:56:38 -04:00
|
|
|
CHECK(thread1.Start());
|
|
|
|
CHECK(thread2.Start());
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->FetchBackgroundCounters();
|
|
|
|
|
2018-03-07 08:54:53 +01:00
|
|
|
thread1.Join();
|
|
|
|
thread2.Join();
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->FetchBackgroundCounters();
|
|
|
|
|
|
|
|
EXPECT_LE(base::TimeDelta(),
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_BACKGROUND_MARKING]);
|
2018-03-07 08:54:53 +01:00
|
|
|
}
|
|
|
|
|
2018-09-07 17:07:13 +02:00
|
|
|
class GcHistogram {
|
|
|
|
public:
|
|
|
|
static void* CreateHistogram(const char* name, int min, int max,
|
|
|
|
size_t buckets) {
|
|
|
|
histograms_[name] = std::unique_ptr<GcHistogram>(new GcHistogram());
|
|
|
|
return histograms_[name].get();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddHistogramSample(void* histogram, int sample) {
|
|
|
|
if (histograms_.empty()) return;
|
|
|
|
static_cast<GcHistogram*>(histogram)->samples_.push_back(sample);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GcHistogram* Get(const char* name) { return histograms_[name].get(); }
|
|
|
|
|
|
|
|
static void CleanUp() { histograms_.clear(); }
|
|
|
|
|
2019-03-12 09:01:49 +01:00
|
|
|
int Total() const {
|
2018-09-07 17:07:13 +02:00
|
|
|
int result = 0;
|
|
|
|
for (int i : samples_) {
|
|
|
|
result += i;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-03-12 09:01:49 +01:00
|
|
|
int Count() const { return static_cast<int>(samples_.size()); }
|
2018-09-07 17:07:13 +02:00
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<int> samples_;
|
|
|
|
static std::map<std::string, std::unique_ptr<GcHistogram>> histograms_;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::map<std::string, std::unique_ptr<GcHistogram>> GcHistogram::histograms_ =
|
|
|
|
std::map<std::string, std::unique_ptr<GcHistogram>>();
|
|
|
|
|
|
|
|
TEST_F(GCTracerTest, RecordMarkCompactHistograms) {
|
2022-09-21 13:28:42 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2018-09-07 17:07:13 +02:00
|
|
|
isolate()->SetCreateHistogramFunction(&GcHistogram::CreateHistogram);
|
|
|
|
isolate()->SetAddHistogramSampleFunction(&GcHistogram::AddHistogramSample);
|
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_CLEAR] =
|
|
|
|
base::TimeDelta::FromMilliseconds(1);
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_EPILOGUE] =
|
|
|
|
base::TimeDelta::FromMilliseconds(2);
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_EVACUATE] =
|
|
|
|
base::TimeDelta::FromMilliseconds(3);
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_FINISH] =
|
|
|
|
base::TimeDelta::FromMilliseconds(4);
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_MARK] =
|
|
|
|
base::TimeDelta::FromMilliseconds(5);
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_PROLOGUE] =
|
|
|
|
base::TimeDelta::FromMilliseconds(6);
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::MC_SWEEP] =
|
|
|
|
base::TimeDelta::FromMilliseconds(7);
|
2022-04-12 11:10:15 +02:00
|
|
|
tracer->RecordGCPhasesHistograms(
|
|
|
|
GCTracer::RecordGCPhasesInfo::Mode::Finalize);
|
2018-09-07 17:07:13 +02:00
|
|
|
EXPECT_EQ(1, GcHistogram::Get("V8.GCFinalizeMC.Clear")->Total());
|
|
|
|
EXPECT_EQ(2, GcHistogram::Get("V8.GCFinalizeMC.Epilogue")->Total());
|
|
|
|
EXPECT_EQ(3, GcHistogram::Get("V8.GCFinalizeMC.Evacuate")->Total());
|
|
|
|
EXPECT_EQ(4, GcHistogram::Get("V8.GCFinalizeMC.Finish")->Total());
|
|
|
|
EXPECT_EQ(5, GcHistogram::Get("V8.GCFinalizeMC.Mark")->Total());
|
|
|
|
EXPECT_EQ(6, GcHistogram::Get("V8.GCFinalizeMC.Prologue")->Total());
|
|
|
|
EXPECT_EQ(7, GcHistogram::Get("V8.GCFinalizeMC.Sweep")->Total());
|
|
|
|
GcHistogram::CleanUp();
|
|
|
|
}
|
|
|
|
|
2018-12-04 08:20:37 +01:00
|
|
|
TEST_F(GCTracerTest, RecordScavengerHistograms) {
|
2022-09-21 13:28:42 +02:00
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
2018-12-04 08:20:37 +01:00
|
|
|
isolate()->SetCreateHistogramFunction(&GcHistogram::CreateHistogram);
|
|
|
|
isolate()->SetAddHistogramSampleFunction(&GcHistogram::AddHistogramSample);
|
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
tracer->ResetForTesting();
|
2023-10-05 08:46:07 +02:00
|
|
|
tracer->current_.scopes[GCTracer::Scope::SCAVENGER_SCAVENGE_ROOTS] =
|
|
|
|
base::TimeDelta::FromMilliseconds(1);
|
|
|
|
tracer->current_.scopes[GCTracer::Scope::SCAVENGER_SCAVENGE_PARALLEL] =
|
|
|
|
base::TimeDelta::FromMilliseconds(2);
|
2022-04-12 11:10:15 +02:00
|
|
|
tracer->RecordGCPhasesHistograms(
|
|
|
|
GCTracer::RecordGCPhasesInfo::Mode::Scavenger);
|
2018-12-04 08:20:37 +01:00
|
|
|
EXPECT_EQ(1, GcHistogram::Get("V8.GCScavenger.ScavengeRoots")->Total());
|
|
|
|
EXPECT_EQ(2, GcHistogram::Get("V8.GCScavenger.ScavengeMain")->Total());
|
|
|
|
GcHistogram::CleanUp();
|
|
|
|
}
|
|
|
|
|
2025-04-29 08:03:15 +02:00
|
|
|
TEST_F(GCTracerTest, CyclePriorities) {
|
|
|
|
using Priority = v8::Isolate::Priority;
|
|
|
|
if (v8_flags.stress_incremental_marking) return;
|
|
|
|
GCTracer* tracer = i_isolate()->heap()->tracer();
|
|
|
|
CHECK_EQ(i_isolate()->priority(), Priority::kUserBlocking);
|
|
|
|
tracer->ResetForTesting();
|
|
|
|
EXPECT_TRUE(tracer->current_.priority.has_value());
|
|
|
|
EXPECT_EQ(tracer->current_.priority, Priority::kUserBlocking);
|
|
|
|
// Setting the same priority again doesn't change the cycle priority.
|
|
|
|
i_isolate()->SetPriority(Priority::kUserBlocking);
|
|
|
|
EXPECT_TRUE(tracer->current_.priority.has_value());
|
|
|
|
EXPECT_EQ(tracer->current_.priority, Priority::kUserBlocking);
|
|
|
|
// Setting a different priority resets the cycle priority.
|
|
|
|
i_isolate()->SetPriority(Priority::kUserVisible);
|
|
|
|
EXPECT_FALSE(tracer->current_.priority.has_value());
|
|
|
|
tracer->ResetForTesting();
|
|
|
|
// Initial cycle priority is the same as the isolate priority.
|
|
|
|
EXPECT_TRUE(tracer->current_.priority.has_value());
|
|
|
|
EXPECT_EQ(tracer->current_.priority, Priority::kUserVisible);
|
|
|
|
// Undoing a priority change doesn't restore a cycle priority.
|
|
|
|
i_isolate()->SetPriority(Priority::kUserBlocking);
|
|
|
|
i_isolate()->SetPriority(Priority::kUserVisible);
|
|
|
|
EXPECT_FALSE(tracer->current_.priority.has_value());
|
|
|
|
}
|
|
|
|
|
2023-10-05 08:46:07 +02:00
|
|
|
} // namespace v8::internal
|