blob: 21e4cdddb184c2110351cf930017af2511f3f20a [file] [log] [blame]
// Copyright 2023 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/js_profiler/profiler.h"
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include "base/logging.h"
#include "cobalt/js_profiler/profiler_trace_wrapper.h"
#include "cobalt/web/cache_utils.h"
#include "cobalt/web/context.h"
#include "cobalt/web/dom_exception.h"
#include "cobalt/web/environment_settings_helper.h"
namespace cobalt {
namespace js_profiler {
Profiler::Profiler(script::EnvironmentSettings* settings,
ProfilerInitOptions options,
script::ExceptionState* exception_state)
: stopped_(false), time_origin_{base::TimeTicks::Now()} {
profiler_group_ = ProfilerGroup::From(settings);
profiler_id_ = profiler_group_->NextProfilerId();
const base::TimeDelta sample_interval =
base::Milliseconds(options.sample_interval());
int64_t sample_interval_us = sample_interval.InMicroseconds();
if (sample_interval_us < 0 ||
sample_interval_us > std::numeric_limits<int>::max()) {
sample_interval_us = 0;
}
int effective_sample_interval_ms =
static_cast<int>(sample_interval.InMilliseconds());
if (effective_sample_interval_ms % Profiler::kBaseSampleIntervalMs != 0 ||
effective_sample_interval_ms == 0) {
effective_sample_interval_ms +=
(Profiler::kBaseSampleIntervalMs -
effective_sample_interval_ms % Profiler::kBaseSampleIntervalMs);
}
sample_interval_ = effective_sample_interval_ms;
SB_LOG(INFO) << "[PROFILER] START " + profiler_id_;
auto status = profiler_group_->ProfilerStart(
this, settings,
v8::CpuProfilingOptions(v8::kLeafNodeLineNumbers,
options.max_buffer_size(), sample_interval_us));
if (status == v8::CpuProfilingStatus::kAlreadyStarted) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
"Profiler Already started", exception_state);
} else if (status == v8::CpuProfilingStatus::kErrorTooManyProfilers) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
"Too Many Profilers", exception_state);
}
}
void Profiler::AddEventListener(
script::EnvironmentSettings* environment_settings, const std::string& name,
const Profiler::SampleBufferFullCallbackHolder& holder) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (name != base::Tokens::samplebufferfull()) {
return;
}
auto* global_wrappable = web::get_global_wrappable(environment_settings);
SampleBufferFullCallbackReference* token_callback =
new SampleBufferFullCallbackReference(global_wrappable, holder);
listeners_.push_back(
std::unique_ptr<SampleBufferFullCallbackReference>(token_callback));
}
void Profiler::DispatchSampleBufferFullEvent() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
(*it)->value().Run();
}
listeners_.clear();
}
Profiler::ProfilerTracePromise Profiler::Stop(
script::EnvironmentSettings* environment_settings) {
SB_LOG(INFO) << "[PROFILER] STOPPING " + profiler_id_;
script::HandlePromiseWrappable promise =
web::get_script_value_factory(environment_settings)
->CreateInterfacePromise<scoped_refptr<ProfilerTraceWrapper>>();
if (!stopped()) {
stopped_ = true;
auto* global_wrappable = web::get_global_wrappable(environment_settings);
auto* context = web::get_context(environment_settings);
std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference(
new script::ValuePromiseWrappable::Reference(global_wrappable,
promise));
context->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&Profiler::PerformStop, base::Unretained(this),
profiler_group_, std::move(promise_reference),
std::move(time_origin_), std::move(profiler_id_)));
} else {
promise->Reject(new web::DOMException(web::DOMException::kInvalidStateErr,
"Profiler already stopped."));
}
return promise;
}
void Profiler::PerformStop(
ProfilerGroup* profiler_group,
std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference,
base::TimeTicks time_origin, std::string profiler_id) {
SB_LOG(INFO) << "[PROFILER] STOPPED " + profiler_id_;
auto trace = profiler_group->ProfilerStop(this);
scoped_refptr<ProfilerTraceWrapper> result(new ProfilerTraceWrapper(trace));
promise_reference->value().Resolve(result);
}
void Profiler::Cancel() {
if (!stopped_) {
stopped_ = true;
profiler_group_->ProfilerStop(this);
}
profiler_group_ = nullptr;
}
} // namespace js_profiler
} // namespace cobalt