Commit 88e5b8f5 authored by Omer Katz's avatar Omer Katz Committed by V8 LUCI CQ

cppgc, heap: Implement UMA reporting for cppgc library.

This CL does 2 things:
1) Implements forwarding of histogram reporting from cppgc to v8 via
CppHeap.
2) Establishes the pipeline in GCTracer for sending the histograms to
the embedder.

Currently only cppgc histograms are populated.

See crrev.com/c/2916956 for usage.

Bug: chromium:1154636
Change-Id: I8150116f757e105d0dfac96a3f6e7dd95717f5bd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2917033
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74830}
parent 990c9386
...@@ -8820,7 +8820,7 @@ void Isolate::SetAddHistogramSampleFunction( ...@@ -8820,7 +8820,7 @@ void Isolate::SetAddHistogramSampleFunction(
void Isolate::SetMetricsRecorder( void Isolate::SetMetricsRecorder(
const std::shared_ptr<metrics::Recorder>& metrics_recorder) { const std::shared_ptr<metrics::Recorder>& metrics_recorder) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->metrics_recorder()->SetRecorder(isolate, metrics_recorder); isolate->metrics_recorder()->SetEmbedderRecorder(isolate, metrics_recorder);
} }
void Isolate::SetAddCrashKeyCallback(AddCrashKeyCallback callback) { void Isolate::SetAddCrashKeyCallback(AddCrashKeyCallback callback) {
......
...@@ -15,8 +15,9 @@ ...@@ -15,8 +15,9 @@
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/base/platform/time.h" #include "src/base/platform/time.h"
#include "src/execution/isolate.h" #include "src/execution/isolate-inl.h"
#include "src/flags/flags.h" #include "src/flags/flags.h"
#include "src/handles/handles.h"
#include "src/heap/base/stack.h" #include "src/heap/base/stack.h"
#include "src/heap/cppgc-js/cpp-snapshot.h" #include "src/heap/cppgc-js/cpp-snapshot.h"
#include "src/heap/cppgc-js/unified-heap-marking-state.h" #include "src/heap/cppgc-js/unified-heap-marking-state.h"
...@@ -29,15 +30,18 @@ ...@@ -29,15 +30,18 @@
#include "src/heap/cppgc/marker.h" #include "src/heap/cppgc/marker.h"
#include "src/heap/cppgc/marking-state.h" #include "src/heap/cppgc/marking-state.h"
#include "src/heap/cppgc/marking-visitor.h" #include "src/heap/cppgc/marking-visitor.h"
#include "src/heap/cppgc/metric-recorder.h"
#include "src/heap/cppgc/object-allocator.h" #include "src/heap/cppgc/object-allocator.h"
#include "src/heap/cppgc/prefinalizer-handler.h" #include "src/heap/cppgc/prefinalizer-handler.h"
#include "src/heap/cppgc/raw-heap.h" #include "src/heap/cppgc/raw-heap.h"
#include "src/heap/cppgc/stats-collector.h" #include "src/heap/cppgc/stats-collector.h"
#include "src/heap/cppgc/sweeper.h" #include "src/heap/cppgc/sweeper.h"
#include "src/heap/embedder-tracing.h" #include "src/heap/embedder-tracing.h"
#include "src/heap/gc-tracer.h"
#include "src/heap/marking-worklist.h" #include "src/heap/marking-worklist.h"
#include "src/heap/sweeper.h" #include "src/heap/sweeper.h"
#include "src/init/v8.h" #include "src/init/v8.h"
#include "src/logging/metrics.h"
#include "src/profiler/heap-profiler.h" #include "src/profiler/heap-profiler.h"
namespace v8 { namespace v8 {
...@@ -215,16 +219,74 @@ void UnifiedHeapMarker::AddObject(void* object) { ...@@ -215,16 +219,74 @@ void UnifiedHeapMarker::AddObject(void* object) {
} // namespace } // namespace
class CppHeap::MetricRecorderAdapter final
: public cppgc::internal::MetricRecorder {
public:
explicit MetricRecorderAdapter(CppHeap& cpp_heap) : cpp_heap_(cpp_heap) {}
void AddMainThreadEvent(const FullCycle& cppgc_event) final {
cpp_heap_.last_cppgc_full_gc_event_ = cppgc_event;
GetIsolate()->heap()->tracer()->NotifyGCCompleted();
}
void AddMainThreadEvent(const MainThreadIncrementalMark& cppgc_event) final {
// Incremental marking steps might be nested in V8 marking steps. In such
// cases, stash the relevant values and delegate to V8 to report them. For
// non-nested steps, report to the Recorder directly.
if (cpp_heap_.is_in_v8_marking_step_) {
cpp_heap_.last_cppgc_incremental_mark_event_ = cppgc_event;
return;
}
// This is a standalone incremental marking step.
const std::shared_ptr<metrics::Recorder>& recorder =
GetIsolate()->metrics_recorder();
DCHECK_NOT_NULL(recorder);
if (!recorder->HasEmbedderRecorder()) return;
::v8::metrics::GarbageCollectionFullMainThreadIncrementalMark event;
event.cpp_wall_clock_duration_in_us = cppgc_event.duration_us;
// TODO(chromium:1154636): Populate event.wall_clock_duration_in_us.
recorder->AddMainThreadEvent(event, GetContextId());
}
void AddMainThreadEvent(const MainThreadIncrementalSweep& cppgc_event) final {
// Incremental sweeping steps are never nested inside V8 sweeping steps, so
// report to the Recorder directly.
const std::shared_ptr<metrics::Recorder>& recorder =
GetIsolate()->metrics_recorder();
DCHECK_NOT_NULL(recorder);
if (!recorder->HasEmbedderRecorder()) return;
::v8::metrics::GarbageCollectionFullMainThreadIncrementalSweep event;
event.cpp_wall_clock_duration_in_us = cppgc_event.duration_us;
// TODO(chromium:1154636): Populate event.wall_clock_duration_in_us.
recorder->AddMainThreadEvent(event, GetContextId());
}
private:
Isolate* GetIsolate() {
DCHECK_NOT_NULL(cpp_heap_.isolate());
return reinterpret_cast<Isolate*>(cpp_heap_.isolate());
}
v8::metrics::Recorder::ContextId GetContextId() {
DCHECK_NOT_NULL(GetIsolate());
if (GetIsolate()->context().is_null())
return v8::metrics::Recorder::ContextId::Empty();
HandleScope scope(GetIsolate());
return GetIsolate()->GetOrRegisterRecorderContextId(
GetIsolate()->native_context());
}
CppHeap& cpp_heap_;
};
CppHeap::CppHeap( CppHeap::CppHeap(
v8::Platform* platform, v8::Platform* platform,
const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces, const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces,
const v8::WrapperDescriptor& wrapper_descriptor, const v8::WrapperDescriptor& wrapper_descriptor)
std::unique_ptr<cppgc::internal::MetricRecorder> metric_recorder)
: cppgc::internal::HeapBase( : cppgc::internal::HeapBase(
std::make_shared<CppgcPlatformAdapter>(platform), custom_spaces, std::make_shared<CppgcPlatformAdapter>(platform), custom_spaces,
cppgc::internal::HeapBase::StackSupport:: cppgc::internal::HeapBase::StackSupport::
kSupportsConservativeStackScan, kSupportsConservativeStackScan),
std::move(metric_recorder)),
wrapper_descriptor_(wrapper_descriptor) { wrapper_descriptor_(wrapper_descriptor) {
CHECK_NE(WrapperDescriptor::kUnknownEmbedderId, CHECK_NE(WrapperDescriptor::kUnknownEmbedderId,
wrapper_descriptor_.embedder_id_for_garbage_collected); wrapper_descriptor_.embedder_id_for_garbage_collected);
...@@ -260,6 +322,7 @@ void CppHeap::AttachIsolate(Isolate* isolate) { ...@@ -260,6 +322,7 @@ void CppHeap::AttachIsolate(Isolate* isolate) {
isolate_->heap()->SetEmbedderHeapTracer(this); isolate_->heap()->SetEmbedderHeapTracer(this);
isolate_->heap()->local_embedder_heap_tracer()->SetWrapperDescriptor( isolate_->heap()->local_embedder_heap_tracer()->SetWrapperDescriptor(
wrapper_descriptor_); wrapper_descriptor_);
SetMetricRecorder(std::make_unique<MetricRecorderAdapter>(*this));
no_gc_scope_--; no_gc_scope_--;
} }
...@@ -277,6 +340,7 @@ void CppHeap::DetachIsolate() { ...@@ -277,6 +340,7 @@ void CppHeap::DetachIsolate() {
isolate_->heap_profiler()->RemoveBuildEmbedderGraphCallback( isolate_->heap_profiler()->RemoveBuildEmbedderGraphCallback(
&CppGraphBuilder::Run, this); &CppGraphBuilder::Run, this);
} }
SetMetricRecorder(nullptr);
isolate_ = nullptr; isolate_ = nullptr;
// Any future garbage collections will ignore the V8->C++ references. // Any future garbage collections will ignore the V8->C++ references.
isolate()->SetEmbedderHeapTracer(nullptr); isolate()->SetEmbedderHeapTracer(nullptr);
...@@ -328,6 +392,7 @@ void CppHeap::TracePrologue(TraceFlags flags) { ...@@ -328,6 +392,7 @@ void CppHeap::TracePrologue(TraceFlags flags) {
} }
bool CppHeap::AdvanceTracing(double deadline_in_ms) { bool CppHeap::AdvanceTracing(double deadline_in_ms) {
is_in_v8_marking_step_ = true;
cppgc::internal::StatsCollector::EnabledScope stats_scope( cppgc::internal::StatsCollector::EnabledScope stats_scope(
stats_collector(), stats_collector(),
in_atomic_pause_ ? cppgc::internal::StatsCollector::kAtomicMark in_atomic_pause_ ? cppgc::internal::StatsCollector::kAtomicMark
...@@ -341,6 +406,7 @@ bool CppHeap::AdvanceTracing(double deadline_in_ms) { ...@@ -341,6 +406,7 @@ bool CppHeap::AdvanceTracing(double deadline_in_ms) {
marking_done_ = marking_done_ =
marker_->AdvanceMarkingWithLimits(deadline, marked_bytes_limit); marker_->AdvanceMarkingWithLimits(deadline, marked_bytes_limit);
DCHECK_IMPLIES(in_atomic_pause_, marking_done_); DCHECK_IMPLIES(in_atomic_pause_, marking_done_);
is_in_v8_marking_step_ = false;
return marking_done_; return marking_done_;
} }
......
...@@ -39,9 +39,7 @@ class V8_EXPORT_PRIVATE CppHeap final ...@@ -39,9 +39,7 @@ class V8_EXPORT_PRIVATE CppHeap final
CppHeap( CppHeap(
v8::Platform* platform, v8::Platform* platform,
const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces, const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces,
const v8::WrapperDescriptor& wrapper_descriptor, const v8::WrapperDescriptor& wrapper_descriptor);
std::unique_ptr<cppgc::internal::MetricRecorder> metric_recorder =
nullptr);
~CppHeap() final; ~CppHeap() final;
CppHeap(const CppHeap&) = delete; CppHeap(const CppHeap&) = delete;
...@@ -78,7 +76,24 @@ class V8_EXPORT_PRIVATE CppHeap final ...@@ -78,7 +76,24 @@ class V8_EXPORT_PRIVATE CppHeap final
void AllocatedObjectSizeDecreased(size_t) final; void AllocatedObjectSizeDecreased(size_t) final;
void ResetAllocatedObjectSize(size_t) final {} void ResetAllocatedObjectSize(size_t) final {}
// The following 3 methods are only used for reporting nested cpp events
// through V8. Standalone events are reported directly.
bool MetricsReportPending() const {
return last_cppgc_full_gc_event_.has_value();
}
const base::Optional<cppgc::internal::MetricRecorder::FullCycle>
ExtractLastCppgcFullGcEvent() {
return std::move(last_cppgc_full_gc_event_);
}
const base::Optional<
cppgc::internal::MetricRecorder::MainThreadIncrementalMark>
ExtractLastCppgcIncrementalMarkEvent() {
return std::move(last_cppgc_incremental_mark_event_);
}
private: private:
class MetricRecorderAdapter;
void FinalizeIncrementalGarbageCollectionIfNeeded( void FinalizeIncrementalGarbageCollectionIfNeeded(
cppgc::Heap::StackState) final { cppgc::Heap::StackState) final {
// For unified heap, CppHeap shouldn't finalize independently (i.e. // For unified heap, CppHeap shouldn't finalize independently (i.e.
...@@ -103,6 +118,14 @@ class V8_EXPORT_PRIVATE CppHeap final ...@@ -103,6 +118,14 @@ class V8_EXPORT_PRIVATE CppHeap final
bool in_detached_testing_mode_ = false; bool in_detached_testing_mode_ = false;
bool force_incremental_marking_for_testing_ = false; bool force_incremental_marking_for_testing_ = false;
bool is_in_v8_marking_step_ = false;
base::Optional<cppgc::internal::MetricRecorder::FullCycle>
last_cppgc_full_gc_event_;
base::Optional<cppgc::internal::MetricRecorder::MainThreadIncrementalMark>
last_cppgc_incremental_mark_event_;
friend class MetricRecorderAdapter;
}; };
} // namespace internal } // namespace internal
......
...@@ -53,8 +53,7 @@ class ObjectSizeCounter : private HeapVisitor<ObjectSizeCounter> { ...@@ -53,8 +53,7 @@ class ObjectSizeCounter : private HeapVisitor<ObjectSizeCounter> {
HeapBase::HeapBase( HeapBase::HeapBase(
std::shared_ptr<cppgc::Platform> platform, std::shared_ptr<cppgc::Platform> platform,
const std::vector<std::unique_ptr<CustomSpaceBase>>& custom_spaces, const std::vector<std::unique_ptr<CustomSpaceBase>>& custom_spaces,
StackSupport stack_support, StackSupport stack_support)
std::unique_ptr<MetricRecorder> histogram_recorder)
: raw_heap_(this, custom_spaces), : raw_heap_(this, custom_spaces),
platform_(std::move(platform)), platform_(std::move(platform)),
#if defined(LEAK_SANITIZER) #if defined(LEAK_SANITIZER)
...@@ -67,8 +66,7 @@ HeapBase::HeapBase( ...@@ -67,8 +66,7 @@ HeapBase::HeapBase(
#else // !CPPGC_CAGED_HEAP #else // !CPPGC_CAGED_HEAP
page_backend_(std::make_unique<PageBackend>(page_allocator())), page_backend_(std::make_unique<PageBackend>(page_allocator())),
#endif // !CPPGC_CAGED_HEAP #endif // !CPPGC_CAGED_HEAP
stats_collector_(std::make_unique<StatsCollector>( stats_collector_(std::make_unique<StatsCollector>(platform_.get())),
std::move(histogram_recorder), platform_.get())),
stack_(std::make_unique<heap::base::Stack>( stack_(std::make_unique<heap::base::Stack>(
v8::base::Stack::GetStackStart())), v8::base::Stack::GetStackStart())),
prefinalizer_handler_(std::make_unique<PreFinalizerHandler>(*this)), prefinalizer_handler_(std::make_unique<PreFinalizerHandler>(*this)),
......
...@@ -83,8 +83,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -83,8 +83,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
HeapBase(std::shared_ptr<cppgc::Platform> platform, HeapBase(std::shared_ptr<cppgc::Platform> platform,
const std::vector<std::unique_ptr<CustomSpaceBase>>& custom_spaces, const std::vector<std::unique_ptr<CustomSpaceBase>>& custom_spaces,
StackSupport stack_support, StackSupport stack_support);
std::unique_ptr<MetricRecorder> histogram_recorder);
virtual ~HeapBase(); virtual ~HeapBase();
HeapBase(const HeapBase&) = delete; HeapBase(const HeapBase&) = delete;
...@@ -196,6 +195,10 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -196,6 +195,10 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
virtual void FinalizeIncrementalGarbageCollectionForTesting( virtual void FinalizeIncrementalGarbageCollectionForTesting(
EmbedderStackState) = 0; EmbedderStackState) = 0;
void SetMetricRecorder(std::unique_ptr<MetricRecorder> histogram_recorder) {
stats_collector_->SetMetricRecorder(std::move(histogram_recorder));
}
protected: protected:
// Used by the incremental scheduler to finalize a GC if supported. // Used by the incremental scheduler to finalize a GC if supported.
virtual void FinalizeIncrementalGarbageCollectionIfNeeded( virtual void FinalizeIncrementalGarbageCollectionIfNeeded(
......
...@@ -87,8 +87,7 @@ void CheckConfig(Heap::Config config, Heap::MarkingType marking_support, ...@@ -87,8 +87,7 @@ void CheckConfig(Heap::Config config, Heap::MarkingType marking_support,
Heap::Heap(std::shared_ptr<cppgc::Platform> platform, Heap::Heap(std::shared_ptr<cppgc::Platform> platform,
cppgc::Heap::HeapOptions options) cppgc::Heap::HeapOptions options)
: HeapBase(platform, options.custom_spaces, options.stack_support, : HeapBase(platform, options.custom_spaces, options.stack_support),
nullptr /* metric_recorder */),
gc_invoker_(this, platform_.get(), options.stack_support), gc_invoker_(this, platform_.get(), options.stack_support),
growing_(&gc_invoker_, stats_collector_.get(), growing_(&gc_invoker_, stats_collector_.get(),
options.resource_constraints, options.marking_support, options.resource_constraints, options.marking_support,
......
...@@ -20,19 +20,19 @@ class StatsCollector; ...@@ -20,19 +20,19 @@ class StatsCollector;
*/ */
class MetricRecorder { class MetricRecorder {
public: public:
struct CppGCFullCycle { struct FullCycle {
struct IncrementalPhases { struct IncrementalPhases {
int64_t mark_duration_us; int64_t mark_duration_us = -1;
int64_t sweep_duration_us; int64_t sweep_duration_us = -1;
}; };
struct Phases : public IncrementalPhases { struct Phases : public IncrementalPhases {
int64_t weak_duration_us; int64_t weak_duration_us = -1;
int64_t compact_duration_us; int64_t compact_duration_us = -1;
}; };
struct Sizes { struct Sizes {
int64_t before_bytes; int64_t before_bytes = -1;
int64_t after_bytes; int64_t after_bytes = -1;
int64_t freed_bytes; int64_t freed_bytes = -1;
}; };
Phases total; Phases total;
...@@ -46,21 +46,19 @@ class MetricRecorder { ...@@ -46,21 +46,19 @@ class MetricRecorder {
double main_thread_efficiency_in_bytes_per_us; double main_thread_efficiency_in_bytes_per_us;
}; };
struct CppGCMainThreadIncrementalMark { struct MainThreadIncrementalMark {
int64_t duration_us; int64_t duration_us = -1;
}; };
struct CppGCMainThreadIncrementalSweep { struct MainThreadIncrementalSweep {
int64_t duration_us; int64_t duration_us = -1;
}; };
virtual ~MetricRecorder() = default; virtual ~MetricRecorder() = default;
virtual void AddMainThreadEvent(const CppGCFullCycle& event) {} virtual void AddMainThreadEvent(const FullCycle& event) {}
virtual void AddMainThreadEvent(const CppGCMainThreadIncrementalMark& event) { virtual void AddMainThreadEvent(const MainThreadIncrementalMark& event) {}
} virtual void AddMainThreadEvent(const MainThreadIncrementalSweep& event) {}
virtual void AddMainThreadEvent(
const CppGCMainThreadIncrementalSweep& event) {}
}; };
} // namespace internal } // namespace internal
......
...@@ -18,9 +18,7 @@ namespace internal { ...@@ -18,9 +18,7 @@ namespace internal {
// static // static
constexpr size_t StatsCollector::kAllocationThresholdBytes; constexpr size_t StatsCollector::kAllocationThresholdBytes;
StatsCollector::StatsCollector( StatsCollector::StatsCollector(Platform* platform) : platform_(platform) {
std::unique_ptr<MetricRecorder> histogram_recorder, Platform* platform)
: metric_recorder_(std::move(histogram_recorder)), platform_(platform) {
USE(platform_); USE(platform_);
} }
...@@ -154,12 +152,12 @@ double StatsCollector::GetRecentAllocationSpeedInBytesPerMs() const { ...@@ -154,12 +152,12 @@ double StatsCollector::GetRecentAllocationSpeedInBytesPerMs() const {
namespace { namespace {
int64_t SumPhases(const MetricRecorder::CppGCFullCycle::Phases& phases) { int64_t SumPhases(const MetricRecorder::FullCycle::Phases& phases) {
return phases.mark_duration_us + phases.weak_duration_us + return phases.mark_duration_us + phases.weak_duration_us +
phases.compact_duration_us + phases.sweep_duration_us; phases.compact_duration_us + phases.sweep_duration_us;
} }
MetricRecorder::CppGCFullCycle GetFullCycleEventForMetricRecorder( MetricRecorder::FullCycle GetFullCycleEventForMetricRecorder(
int64_t atomic_mark_us, int64_t atomic_weak_us, int64_t atomic_compact_us, int64_t atomic_mark_us, int64_t atomic_weak_us, int64_t atomic_compact_us,
int64_t atomic_sweep_us, int64_t incremental_mark_us, int64_t atomic_sweep_us, int64_t incremental_mark_us,
int64_t incremental_sweep_us, int64_t concurrent_mark_us, int64_t incremental_sweep_us, int64_t concurrent_mark_us,
...@@ -167,7 +165,7 @@ MetricRecorder::CppGCFullCycle GetFullCycleEventForMetricRecorder( ...@@ -167,7 +165,7 @@ MetricRecorder::CppGCFullCycle GetFullCycleEventForMetricRecorder(
int64_t objects_after_bytes, int64_t objects_freed_bytes, int64_t objects_after_bytes, int64_t objects_freed_bytes,
int64_t memory_before_bytes, int64_t memory_after_bytes, int64_t memory_before_bytes, int64_t memory_after_bytes,
int64_t memory_freed_bytes) { int64_t memory_freed_bytes) {
MetricRecorder::CppGCFullCycle event; MetricRecorder::FullCycle event;
// MainThread.Incremental: // MainThread.Incremental:
event.main_thread_incremental.mark_duration_us = incremental_mark_us; event.main_thread_incremental.mark_duration_us = incremental_mark_us;
event.main_thread_incremental.sweep_duration_us = incremental_sweep_us; event.main_thread_incremental.sweep_duration_us = incremental_sweep_us;
...@@ -223,7 +221,7 @@ void StatsCollector::NotifySweepingCompleted() { ...@@ -223,7 +221,7 @@ void StatsCollector::NotifySweepingCompleted() {
previous_ = std::move(current_); previous_ = std::move(current_);
current_ = Event(); current_ = Event();
if (metric_recorder_) { if (metric_recorder_) {
MetricRecorder::CppGCFullCycle event = GetFullCycleEventForMetricRecorder( MetricRecorder::FullCycle event = GetFullCycleEventForMetricRecorder(
previous_.scope_data[kAtomicMark].InMicroseconds(), previous_.scope_data[kAtomicMark].InMicroseconds(),
previous_.scope_data[kAtomicWeak].InMicroseconds(), previous_.scope_data[kAtomicWeak].InMicroseconds(),
previous_.scope_data[kAtomicCompact].InMicroseconds(), previous_.scope_data[kAtomicCompact].InMicroseconds(),
...@@ -315,14 +313,12 @@ void StatsCollector::RecordHistogramSample(ScopeId scope_id_, ...@@ -315,14 +313,12 @@ void StatsCollector::RecordHistogramSample(ScopeId scope_id_,
v8::base::TimeDelta time) { v8::base::TimeDelta time) {
switch (scope_id_) { switch (scope_id_) {
case kIncrementalMark: { case kIncrementalMark: {
MetricRecorder::CppGCMainThreadIncrementalMark event{ MetricRecorder::MainThreadIncrementalMark event{time.InMicroseconds()};
time.InMicroseconds()};
metric_recorder_->AddMainThreadEvent(event); metric_recorder_->AddMainThreadEvent(event);
break; break;
} }
case kIncrementalSweep: { case kIncrementalSweep: {
MetricRecorder::CppGCMainThreadIncrementalSweep event{ MetricRecorder::MainThreadIncrementalSweep event{time.InMicroseconds()};
time.InMicroseconds()};
metric_recorder_->AddMainThreadEvent(event); metric_recorder_->AddMainThreadEvent(event);
break; break;
} }
......
...@@ -249,7 +249,7 @@ class V8_EXPORT_PRIVATE StatsCollector final { ...@@ -249,7 +249,7 @@ class V8_EXPORT_PRIVATE StatsCollector final {
// reasonably interesting sizes. // reasonably interesting sizes.
static constexpr size_t kAllocationThresholdBytes = 1024; static constexpr size_t kAllocationThresholdBytes = 1024;
StatsCollector(std::unique_ptr<MetricRecorder>, Platform*); explicit StatsCollector(Platform*);
StatsCollector(const StatsCollector&) = delete; StatsCollector(const StatsCollector&) = delete;
StatsCollector& operator=(const StatsCollector&) = delete; StatsCollector& operator=(const StatsCollector&) = delete;
...@@ -293,8 +293,7 @@ class V8_EXPORT_PRIVATE StatsCollector final { ...@@ -293,8 +293,7 @@ class V8_EXPORT_PRIVATE StatsCollector final {
void NotifyAllocatedMemory(int64_t); void NotifyAllocatedMemory(int64_t);
void NotifyFreedMemory(int64_t); void NotifyFreedMemory(int64_t);
void SetMetricRecorderForTesting( void SetMetricRecorder(std::unique_ptr<MetricRecorder> histogram_recorder) {
std::unique_ptr<MetricRecorder> histogram_recorder) {
metric_recorder_ = std::move(histogram_recorder); metric_recorder_ = std::move(histogram_recorder);
} }
...@@ -347,6 +346,7 @@ class V8_EXPORT_PRIVATE StatsCollector final { ...@@ -347,6 +346,7 @@ class V8_EXPORT_PRIVATE StatsCollector final {
std::unique_ptr<MetricRecorder> metric_recorder_; std::unique_ptr<MetricRecorder> metric_recorder_;
// |platform_| is used by the TRACE_EVENT_* macros.
Platform* platform_; Platform* platform_;
}; };
......
...@@ -6,14 +6,18 @@ ...@@ -6,14 +6,18 @@
#include <cstdarg> #include <cstdarg>
#include "include/v8-metrics.h"
#include "src/base/atomic-utils.h" #include "src/base/atomic-utils.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/execution/isolate.h" #include "src/execution/isolate.h"
#include "src/execution/thread-id.h" #include "src/execution/thread-id.h"
#include "src/heap/cppgc-js/cpp-heap.h"
#include "src/heap/cppgc/metric-recorder.h"
#include "src/heap/heap-inl.h" #include "src/heap/heap-inl.h"
#include "src/heap/incremental-marking.h" #include "src/heap/incremental-marking.h"
#include "src/heap/spaces.h" #include "src/heap/spaces.h"
#include "src/logging/counters.h" #include "src/logging/counters.h"
#include "src/logging/metrics.h"
#include "src/logging/tracing-flags.h" #include "src/logging/tracing-flags.h"
#include "src/tracing/tracing-category-observer.h" #include "src/tracing/tracing-category-observer.h"
...@@ -445,6 +449,8 @@ void GCTracer::NotifySweepingCompleted() { ...@@ -445,6 +449,8 @@ void GCTracer::NotifySweepingCompleted() {
heap_->code_space()->PrintAllocationsOrigins(); heap_->code_space()->PrintAllocationsOrigins();
heap_->map_space()->PrintAllocationsOrigins(); heap_->map_space()->PrintAllocationsOrigins();
} }
metrics_report_pending_ = true;
NotifyGCCompleted();
} }
void GCTracer::SampleAllocation(double current_ms, void GCTracer::SampleAllocation(double current_ms,
...@@ -513,6 +519,7 @@ void GCTracer::AddIncrementalMarkingStep(double duration, size_t bytes) { ...@@ -513,6 +519,7 @@ void GCTracer::AddIncrementalMarkingStep(double duration, size_t bytes) {
incremental_marking_bytes_ += bytes; incremental_marking_bytes_ += bytes;
incremental_marking_duration_ += duration; incremental_marking_duration_ += duration;
} }
ReportIncrementalMarkingStepToRecorder();
} }
void GCTracer::Output(const char* format, ...) const { void GCTracer::Output(const char* format, ...) const {
...@@ -1295,5 +1302,123 @@ void GCTracer::RecordGCSumCounters(double atomic_pause_duration) { ...@@ -1295,5 +1302,123 @@ void GCTracer::RecordGCSumCounters(double atomic_pause_duration) {
"background_duration", marking_background_duration); "background_duration", marking_background_duration);
} }
void GCTracer::NotifyGCCompleted() {
// Report full GC cycle metric to recorder only when both v8 and cppgc (if
// available) GCs have finished. This method is invoked by both v8 and cppgc.
if (!metrics_report_pending_) {
// V8 sweeping is not done yet.
return;
}
const auto* cpp_heap = heap_->cpp_heap();
if (cpp_heap && !CppHeap::From(cpp_heap)->MetricsReportPending()) {
// Cppgc sweeping is not done yet.
return;
}
ReportFullCycleToRecorder();
}
namespace {
void CopyTimeMetrics(
::v8::metrics::GarbageCollectionPhases& metrics,
const cppgc::internal::MetricRecorder::FullCycle::IncrementalPhases&
cppgc_metrics) {
DCHECK_NE(-1, cppgc_metrics.mark_duration_us);
metrics.mark_wall_clock_duration_in_us = cppgc_metrics.mark_duration_us;
DCHECK_NE(-1, cppgc_metrics.sweep_duration_us);
metrics.sweep_wall_clock_duration_in_us = cppgc_metrics.sweep_duration_us;
}
void CopyTimeMetrics(
::v8::metrics::GarbageCollectionPhases& metrics,
const cppgc::internal::MetricRecorder::FullCycle::Phases& cppgc_metrics) {
DCHECK_NE(-1, cppgc_metrics.compact_duration_us);
metrics.compact_wall_clock_duration_in_us = cppgc_metrics.compact_duration_us;
DCHECK_NE(-1, cppgc_metrics.mark_duration_us);
metrics.mark_wall_clock_duration_in_us = cppgc_metrics.mark_duration_us;
DCHECK_NE(-1, cppgc_metrics.sweep_duration_us);
metrics.sweep_wall_clock_duration_in_us = cppgc_metrics.sweep_duration_us;
DCHECK_NE(-1, cppgc_metrics.weak_duration_us);
metrics.weak_wall_clock_duration_in_us = cppgc_metrics.weak_duration_us;
}
void CopySizeMetrics(
::v8::metrics::GarbageCollectionSizes& metrics,
const cppgc::internal::MetricRecorder::FullCycle::Sizes& cppgc_metrics) {
DCHECK_NE(-1, cppgc_metrics.after_bytes);
metrics.bytes_after = cppgc_metrics.after_bytes;
DCHECK_NE(-1, cppgc_metrics.before_bytes);
metrics.bytes_before = cppgc_metrics.before_bytes;
DCHECK_NE(-1, cppgc_metrics.freed_bytes);
metrics.bytes_freed = cppgc_metrics.freed_bytes;
}
::v8::metrics::Recorder::ContextId GetContextId(
v8::internal::Isolate* isolate) {
DCHECK_NOT_NULL(isolate);
if (isolate->context().is_null())
return v8::metrics::Recorder::ContextId::Empty();
HandleScope scope(isolate);
return isolate->GetOrRegisterRecorderContextId(isolate->native_context());
}
} // namespace
void GCTracer::ReportFullCycleToRecorder() {
const std::shared_ptr<metrics::Recorder>& recorder =
heap_->isolate()->metrics_recorder();
DCHECK_NOT_NULL(recorder);
if (!recorder->HasEmbedderRecorder()) return;
::v8::metrics::GarbageCollectionFullCycle event;
if (heap_->cpp_heap()) {
const base::Optional<cppgc::internal::MetricRecorder::FullCycle>
optional_cppgc_event = v8::internal::CppHeap::From(heap_->cpp_heap())
->ExtractLastCppgcFullGcEvent();
DCHECK(optional_cppgc_event.has_value());
const cppgc::internal::MetricRecorder::FullCycle& cppgc_event =
optional_cppgc_event.value();
CopyTimeMetrics(event.total_cpp, cppgc_event.total);
CopyTimeMetrics(event.main_thread_cpp, cppgc_event.main_thread);
CopyTimeMetrics(event.main_thread_atomic_cpp,
cppgc_event.main_thread_atomic);
CopyTimeMetrics(event.main_thread_incremental_cpp,
cppgc_event.main_thread_incremental);
CopySizeMetrics(event.objects_cpp, cppgc_event.objects);
CopySizeMetrics(event.memory_cpp, cppgc_event.memory);
DCHECK_NE(-1, cppgc_event.collection_rate_in_percent);
event.collection_rate_cpp_in_percent =
cppgc_event.collection_rate_in_percent;
DCHECK_NE(-1, cppgc_event.efficiency_in_bytes_per_us);
event.efficiency_cpp_in_bytes_per_us =
cppgc_event.efficiency_in_bytes_per_us;
DCHECK_NE(-1, cppgc_event.main_thread_efficiency_in_bytes_per_us);
event.main_thread_efficiency_cpp_in_bytes_per_us =
cppgc_event.main_thread_efficiency_in_bytes_per_us;
}
// TODO(chromium:1154636): Populate v8 metrics.
recorder->AddMainThreadEvent(event, GetContextId(heap_->isolate()));
metrics_report_pending_ = false;
}
void GCTracer::ReportIncrementalMarkingStepToRecorder() {
const std::shared_ptr<metrics::Recorder>& recorder =
heap_->isolate()->metrics_recorder();
DCHECK_NOT_NULL(recorder);
if (!recorder->HasEmbedderRecorder()) return;
::v8::metrics::GarbageCollectionFullMainThreadIncrementalMark event;
if (heap_->cpp_heap()) {
const base::Optional<
cppgc::internal::MetricRecorder::MainThreadIncrementalMark>
cppgc_event = v8::internal::CppHeap::From(heap_->cpp_heap())
->ExtractLastCppgcIncrementalMarkEvent();
if (cppgc_event.has_value()) {
DCHECK_NE(-1, cppgc_event.value().duration_us);
event.cpp_wall_clock_duration_in_us = cppgc_event.value().duration_us;
}
}
// TODO(chromium:1154636): Populate event.wall_clock_duration_in_us.
recorder->AddMainThreadEvent(event, GetContextId(heap_->isolate()));
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -214,6 +214,8 @@ class V8_EXPORT_PRIVATE GCTracer { ...@@ -214,6 +214,8 @@ class V8_EXPORT_PRIVATE GCTracer {
void NotifySweepingCompleted(); void NotifySweepingCompleted();
void NotifyGCCompleted();
void NotifyYoungGenerationHandling( void NotifyYoungGenerationHandling(
YoungGenerationHandling young_generation_handling); YoungGenerationHandling young_generation_handling);
...@@ -414,6 +416,9 @@ class V8_EXPORT_PRIVATE GCTracer { ...@@ -414,6 +416,9 @@ class V8_EXPORT_PRIVATE GCTracer {
void FetchBackgroundMarkCompactCounters(); void FetchBackgroundMarkCompactCounters();
void FetchBackgroundGeneralCounters(); void FetchBackgroundGeneralCounters();
void ReportFullCycleToRecorder();
void ReportIncrementalMarkingStepToRecorder();
// Pointer to the heap that owns this tracer. // Pointer to the heap that owns this tracer.
Heap* heap_; Heap* heap_;
...@@ -479,6 +484,8 @@ class V8_EXPORT_PRIVATE GCTracer { ...@@ -479,6 +484,8 @@ class V8_EXPORT_PRIVATE GCTracer {
base::RingBuffer<BytesAndDuration> recorded_embedder_generation_allocations_; base::RingBuffer<BytesAndDuration> recorded_embedder_generation_allocations_;
base::RingBuffer<double> recorded_survival_ratios_; base::RingBuffer<double> recorded_survival_ratios_;
bool metrics_report_pending_ = false;
base::Mutex background_counter_mutex_; base::Mutex background_counter_mutex_;
BackgroundCounter background_counter_[Scope::NUMBER_OF_SCOPES]; BackgroundCounter background_counter_[Scope::NUMBER_OF_SCOPES];
}; };
......
...@@ -31,7 +31,7 @@ class Recorder::Task : public v8::Task { ...@@ -31,7 +31,7 @@ class Recorder::Task : public v8::Task {
std::shared_ptr<Recorder> recorder_; std::shared_ptr<Recorder> recorder_;
}; };
void Recorder::SetRecorder( void Recorder::SetEmbedderRecorder(
Isolate* isolate, Isolate* isolate,
const std::shared_ptr<v8::metrics::Recorder>& embedder_recorder) { const std::shared_ptr<v8::metrics::Recorder>& embedder_recorder) {
foreground_task_runner_ = V8::GetCurrentPlatform()->GetForegroundTaskRunner( foreground_task_runner_ = V8::GetCurrentPlatform()->GetForegroundTaskRunner(
...@@ -40,6 +40,8 @@ void Recorder::SetRecorder( ...@@ -40,6 +40,8 @@ void Recorder::SetRecorder(
embedder_recorder_ = embedder_recorder; embedder_recorder_ = embedder_recorder;
} }
bool Recorder::HasEmbedderRecorder() const { return embedder_recorder_.get(); }
void Recorder::NotifyIsolateDisposal() { void Recorder::NotifyIsolateDisposal() {
if (embedder_recorder_) { if (embedder_recorder_) {
embedder_recorder_->NotifyIsolateDisposal(); embedder_recorder_->NotifyIsolateDisposal();
......
...@@ -22,10 +22,12 @@ namespace metrics { ...@@ -22,10 +22,12 @@ namespace metrics {
class Recorder : public std::enable_shared_from_this<Recorder> { class Recorder : public std::enable_shared_from_this<Recorder> {
public: public:
V8_EXPORT_PRIVATE void SetRecorder( V8_EXPORT_PRIVATE void SetEmbedderRecorder(
Isolate* isolate, Isolate* isolate,
const std::shared_ptr<v8::metrics::Recorder>& embedder_recorder); const std::shared_ptr<v8::metrics::Recorder>& embedder_recorder);
V8_EXPORT_PRIVATE bool HasEmbedderRecorder() const;
V8_EXPORT_PRIVATE void NotifyIsolateDisposal(); V8_EXPORT_PRIVATE void NotifyIsolateDisposal();
template <class T> template <class T>
......
...@@ -57,11 +57,12 @@ void FakeAllocate(StatsCollector* stats_collector, size_t bytes) { ...@@ -57,11 +57,12 @@ void FakeAllocate(StatsCollector* stats_collector, size_t bytes) {
stats_collector->NotifySafePointForConservativeCollection(); stats_collector->NotifySafePointForConservativeCollection();
} }
static constexpr Platform* kNoPlatform = nullptr;
} // namespace } // namespace
TEST(HeapGrowingTest, ConservativeGCInvoked) { TEST(HeapGrowingTest, ConservativeGCInvoked) {
StatsCollector stats_collector(nullptr /* metric_recorder */, StatsCollector stats_collector(kNoPlatform);
nullptr /* platform */);
MockGarbageCollector gc; MockGarbageCollector gc;
cppgc::Heap::ResourceConstraints constraints; cppgc::Heap::ResourceConstraints constraints;
// Force GC at the first update. // Force GC at the first update.
...@@ -77,8 +78,7 @@ TEST(HeapGrowingTest, ConservativeGCInvoked) { ...@@ -77,8 +78,7 @@ TEST(HeapGrowingTest, ConservativeGCInvoked) {
} }
TEST(HeapGrowingTest, InitialHeapSize) { TEST(HeapGrowingTest, InitialHeapSize) {
StatsCollector stats_collector(nullptr /* metric_recorder */, StatsCollector stats_collector(kNoPlatform);
nullptr /* platform */);
MockGarbageCollector gc; MockGarbageCollector gc;
cppgc::Heap::ResourceConstraints constraints; cppgc::Heap::ResourceConstraints constraints;
// Use larger size to avoid running into small heap optimizations. // Use larger size to avoid running into small heap optimizations.
...@@ -98,8 +98,7 @@ TEST(HeapGrowingTest, InitialHeapSize) { ...@@ -98,8 +98,7 @@ TEST(HeapGrowingTest, InitialHeapSize) {
TEST(HeapGrowingTest, ConstantGrowingFactor) { TEST(HeapGrowingTest, ConstantGrowingFactor) {
// Use larger size to avoid running into small heap optimizations. // Use larger size to avoid running into small heap optimizations.
constexpr size_t kObjectSize = 10 * HeapGrowing::kMinLimitIncrease; constexpr size_t kObjectSize = 10 * HeapGrowing::kMinLimitIncrease;
StatsCollector stats_collector(nullptr /* metric_recorder */, StatsCollector stats_collector(kNoPlatform);
nullptr /* platform */);
FakeGarbageCollector gc(&stats_collector); FakeGarbageCollector gc(&stats_collector);
cppgc::Heap::ResourceConstraints constraints; cppgc::Heap::ResourceConstraints constraints;
// Force GC at the first update. // Force GC at the first update.
...@@ -117,8 +116,7 @@ TEST(HeapGrowingTest, ConstantGrowingFactor) { ...@@ -117,8 +116,7 @@ TEST(HeapGrowingTest, ConstantGrowingFactor) {
TEST(HeapGrowingTest, SmallHeapGrowing) { TEST(HeapGrowingTest, SmallHeapGrowing) {
// Larger constant to avoid running into special handling for smaller heaps. // Larger constant to avoid running into special handling for smaller heaps.
constexpr size_t kLargeAllocation = 100 * kMB; constexpr size_t kLargeAllocation = 100 * kMB;
StatsCollector stats_collector(nullptr /* metric_recorder */, StatsCollector stats_collector(kNoPlatform);
nullptr /* platform */);
FakeGarbageCollector gc(&stats_collector); FakeGarbageCollector gc(&stats_collector);
cppgc::Heap::ResourceConstraints constraints; cppgc::Heap::ResourceConstraints constraints;
// Force GC at the first update. // Force GC at the first update.
...@@ -134,8 +132,7 @@ TEST(HeapGrowingTest, SmallHeapGrowing) { ...@@ -134,8 +132,7 @@ TEST(HeapGrowingTest, SmallHeapGrowing) {
} }
TEST(HeapGrowingTest, IncrementalGCStarted) { TEST(HeapGrowingTest, IncrementalGCStarted) {
StatsCollector stats_collector(nullptr /* metric_recorder */, StatsCollector stats_collector(kNoPlatform);
nullptr /* platform */);
MockGarbageCollector gc; MockGarbageCollector gc;
cppgc::Heap::ResourceConstraints constraints; cppgc::Heap::ResourceConstraints constraints;
HeapGrowing growing(&gc, &stats_collector, constraints, HeapGrowing growing(&gc, &stats_collector, constraints,
...@@ -152,8 +149,7 @@ TEST(HeapGrowingTest, IncrementalGCStarted) { ...@@ -152,8 +149,7 @@ TEST(HeapGrowingTest, IncrementalGCStarted) {
} }
TEST(HeapGrowingTest, IncrementalGCFinalized) { TEST(HeapGrowingTest, IncrementalGCFinalized) {
StatsCollector stats_collector(nullptr /* metric_recorder */, StatsCollector stats_collector(kNoPlatform);
nullptr /* platform */);
MockGarbageCollector gc; MockGarbageCollector gc;
cppgc::Heap::ResourceConstraints constraints; cppgc::Heap::ResourceConstraints constraints;
HeapGrowing growing(&gc, &stats_collector, constraints, HeapGrowing growing(&gc, &stats_collector, constraints,
......
...@@ -18,8 +18,9 @@ constexpr size_t kMinReportedSize = StatsCollector::kAllocationThresholdBytes; ...@@ -18,8 +18,9 @@ constexpr size_t kMinReportedSize = StatsCollector::kAllocationThresholdBytes;
class StatsCollectorTest : public ::testing::Test { class StatsCollectorTest : public ::testing::Test {
public: public:
StatsCollectorTest() static constexpr Platform* kNoPlatform = nullptr;
: stats(nullptr /* metric_recorder */, nullptr /* platform */) {}
StatsCollectorTest() : stats(kNoPlatform) {}
void FakeAllocate(size_t bytes) { void FakeAllocate(size_t bytes) {
stats.NotifyAllocation(bytes); stats.NotifyAllocation(bytes);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment