blob: 4e8991437dfafa5d6d04b9847126f598bc07be24 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/timing/profiler_group.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/bindings/core/v8/profiler_trace_builder.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/timing/profiler.h"
#include "third_party/blink/renderer/core/timing/profiler_init_options.h"
#include "third_party/blink/renderer/core/timing/profiler_trace.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "v8/include/v8-profiler.h"
#include "v8/include/v8.h"
namespace blink {
namespace {
#if defined(OS_WIN)
// On Windows, assume we have the coarsest possible timer.
static constexpr int kBaseSampleIntervalMs =
base::Time::kMinLowResolutionThresholdMs;
#else
// Default to a 10ms base sampling interval on other platforms.
// TODO(acomminos): Reevaluate based on empirical overhead.
static constexpr int kBaseSampleIntervalMs = 10;
#endif // defined(OS_WIN)
} // namespace
ProfilerGroup* ProfilerGroup::From(v8::Isolate* isolate) {
auto* isolate_data = V8PerIsolateData::From(isolate);
auto* profiler_group =
reinterpret_cast<ProfilerGroup*>(isolate_data->ProfilerGroup());
if (!profiler_group) {
profiler_group = MakeGarbageCollected<ProfilerGroup>(isolate);
isolate_data->SetProfilerGroup(profiler_group);
}
return profiler_group;
}
ProfilerGroup::ProfilerGroup(v8::Isolate* isolate)
: isolate_(isolate),
cpu_profiler_(nullptr),
next_profiler_id_(0),
num_active_profilers_(0) {
DCHECK(RuntimeEnabledFeatures::ExperimentalJSProfilerEnabled());
}
Profiler* ProfilerGroup::CreateProfiler(ScriptState* script_state,
const ProfilerInitOptions& init_options,
base::TimeTicks time_origin,
ExceptionState& exception_state) {
DCHECK_EQ(script_state->GetIsolate(), isolate_);
DCHECK(init_options.hasSampleInterval());
if (init_options.sampleInterval() < 0) {
exception_state.ThrowRangeError("Expected non-negative sample interval");
return nullptr;
}
if (!cpu_profiler_)
InitV8Profiler();
DCHECK(cpu_profiler_);
// TODO(acomminos): Requires support in V8 for subsampling intervals
int sample_interval_ms = kBaseSampleIntervalMs;
String profiler_id = NextProfilerId();
v8::CpuProfilingOptions options(
v8::kLeafNodeLineNumbers, init_options.hasMaxBufferSize()
? init_options.maxBufferSize()
: v8::CpuProfilingOptions::kNoSampleLimit);
cpu_profiler_->StartProfiling(V8String(isolate_, profiler_id), options);
// Limit non-crossorigin script frames to the origin that started the
// profiler.
auto* execution_context = ExecutionContext::From(script_state);
scoped_refptr<const SecurityOrigin> source_origin(
execution_context->GetSecurityOrigin());
auto* profiler = MakeGarbageCollected<Profiler>(
this, profiler_id, sample_interval_ms, source_origin, time_origin);
profilers_.insert(profiler);
num_active_profilers_++;
return profiler;
}
ProfilerGroup::~ProfilerGroup() {
// v8::CpuProfiler should have been torn down by WillBeDestroyed.
DCHECK(!cpu_profiler_);
}
void ProfilerGroup::WillBeDestroyed() {
for (auto& profiler : profilers_) {
DCHECK(profiler);
profiler->Dispose();
DCHECK(profiler->stopped());
}
if (cpu_profiler_)
TeardownV8Profiler();
}
void ProfilerGroup::Trace(blink::Visitor* visitor) {
visitor->Trace(profilers_);
V8PerIsolateData::GarbageCollectedData::Trace(visitor);
}
void ProfilerGroup::InitV8Profiler() {
DCHECK(!cpu_profiler_);
DCHECK_EQ(num_active_profilers_, 0);
cpu_profiler_ = v8::CpuProfiler::New(isolate_, v8::kStandardNaming);
#if defined(OS_WIN)
// Avoid busy-waiting on Windows, clamping us to the system clock interrupt
// interval in the worst case.
cpu_profiler_->SetUsePreciseSampling(false);
#endif // defined(OS_WIN)
cpu_profiler_->SetSamplingInterval(kBaseSampleIntervalMs *
base::Time::kMicrosecondsPerMillisecond);
}
void ProfilerGroup::TeardownV8Profiler() {
DCHECK(cpu_profiler_);
DCHECK_EQ(num_active_profilers_, 0);
cpu_profiler_->Dispose();
cpu_profiler_ = nullptr;
}
void ProfilerGroup::StopProfiler(ScriptState* script_state,
Profiler* profiler,
ScriptPromiseResolver* resolver) {
DCHECK(cpu_profiler_);
DCHECK(!profiler->stopped());
v8::Local<v8::String> profiler_id =
V8String(isolate_, profiler->ProfilerId());
auto* profile = cpu_profiler_->StopProfiling(profiler_id);
auto* trace = ProfilerTraceBuilder::FromProfile(
script_state, profile, profiler->SourceOrigin(), profiler->TimeOrigin());
resolver->Resolve(trace);
profile->Delete();
if (--num_active_profilers_ == 0)
TeardownV8Profiler();
}
void ProfilerGroup::CancelProfiler(Profiler* profiler) {
DCHECK(cpu_profiler_);
DCHECK(!profiler->stopped());
v8::HandleScope scope(isolate_);
v8::Local<v8::String> profiler_id =
V8String(isolate_, profiler->ProfilerId());
auto* profile = cpu_profiler_->StopProfiling(profiler_id);
profile->Delete();
if (--num_active_profilers_ == 0)
TeardownV8Profiler();
}
String ProfilerGroup::NextProfilerId() {
auto id = String::Format("blink::Profiler[%d]", next_profiler_id_);
++next_profiler_id_;
return id;
}
} // namespace blink