Commit 521ae93b authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[api] Add v8::metrics::LongTaskStats for the LongTasks UKM

Bug: chromium:1173527
Change-Id: If918b739f137b9c09030104b909e7bd2d4cd5984
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2817616Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73953}
parent 3124e59d
...@@ -33,6 +33,7 @@ const int kApiSystemPointerSize = sizeof(void*); ...@@ -33,6 +33,7 @@ const int kApiSystemPointerSize = sizeof(void*);
const int kApiDoubleSize = sizeof(double); const int kApiDoubleSize = sizeof(double);
const int kApiInt32Size = sizeof(int32_t); const int kApiInt32Size = sizeof(int32_t);
const int kApiInt64Size = sizeof(int64_t); const int kApiInt64Size = sizeof(int64_t);
const int kApiSizetSize = sizeof(size_t);
// Tag information for HeapObject. // Tag information for HeapObject.
const int kHeapObjectTag = 1; const int kHeapObjectTag = 1;
...@@ -226,8 +227,10 @@ class Internals { ...@@ -226,8 +227,10 @@ class Internals {
kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize; kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize;
static const int kIsolateCageBaseOffset = static const int kIsolateCageBaseOffset =
kIsolateFastApiCallTargetOffset + kApiSystemPointerSize; kIsolateFastApiCallTargetOffset + kApiSystemPointerSize;
static const int kIsolateStackGuardOffset = static const int kIsolateLongTaskStatsCounterOffset =
kIsolateCageBaseOffset + kApiSystemPointerSize; kIsolateCageBaseOffset + kApiSystemPointerSize;
static const int kIsolateStackGuardOffset =
kIsolateLongTaskStatsCounterOffset + kApiSizetSize;
static const int kIsolateRootsOffset = static const int kIsolateRootsOffset =
kIsolateStackGuardOffset + 7 * kApiSystemPointerSize; kIsolateStackGuardOffset + 7 * kApiSystemPointerSize;
...@@ -360,6 +363,12 @@ class Internals { ...@@ -360,6 +363,12 @@ class Internals {
return *reinterpret_cast<void* const*>(addr); return *reinterpret_cast<void* const*>(addr);
} }
V8_INLINE static void IncrementLongTasksStatsCounter(v8::Isolate* isolate) {
internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
kIsolateLongTaskStatsCounterOffset;
++(*reinterpret_cast<size_t*>(addr));
}
V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) { V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) {
internal::Address addr = reinterpret_cast<internal::Address>(isolate) + internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
kIsolateRootsOffset + kIsolateRootsOffset +
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
#ifndef V8_METRICS_H_ #ifndef V8_METRICS_H_
#define V8_METRICS_H_ #define V8_METRICS_H_
#include "v8.h" // NOLINT(build/include_directory) #include "v8-internal.h" // NOLINT(build/include_directory)
#include "v8.h" // NOLINT(build/include_directory)
namespace v8 { namespace v8 {
namespace metrics { namespace metrics {
...@@ -183,6 +184,32 @@ class V8_EXPORT Recorder { ...@@ -183,6 +184,32 @@ class V8_EXPORT Recorder {
static ContextId GetContextId(Local<Context> context); static ContextId GetContextId(Local<Context> context);
}; };
/**
* Experimental API intended for the LongTasks UKM (crbug.com/1173527).
* The Reset() method should be called at the start of a potential
* long task. The Get() method returns durations of V8 work that
* happened during the task.
*
* This API is experimental and may be removed/changed in the future.
*/
struct V8_EXPORT LongTaskStats {
/**
* Resets durations of V8 work for the new task.
*/
V8_INLINE static void Reset(Isolate* isolate) {
v8::internal::Internals::IncrementLongTasksStatsCounter(isolate);
}
/**
* Returns durations of V8 work that happened since the last Reset().
*/
static LongTaskStats Get(Isolate* isolate);
int64_t gc_full_atomic_wall_clock_duration_us = 0;
int64_t gc_full_incremental_wall_clock_duration_us = 0;
int64_t gc_young_wall_clock_duration_us = 0;
};
} // namespace metrics } // namespace metrics
} // namespace v8 } // namespace v8
......
...@@ -6276,6 +6276,11 @@ metrics::Recorder::ContextId metrics::Recorder::GetContextId( ...@@ -6276,6 +6276,11 @@ metrics::Recorder::ContextId metrics::Recorder::GetContextId(
handle(i_context->native_context(), isolate)); handle(i_context->native_context(), isolate));
} }
metrics::LongTaskStats metrics::LongTaskStats::Get(v8::Isolate* isolate) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
return *i_isolate->GetCurrentLongTaskStats();
}
namespace { namespace {
i::Address* GetSerializedDataFromFixedArray(i::Isolate* isolate, i::Address* GetSerializedDataFromFixedArray(i::Isolate* isolate,
i::FixedArray list, size_t index) { i::FixedArray list, size_t index) {
......
...@@ -153,6 +153,7 @@ class IsolateData final { ...@@ -153,6 +153,7 @@ class IsolateData final {
V(kFastCCallCallerPCOffset, kSystemPointerSize) \ V(kFastCCallCallerPCOffset, kSystemPointerSize) \
V(kFastApiCallTargetOffset, kSystemPointerSize) \ V(kFastApiCallTargetOffset, kSystemPointerSize) \
V(kCageBaseOffset, kSystemPointerSize) \ V(kCageBaseOffset, kSystemPointerSize) \
V(kLongTaskStatsCounterOffset, kSizetSize) \
V(kStackGuardOffset, StackGuard::kSizeInBytes) \ V(kStackGuardOffset, StackGuard::kSizeInBytes) \
V(kRootsTableOffset, RootsTable::kEntriesCount* kSystemPointerSize) \ V(kRootsTableOffset, RootsTable::kEntriesCount* kSystemPointerSize) \
V(kExternalReferenceTableOffset, ExternalReferenceTable::kSizeInBytes) \ V(kExternalReferenceTableOffset, ExternalReferenceTable::kSizeInBytes) \
...@@ -193,6 +194,10 @@ class IsolateData final { ...@@ -193,6 +194,10 @@ class IsolateData final {
Address cage_base_ = kNullAddress; Address cage_base_ = kNullAddress;
// Used for implementation of LongTaskStats. Counts the number of potential
// long tasks.
size_t long_task_stats_counter_ = 0;
// Fields related to the system and JS stack. In particular, this contains // Fields related to the system and JS stack. In particular, this contains
// the stack limit used by stack checks in generated code. // the stack limit used by stack checks in generated code.
StackGuard stack_guard_; StackGuard stack_guard_;
...@@ -259,6 +264,8 @@ void IsolateData::AssertPredictableLayout() { ...@@ -259,6 +264,8 @@ void IsolateData::AssertPredictableLayout() {
STATIC_ASSERT(offsetof(IsolateData, fast_api_call_target_) == STATIC_ASSERT(offsetof(IsolateData, fast_api_call_target_) ==
kFastApiCallTargetOffset); kFastApiCallTargetOffset);
STATIC_ASSERT(offsetof(IsolateData, cage_base_) == kCageBaseOffset); STATIC_ASSERT(offsetof(IsolateData, cage_base_) == kCageBaseOffset);
STATIC_ASSERT(offsetof(IsolateData, long_task_stats_counter_) ==
kLongTaskStatsCounterOffset);
STATIC_ASSERT(offsetof(IsolateData, stack_guard_) == kStackGuardOffset); STATIC_ASSERT(offsetof(IsolateData, stack_guard_) == kStackGuardOffset);
#ifdef V8_HEAP_SANDBOX #ifdef V8_HEAP_SANDBOX
STATIC_ASSERT(offsetof(IsolateData, external_pointer_table_) == STATIC_ASSERT(offsetof(IsolateData, external_pointer_table_) ==
......
...@@ -3006,6 +3006,9 @@ void Isolate::CheckIsolateLayout() { ...@@ -3006,6 +3006,9 @@ void Isolate::CheckIsolateLayout() {
Internals::kIsolateFastCCallCallerPcOffset); Internals::kIsolateFastCCallCallerPcOffset);
CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.cage_base_)), CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.cage_base_)),
Internals::kIsolateCageBaseOffset); Internals::kIsolateCageBaseOffset);
CHECK_EQ(static_cast<int>(
OFFSET_OF(Isolate, isolate_data_.long_task_stats_counter_)),
Internals::kIsolateLongTaskStatsCounterOffset);
CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.stack_guard_)), CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.stack_guard_)),
Internals::kIsolateStackGuardOffset); Internals::kIsolateStackGuardOffset);
CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.roots_)), CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.roots_)),
...@@ -4906,6 +4909,18 @@ MaybeLocal<v8::Context> Isolate::GetContextFromRecorderContextId( ...@@ -4906,6 +4909,18 @@ MaybeLocal<v8::Context> Isolate::GetContextFromRecorderContextId(
return result->second.Get(reinterpret_cast<v8::Isolate*>(this)); return result->second.Get(reinterpret_cast<v8::Isolate*>(this));
} }
void Isolate::UpdateLongTaskStats() {
if (last_long_task_stats_counter_ != isolate_data_.long_task_stats_counter_) {
last_long_task_stats_counter_ = isolate_data_.long_task_stats_counter_;
long_task_stats_ = v8::metrics::LongTaskStats{};
}
}
v8::metrics::LongTaskStats* Isolate::GetCurrentLongTaskStats() {
UpdateLongTaskStats();
return &long_task_stats_;
}
void Isolate::RemoveContextIdCallback(const v8::WeakCallbackInfo<void>& data) { void Isolate::RemoveContextIdCallback(const v8::WeakCallbackInfo<void>& data) {
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate()); Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
uintptr_t context_id = reinterpret_cast<uintptr_t>(data.GetParameter()); uintptr_t context_id = reinterpret_cast<uintptr_t>(data.GetParameter());
......
...@@ -1704,6 +1704,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -1704,6 +1704,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
MaybeLocal<v8::Context> GetContextFromRecorderContextId( MaybeLocal<v8::Context> GetContextFromRecorderContextId(
v8::metrics::Recorder::ContextId id); v8::metrics::Recorder::ContextId id);
void UpdateLongTaskStats();
v8::metrics::LongTaskStats* GetCurrentLongTaskStats();
LocalIsolate* main_thread_local_isolate() { LocalIsolate* main_thread_local_isolate() {
return main_thread_local_isolate_.get(); return main_thread_local_isolate_.get();
} }
...@@ -2033,6 +2036,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -2033,6 +2036,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>>> Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>>>
recorder_context_id_map_; recorder_context_id_map_;
size_t last_long_task_stats_counter_;
v8::metrics::LongTaskStats long_task_stats_;
std::vector<Object> startup_object_cache_; std::vector<Object> startup_object_cache_;
// Used during builtins compilation to build the builtins constants table, // Used during builtins compilation to build the builtins constants table,
......
...@@ -87,6 +87,15 @@ GCTracer::Scope::~Scope() { ...@@ -87,6 +87,15 @@ GCTracer::Scope::~Scope() {
if (thread_kind_ == ThreadKind::kMain) { if (thread_kind_ == ThreadKind::kMain) {
DCHECK_EQ(tracer_->heap_->isolate()->thread_id(), ThreadId::Current()); DCHECK_EQ(tracer_->heap_->isolate()->thread_id(), ThreadId::Current());
tracer_->AddScopeSample(scope_, duration_ms); tracer_->AddScopeSample(scope_, duration_ms);
if (scope_ == ScopeId::MC_INCREMENTAL ||
scope_ == ScopeId::MC_INCREMENTAL_START ||
scope_ == MC_INCREMENTAL_FINALIZE) {
auto* long_task_stats =
tracer_->heap_->isolate()->GetCurrentLongTaskStats();
long_task_stats->gc_full_incremental_wall_clock_duration_us +=
static_cast<int64_t>(duration_ms *
base::Time::kMicrosecondsPerMillisecond);
}
} else { } else {
tracer_->AddScopeSampleBackground(scope_, duration_ms); tracer_->AddScopeSampleBackground(scope_, duration_ms);
} }
...@@ -342,6 +351,9 @@ void GCTracer::Stop(GarbageCollector collector) { ...@@ -342,6 +351,9 @@ void GCTracer::Stop(GarbageCollector collector) {
AddAllocation(current_.end_time); AddAllocation(current_.end_time);
double duration = current_.end_time - current_.start_time; double duration = current_.end_time - current_.start_time;
int64_t duration_us =
static_cast<int64_t>(duration * base::Time::kMicrosecondsPerMillisecond);
auto* long_task_stats = heap_->isolate()->GetCurrentLongTaskStats();
switch (current_.type) { switch (current_.type) {
case Event::SCAVENGER: case Event::SCAVENGER:
...@@ -351,6 +363,7 @@ void GCTracer::Stop(GarbageCollector collector) { ...@@ -351,6 +363,7 @@ void GCTracer::Stop(GarbageCollector collector) {
recorded_minor_gcs_survived_.Push( recorded_minor_gcs_survived_.Push(
MakeBytesAndDuration(current_.survived_young_object_size, duration)); MakeBytesAndDuration(current_.survived_young_object_size, duration));
FetchBackgroundMinorGCCounters(); FetchBackgroundMinorGCCounters();
long_task_stats->gc_young_wall_clock_duration_us += duration_us;
break; break;
case Event::INCREMENTAL_MARK_COMPACTOR: case Event::INCREMENTAL_MARK_COMPACTOR:
current_.incremental_marking_bytes = incremental_marking_bytes_; current_.incremental_marking_bytes = incremental_marking_bytes_;
...@@ -370,6 +383,7 @@ void GCTracer::Stop(GarbageCollector collector) { ...@@ -370,6 +383,7 @@ void GCTracer::Stop(GarbageCollector collector) {
ResetIncrementalMarkingCounters(); ResetIncrementalMarkingCounters();
combined_mark_compact_speed_cache_ = 0.0; combined_mark_compact_speed_cache_ = 0.0;
FetchBackgroundMarkCompactCounters(); FetchBackgroundMarkCompactCounters();
long_task_stats->gc_full_atomic_wall_clock_duration_us += duration_us;
break; break;
case Event::MARK_COMPACTOR: case Event::MARK_COMPACTOR:
DCHECK_EQ(0u, current_.incremental_marking_bytes); DCHECK_EQ(0u, current_.incremental_marking_bytes);
...@@ -382,6 +396,7 @@ void GCTracer::Stop(GarbageCollector collector) { ...@@ -382,6 +396,7 @@ void GCTracer::Stop(GarbageCollector collector) {
ResetIncrementalMarkingCounters(); ResetIncrementalMarkingCounters();
combined_mark_compact_speed_cache_ = 0.0; combined_mark_compact_speed_cache_ = 0.0;
FetchBackgroundMarkCompactCounters(); FetchBackgroundMarkCompactCounters();
long_task_stats->gc_full_atomic_wall_clock_duration_us += duration_us;
break; break;
case Event::START: case Event::START:
UNREACHABLE(); UNREACHABLE();
......
...@@ -7351,6 +7351,18 @@ TEST(Regress10900) { ...@@ -7351,6 +7351,18 @@ TEST(Regress10900) {
CcTest::CollectAllAvailableGarbage(); CcTest::CollectAllAvailableGarbage();
} }
namespace {
void GenerateGarbage() {
const char* source =
"let roots = [];"
"for (let i = 0; i < 100; i++) roots.push(new Array(1000).fill(0));"
"roots.push(new Array(1000000).fill(0));"
"roots;";
CompileRun(source);
}
} // anonymous namespace
TEST(Regress11181) { TEST(Regress11181) {
FLAG_always_compact = true; FLAG_always_compact = true;
CcTest::InitializeVM(); CcTest::InitializeVM();
...@@ -7358,16 +7370,70 @@ TEST(Regress11181) { ...@@ -7358,16 +7370,70 @@ TEST(Regress11181) {
v8::tracing::TracingCategoryObserver::ENABLED_BY_NATIVE, v8::tracing::TracingCategoryObserver::ENABLED_BY_NATIVE,
std::memory_order_relaxed); std::memory_order_relaxed);
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
const char* source = GenerateGarbage();
"let roots = [];"
"for (let i = 0; i < 100; i++) roots.push(new Array(1000).fill(0));"
"roots.push(new Array(1000000).fill(0));"
"roots;";
CompileRun(source);
CcTest::CollectAllAvailableGarbage(); CcTest::CollectAllAvailableGarbage();
TracingFlags::runtime_stats.store(0, std::memory_order_relaxed); TracingFlags::runtime_stats.store(0, std::memory_order_relaxed);
} }
TEST(LongTaskStatsFullAtomic) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(CcTest::isolate());
GenerateGarbage();
v8::metrics::LongTaskStats::Reset(isolate);
CHECK_EQ(0u, v8::metrics::LongTaskStats::Get(isolate)
.gc_full_atomic_wall_clock_duration_us);
for (int i = 0; i < 10; ++i) {
CcTest::CollectAllAvailableGarbage();
}
CHECK_LT(0u, v8::metrics::LongTaskStats::Get(isolate)
.gc_full_atomic_wall_clock_duration_us);
v8::metrics::LongTaskStats::Reset(isolate);
CHECK_EQ(0u, v8::metrics::LongTaskStats::Get(isolate)
.gc_full_atomic_wall_clock_duration_us);
}
TEST(LongTaskStatsFullIncremental) {
if (!FLAG_incremental_marking) return;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(CcTest::isolate());
GenerateGarbage();
v8::metrics::LongTaskStats::Reset(isolate);
CHECK_EQ(0u, v8::metrics::LongTaskStats::Get(isolate)
.gc_full_incremental_wall_clock_duration_us);
for (int i = 0; i < 10; ++i) {
heap::SimulateIncrementalMarking(CcTest::heap());
CcTest::CollectAllAvailableGarbage();
}
CHECK_LT(0u, v8::metrics::LongTaskStats::Get(isolate)
.gc_full_incremental_wall_clock_duration_us);
v8::metrics::LongTaskStats::Reset(isolate);
CHECK_EQ(0u, v8::metrics::LongTaskStats::Get(isolate)
.gc_full_incremental_wall_clock_duration_us);
}
TEST(LongTaskStatsYoung) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(CcTest::isolate());
GenerateGarbage();
v8::metrics::LongTaskStats::Reset(isolate);
CHECK_EQ(
0u,
v8::metrics::LongTaskStats::Get(isolate).gc_young_wall_clock_duration_us);
for (int i = 0; i < 10; ++i) {
CcTest::CollectGarbage(NEW_SPACE);
}
CHECK_LT(
0u,
v8::metrics::LongTaskStats::Get(isolate).gc_young_wall_clock_duration_us);
v8::metrics::LongTaskStats::Reset(isolate);
CHECK_EQ(
0u,
v8::metrics::LongTaskStats::Get(isolate).gc_young_wall_clock_duration_us);
}
} // namespace heap } // namespace heap
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
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