[ukm] Add framework for collecting event-based metrics
Add a framework for collecting event-based metrics like UKMs in V8 that is independent of the actual implementation. Design doc: https://docs.google.com/document/d/1vCZQCh4B05isqwJOwTPv7WqcnVp4KJITMgsHSBg35ZI/ R=ulan@chromium.org Bug: chromium:1101749 Change-Id: If3a5b954d1f0bcee4e06a03467b651feae378a5f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2288231 Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#69098}
This commit is contained in:
parent
1250fd59aa
commit
367da30543
3
BUILD.gn
3
BUILD.gn
@ -2221,6 +2221,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"include/v8-inspector-protocol.h",
|
||||
"include/v8-inspector.h",
|
||||
"include/v8-internal.h",
|
||||
"include/v8-metrics.h",
|
||||
"include/v8-platform.h",
|
||||
"include/v8-profiler.h",
|
||||
"include/v8-util.h",
|
||||
@ -2721,6 +2722,8 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/logging/log-utils.h",
|
||||
"src/logging/log.cc",
|
||||
"src/logging/log.h",
|
||||
"src/logging/metrics.cc",
|
||||
"src/logging/metrics.h",
|
||||
"src/logging/off-thread-logger.h",
|
||||
"src/logging/tracing-flags.cc",
|
||||
"src/logging/tracing-flags.h",
|
||||
|
105
include/v8-metrics.h
Normal file
105
include/v8-metrics.h
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#ifndef V8_METRICS_H_
|
||||
#define V8_METRICS_H_
|
||||
|
||||
#include "v8.h" // NOLINT(build/include_directory)
|
||||
|
||||
namespace v8 {
|
||||
namespace metrics {
|
||||
|
||||
struct WasmModuleDecoded {
|
||||
bool async = false;
|
||||
bool streamed = false;
|
||||
bool success = false;
|
||||
size_t module_size_in_bytes = 0;
|
||||
size_t function_count = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
};
|
||||
|
||||
struct WasmModuleCompiled {
|
||||
bool async = false;
|
||||
bool streamed = false;
|
||||
bool cached = false;
|
||||
bool deserialized = false;
|
||||
bool lazy = false;
|
||||
bool success = false;
|
||||
size_t code_size_in_bytes = 0;
|
||||
size_t liftoff_bailout_count = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
};
|
||||
|
||||
struct WasmModuleInstantiated {
|
||||
bool async = false;
|
||||
bool success = false;
|
||||
size_t imported_function_count = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
};
|
||||
|
||||
struct WasmModuleTieredUp {
|
||||
bool lazy = false;
|
||||
size_t code_size_in_bytes = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
};
|
||||
|
||||
struct WasmModulesPerIsolate {
|
||||
size_t count = 0;
|
||||
};
|
||||
|
||||
#define V8_MAIN_THREAD_METRICS_EVENTS(V) \
|
||||
V(WasmModuleDecoded) \
|
||||
V(WasmModuleCompiled) \
|
||||
V(WasmModuleInstantiated) \
|
||||
V(WasmModuleTieredUp)
|
||||
|
||||
#define V8_THREAD_SAFE_METRICS_EVENTS(V) V(WasmModulesPerIsolate)
|
||||
|
||||
/**
|
||||
* This class serves as a base class for recording event-based metrics in V8.
|
||||
* There a two kinds of metrics, those which are expected to be thread-safe and
|
||||
* whose implementation is required to fulfill this requirement and those whose
|
||||
* implementation does not have that requirement and only needs to be
|
||||
* executable on the main thread. If such an event is triggered from a
|
||||
* background thread, it will be delayed and executed by the foreground task
|
||||
* runner.
|
||||
*
|
||||
* The thread-safe events are listed in the V8_THREAD_SAFE_METRICS_EVENTS
|
||||
* macro above while the main thread event are listed in
|
||||
* V8_MAIN_THREAD_METRICS_EVENTS above. For the former, a virtual method
|
||||
* AddMainThreadEvent(const E& event, v8::Context::Token token) will be
|
||||
* generated and for the latter AddThreadSafeEvent(const E& event).
|
||||
*
|
||||
* Thread-safe events are not allowed to access the context and therefore do
|
||||
* not carry a context token with them. These tokens can be generated from
|
||||
* contexts using GetToken() and the token will be valid as long as the isolate
|
||||
* and the context live. It is not guaranteed that the token will still resolve
|
||||
* to a valid context using v8::Context::GetByToken() at the time the metric is
|
||||
* recorded. In this case, an empty handle will be returned.
|
||||
*
|
||||
* The embedder is expected to call v8::Isolate::SetMetricsRecorder()
|
||||
* providing its implementation and have the virtual methods overwritten
|
||||
* for the events it cares about.
|
||||
*/
|
||||
class Recorder {
|
||||
public:
|
||||
virtual ~Recorder() = default;
|
||||
|
||||
#define ADD_MAIN_THREAD_EVENT(E) \
|
||||
virtual void AddMainThreadEvent(const E& event, v8::Context::Token token) {}
|
||||
V8_MAIN_THREAD_METRICS_EVENTS(ADD_MAIN_THREAD_EVENT)
|
||||
#undef ADD_MAIN_THREAD_EVENT
|
||||
|
||||
#define ADD_THREAD_SAFE_EVENT(E) \
|
||||
virtual void AddThreadSafeEvent(const E& event) {}
|
||||
V8_THREAD_SAFE_METRICS_EVENTS(ADD_THREAD_SAFE_EVENT)
|
||||
#undef ADD_THREAD_SAFE_EVENT
|
||||
|
||||
virtual void NotifyIsolateDisposal() {}
|
||||
};
|
||||
|
||||
} // namespace metrics
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_METRICS_H_
|
43
include/v8.h
43
include/v8.h
@ -149,6 +149,10 @@ class StreamingDecoder;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
namespace metrics {
|
||||
class Recorder;
|
||||
} // namespace metrics
|
||||
|
||||
namespace debug {
|
||||
class ConsoleCallArguments;
|
||||
} // namespace debug
|
||||
@ -9139,6 +9143,18 @@ class V8_EXPORT Isolate {
|
||||
void SetCreateHistogramFunction(CreateHistogramCallback);
|
||||
void SetAddHistogramSampleFunction(AddHistogramSampleCallback);
|
||||
|
||||
/**
|
||||
* Enables the host application to provide a mechanism for recording
|
||||
* event based metrics. In order to use this interface
|
||||
* include/v8-metrics.h
|
||||
* needs to be included and the recorder needs to be derived from the
|
||||
* Recorder base class defined there.
|
||||
* This method can only be called once per isolate and must happen during
|
||||
* isolate initialization before background threads are spawned.
|
||||
*/
|
||||
void SetMetricsRecorder(
|
||||
const std::shared_ptr<metrics::Recorder>& metrics_recorder);
|
||||
|
||||
/**
|
||||
* Enables the host application to provide a mechanism for recording a
|
||||
* predefined set of data as crash keys to be used in postmortem debugging in
|
||||
@ -10495,6 +10511,33 @@ class V8_EXPORT Context {
|
||||
const BackupIncumbentScope* prev_ = nullptr;
|
||||
};
|
||||
|
||||
// A unique token for a context in this Isolate.
|
||||
// It is guaranteed to not be reused throughout the lifetime of the Isolate.
|
||||
class Token {
|
||||
public:
|
||||
Token() : token_(kEmptyToken) {}
|
||||
|
||||
bool IsEmpty() const { return token_ == kEmptyToken; }
|
||||
static const Token Empty() { return Token{kEmptyToken}; }
|
||||
|
||||
bool operator==(const Token& other) const { return token_ == other.token_; }
|
||||
bool operator!=(const Token& other) const { return token_ != other.token_; }
|
||||
|
||||
private:
|
||||
friend class Context;
|
||||
friend class internal::Isolate;
|
||||
|
||||
explicit Token(uintptr_t token) : token_(token) {}
|
||||
|
||||
static constexpr uintptr_t kEmptyToken = 0;
|
||||
uintptr_t token_;
|
||||
};
|
||||
|
||||
// Return the context with the given token or an empty handle if the context
|
||||
// was already garbage collected.
|
||||
static MaybeLocal<Context> GetByToken(Isolate* isolate, Token token);
|
||||
v8::Context::Token GetToken();
|
||||
|
||||
private:
|
||||
friend class Value;
|
||||
friend class Script;
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "src/json/json-parser.h"
|
||||
#include "src/json/json-stringifier.h"
|
||||
#include "src/logging/counters.h"
|
||||
#include "src/logging/metrics.h"
|
||||
#include "src/logging/tracing-flags.h"
|
||||
#include "src/numbers/conversions-inl.h"
|
||||
#include "src/objects/api-callbacks.h"
|
||||
@ -6149,6 +6150,19 @@ void Context::SetContinuationPreservedEmbedderData(Local<Value> data) {
|
||||
*i::Handle<i::HeapObject>::cast(Utils::OpenHandle(*data)));
|
||||
}
|
||||
|
||||
MaybeLocal<Context> Context::GetByToken(Isolate* isolate,
|
||||
Context::Token token) {
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
return i_isolate->GetContextFromToken(token);
|
||||
}
|
||||
|
||||
v8::Context::Token Context::GetToken() {
|
||||
i::Handle<i::Context> context = Utils::OpenHandle(this);
|
||||
i::Isolate* isolate = context->GetIsolate();
|
||||
return isolate->GetOrRegisterContextToken(
|
||||
handle(context->native_context(), isolate));
|
||||
}
|
||||
|
||||
namespace {
|
||||
i::Address* GetSerializedDataFromFixedArray(i::Isolate* isolate,
|
||||
i::FixedArray list, size_t index) {
|
||||
@ -8783,6 +8797,12 @@ void Isolate::SetAddHistogramSampleFunction(
|
||||
->SetAddHistogramSampleFunction(callback);
|
||||
}
|
||||
|
||||
void Isolate::SetMetricsRecorder(
|
||||
const std::shared_ptr<metrics::Recorder>& metrics_recorder) {
|
||||
reinterpret_cast<i::Isolate*>(this)->metrics_recorder()->SetRecorder(
|
||||
metrics_recorder);
|
||||
}
|
||||
|
||||
void Isolate::SetAddCrashKeyCallback(AddCrashKeyCallback callback) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
isolate->SetAddCrashKeyCallback(callback);
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "src/libsampler/sampler.h"
|
||||
#include "src/logging/counters.h"
|
||||
#include "src/logging/log.h"
|
||||
#include "src/logging/metrics.h"
|
||||
#include "src/numbers/hash-seed-inl.h"
|
||||
#include "src/objects/backing-store.h"
|
||||
#include "src/objects/elements.h"
|
||||
@ -3037,6 +3038,8 @@ void Isolate::Deinit() {
|
||||
heap_profiler()->StopSamplingHeapProfiler();
|
||||
}
|
||||
|
||||
metrics_recorder_->NotifyIsolateDisposal();
|
||||
|
||||
#if defined(V8_OS_WIN64)
|
||||
if (win64_unwindinfo::CanRegisterUnwindInfoForNonABICompliantCodeRange() &&
|
||||
heap()->memory_allocator() && RequiresCodeRange()) {
|
||||
@ -3511,6 +3514,8 @@ bool Isolate::Init(ReadOnlyDeserializer* read_only_deserializer,
|
||||
// Enable logging before setting up the heap
|
||||
logger_->SetUp(this);
|
||||
|
||||
metrics_recorder_ = std::make_shared<metrics::Recorder>(this);
|
||||
|
||||
{ // NOLINT
|
||||
// Ensure that the thread has a valid stack guard. The v8::Locker object
|
||||
// will ensure this too, but we don't have to use lockers if we are only
|
||||
@ -4678,6 +4683,42 @@ bool Isolate::RequiresCodeRange() const {
|
||||
return kPlatformRequiresCodeRange && !jitless_;
|
||||
}
|
||||
|
||||
v8::Context::Token Isolate::GetOrRegisterContextToken(
|
||||
Handle<NativeContext> context) {
|
||||
if (serializer_enabled_) return v8::Context::Token::Empty();
|
||||
i::Object token = context->context_token();
|
||||
if (token.IsNullOrUndefined()) {
|
||||
CHECK_LT(last_context_token_, i::Smi::kMaxValue);
|
||||
context->set_context_token(i::Smi::FromIntptr(++last_context_token_));
|
||||
v8::HandleScope handle_scope(reinterpret_cast<v8::Isolate*>(this));
|
||||
auto result = context_token_map_.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(last_context_token_),
|
||||
std::forward_as_tuple(reinterpret_cast<v8::Isolate*>(this),
|
||||
ToApiHandle<v8::Context>(context)));
|
||||
result.first->second.SetWeak(reinterpret_cast<void*>(last_context_token_),
|
||||
RemoveContextTokenCallback,
|
||||
v8::WeakCallbackType::kParameter);
|
||||
return v8::Context::Token(last_context_token_);
|
||||
} else {
|
||||
DCHECK(token.IsSmi());
|
||||
return v8::Context::Token(static_cast<uintptr_t>(i::Smi::ToInt(token)));
|
||||
}
|
||||
}
|
||||
|
||||
MaybeLocal<v8::Context> Isolate::GetContextFromToken(v8::Context::Token token) {
|
||||
auto result = context_token_map_.find(token.token_);
|
||||
if (result == context_token_map_.end() || result->second.IsEmpty())
|
||||
return MaybeLocal<v8::Context>();
|
||||
return result->second.Get(reinterpret_cast<v8::Isolate*>(this));
|
||||
}
|
||||
|
||||
void Isolate::RemoveContextTokenCallback(
|
||||
const v8::WeakCallbackInfo<void>& data) {
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
|
||||
uintptr_t token = reinterpret_cast<uintptr_t>(data.GetParameter());
|
||||
isolate->context_token_map_.erase(token);
|
||||
}
|
||||
|
||||
// |chunk| is either a Page or an executable LargePage.
|
||||
void Isolate::RemoveCodeMemoryChunk(MemoryChunk* chunk) {
|
||||
// We only keep track of individual code pages/allocations if we are on arm32,
|
||||
|
@ -124,6 +124,10 @@ namespace win64_unwindinfo {
|
||||
class BuiltinUnwindInfo;
|
||||
}
|
||||
|
||||
namespace metrics {
|
||||
class Recorder;
|
||||
} // namespace metrics
|
||||
|
||||
#define RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate) \
|
||||
do { \
|
||||
Isolate* __isolate__ = (isolate); \
|
||||
@ -926,6 +930,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
DCHECK_NOT_NULL(async_counters_.get());
|
||||
return async_counters_;
|
||||
}
|
||||
const std::shared_ptr<metrics::Recorder>& metrics_recorder() {
|
||||
return metrics_recorder_;
|
||||
}
|
||||
RuntimeProfiler* runtime_profiler() { return runtime_profiler_; }
|
||||
CompilationCache* compilation_cache() { return compilation_cache_; }
|
||||
Logger* logger() {
|
||||
@ -1542,6 +1549,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
static Address load_from_stack_count_address(const char* function_name);
|
||||
static Address store_to_stack_count_address(const char* function_name);
|
||||
|
||||
v8::Context::Token GetOrRegisterContextToken(Handle<NativeContext> context);
|
||||
MaybeLocal<v8::Context> GetContextFromToken(v8::Context::Token token);
|
||||
|
||||
private:
|
||||
explicit Isolate(std::unique_ptr<IsolateAllocator> isolate_allocator);
|
||||
~Isolate();
|
||||
@ -1554,6 +1564,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
void InitializeCodeRanges();
|
||||
void AddCodeMemoryRange(MemoryRange range);
|
||||
|
||||
static void RemoveContextTokenCallback(
|
||||
const v8::WeakCallbackInfo<void>& data);
|
||||
|
||||
class ThreadDataTable {
|
||||
public:
|
||||
ThreadDataTable() = default;
|
||||
@ -1813,6 +1826,13 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
|
||||
v8::Isolate::UseCounterCallback use_counter_callback_ = nullptr;
|
||||
|
||||
std::shared_ptr<metrics::Recorder> metrics_recorder_;
|
||||
uintptr_t last_context_token_ = 0;
|
||||
std::unordered_map<
|
||||
uintptr_t,
|
||||
Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>>>
|
||||
context_token_map_;
|
||||
|
||||
std::vector<Object> startup_object_cache_;
|
||||
|
||||
// Used during builtins compilation to build the builtins constants table,
|
||||
|
63
src/logging/metrics.cc
Normal file
63
src/logging/metrics.cc
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#include "src/logging/metrics.h"
|
||||
|
||||
#include "include/v8-platform.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace metrics {
|
||||
|
||||
class Recorder::Task : public v8::Task {
|
||||
public:
|
||||
explicit Task(const std::shared_ptr<Recorder>& recorder)
|
||||
: recorder_(recorder) {}
|
||||
|
||||
void Run() override {
|
||||
std::queue<std::unique_ptr<Recorder::DelayedEventBase>> delayed_events;
|
||||
{
|
||||
base::MutexGuard lock_scope(&recorder_->lock_);
|
||||
delayed_events.swap(recorder_->delayed_events_);
|
||||
}
|
||||
while (!delayed_events.empty()) {
|
||||
delayed_events.front()->Run(recorder_);
|
||||
delayed_events.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Recorder> recorder_;
|
||||
};
|
||||
|
||||
Recorder::Recorder(Isolate* isolate)
|
||||
: foreground_task_runner_(V8::GetCurrentPlatform()->GetForegroundTaskRunner(
|
||||
reinterpret_cast<v8::Isolate*>(isolate))),
|
||||
embedder_recorder_(nullptr) {}
|
||||
|
||||
void Recorder::SetRecorder(
|
||||
const std::shared_ptr<v8::metrics::Recorder>& embedder_recorder) {
|
||||
CHECK_NULL(embedder_recorder_);
|
||||
embedder_recorder_ = embedder_recorder;
|
||||
}
|
||||
|
||||
void Recorder::NotifyIsolateDisposal() {
|
||||
if (embedder_recorder_) {
|
||||
embedder_recorder_->NotifyIsolateDisposal();
|
||||
}
|
||||
}
|
||||
|
||||
void Recorder::Delay(std::unique_ptr<Recorder::DelayedEventBase>&& event) {
|
||||
base::MutexGuard lock_scope(&lock_);
|
||||
bool was_empty = delayed_events_.empty();
|
||||
delayed_events_.push(std::move(event));
|
||||
if (was_empty) {
|
||||
foreground_task_runner_->PostDelayedTask(
|
||||
std::make_unique<Task>(shared_from_this()), 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace metrics
|
||||
} // namespace internal
|
||||
} // namespace v8
|
104
src/logging/metrics.h
Normal file
104
src/logging/metrics.h
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#ifndef V8_LOGGING_METRICS_H_
|
||||
#define V8_LOGGING_METRICS_H_
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "include/v8-metrics.h"
|
||||
#include "src/base/platform/mutex.h"
|
||||
#include "src/base/platform/time.h"
|
||||
#include "src/init/v8.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
class TaskRunner;
|
||||
|
||||
namespace internal {
|
||||
namespace metrics {
|
||||
|
||||
class Recorder : public std::enable_shared_from_this<Recorder> {
|
||||
public:
|
||||
explicit V8_EXPORT_PRIVATE Recorder(Isolate* isolate);
|
||||
|
||||
V8_EXPORT_PRIVATE void SetRecorder(
|
||||
const std::shared_ptr<v8::metrics::Recorder>& embedder_recorder);
|
||||
|
||||
V8_EXPORT_PRIVATE void NotifyIsolateDisposal();
|
||||
|
||||
template <class T>
|
||||
void AddMainThreadEvent(const T& event, v8::Context::Token token) {
|
||||
if (embedder_recorder_)
|
||||
embedder_recorder_->AddMainThreadEvent(event, token);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DelayMainThreadEvent(const T& event, v8::Context::Token token) {
|
||||
if (!embedder_recorder_) return;
|
||||
Delay(std::make_unique<DelayedEvent<T>>(event, token));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void AddThreadSafeEvent(const T& event) {
|
||||
if (embedder_recorder_) embedder_recorder_->AddThreadSafeEvent(event);
|
||||
}
|
||||
|
||||
private:
|
||||
class DelayedEventBase {
|
||||
public:
|
||||
virtual ~DelayedEventBase() = default;
|
||||
|
||||
virtual void Run(const std::shared_ptr<Recorder>& recorder) = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DelayedEvent : public DelayedEventBase {
|
||||
public:
|
||||
DelayedEvent(const T& event, v8::Context::Token token)
|
||||
: event_(event), token_(token) {}
|
||||
|
||||
void Run(const std::shared_ptr<Recorder>& recorder) override {
|
||||
recorder->AddMainThreadEvent(event_, token_);
|
||||
}
|
||||
|
||||
protected:
|
||||
T event_;
|
||||
v8::Context::Token token_;
|
||||
};
|
||||
|
||||
class Task;
|
||||
|
||||
V8_EXPORT_PRIVATE void Delay(
|
||||
std::unique_ptr<Recorder::DelayedEventBase>&& event);
|
||||
|
||||
base::Mutex lock_;
|
||||
std::shared_ptr<v8::TaskRunner> foreground_task_runner_;
|
||||
std::shared_ptr<v8::metrics::Recorder> embedder_recorder_;
|
||||
std::queue<std::unique_ptr<DelayedEventBase>> delayed_events_;
|
||||
};
|
||||
|
||||
template <class T, int64_t (base::TimeDelta::*precision)() const =
|
||||
&base::TimeDelta::InMicroseconds>
|
||||
class TimedScope {
|
||||
public:
|
||||
TimedScope(T* event, int64_t T::*time)
|
||||
: event_(event), time_(time), start_time_(base::TimeTicks::Now()) {}
|
||||
~TimedScope() {
|
||||
base::TimeDelta duration = base::TimeTicks::Now() - start_time_;
|
||||
event_->*time_ = (duration.*precision)();
|
||||
}
|
||||
|
||||
private:
|
||||
T* event_;
|
||||
int64_t T::*time_;
|
||||
base::TimeTicks start_time_;
|
||||
};
|
||||
|
||||
} // namespace metrics
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_LOGGING_METRICS_H_
|
@ -91,6 +91,7 @@ enum ContextLookupFlags {
|
||||
V(CALL_ASYNC_MODULE_REJECTED, JSFunction, call_async_module_rejected) \
|
||||
V(CALLSITE_FUNCTION_INDEX, JSFunction, callsite_function) \
|
||||
V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
|
||||
V(CONTEXT_TOKEN, Object, context_token) \
|
||||
V(DATA_PROPERTY_DESCRIPTOR_MAP_INDEX, Map, data_property_descriptor_map) \
|
||||
V(DATA_VIEW_FUN_INDEX, JSFunction, data_view_fun) \
|
||||
V(DATE_FUNCTION_INDEX, JSFunction, date_function) \
|
||||
|
@ -25,14 +25,14 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "test/cctest/test-api.h"
|
||||
|
||||
#include <climits>
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "test/cctest/test-api.h"
|
||||
|
||||
#if V8_OS_POSIX
|
||||
#include <unistd.h> // NOLINT
|
||||
#endif
|
||||
@ -53,6 +53,7 @@
|
||||
#include "src/heap/heap-inl.h"
|
||||
#include "src/heap/incremental-marking.h"
|
||||
#include "src/heap/local-allocator.h"
|
||||
#include "src/logging/metrics.h"
|
||||
#include "src/objects/feedback-vector-inl.h"
|
||||
#include "src/objects/feedback-vector.h"
|
||||
#include "src/objects/hash-table-inl.h"
|
||||
@ -27884,3 +27885,183 @@ TEST(FastApiCalls) {
|
||||
// TODO(mslekova): Add tests for FTI that requires access check.
|
||||
#endif // V8_LITE_MODE
|
||||
}
|
||||
|
||||
THREADED_TEST(GetContextByToken) {
|
||||
using v8::Context;
|
||||
using v8::Local;
|
||||
using v8::MaybeLocal;
|
||||
|
||||
// Set up isolate and context.
|
||||
v8::Isolate* iso = CcTest::isolate();
|
||||
Context::Token original_token;
|
||||
std::vector<Context::Token> tokens;
|
||||
{
|
||||
v8::HandleScope scope(iso);
|
||||
Local<Context> context = Context::New(iso);
|
||||
|
||||
// Ensure that we get a valid token.
|
||||
original_token = context->GetToken();
|
||||
CHECK(!original_token.IsEmpty());
|
||||
|
||||
// Request many tokens to ensure correct growth behavior.
|
||||
for (size_t count = 0; count < 50; ++count) {
|
||||
Local<Context> temp_context = Context::New(iso);
|
||||
tokens.push_back(temp_context->GetToken());
|
||||
}
|
||||
for (const Context::Token& token : tokens) {
|
||||
CHECK(!Context::GetByToken(iso, token).IsEmpty());
|
||||
}
|
||||
|
||||
// Ensure that we can get the context from the token.
|
||||
MaybeLocal<Context> retrieved_context =
|
||||
Context::GetByToken(iso, original_token);
|
||||
CHECK_EQ(context, retrieved_context.ToLocalChecked());
|
||||
|
||||
// Ensure that an empty token returns an empty handle.
|
||||
retrieved_context = Context::GetByToken(iso, Context::Token::Empty());
|
||||
CHECK(retrieved_context.IsEmpty());
|
||||
|
||||
// Ensure that repeated token accesses return the same token.
|
||||
Context::Token new_token = context->GetToken();
|
||||
CHECK_EQ(original_token, new_token);
|
||||
}
|
||||
|
||||
// Invalidate the context and therefore the token.
|
||||
CcTest::PreciseCollectAllGarbage();
|
||||
|
||||
// Ensure that a stale token returns an empty handle.
|
||||
{
|
||||
v8::HandleScope scope(iso);
|
||||
MaybeLocal<Context> retrieved_context =
|
||||
Context::GetByToken(iso, original_token);
|
||||
CHECK(retrieved_context.IsEmpty());
|
||||
|
||||
for (const Context::Token& token : tokens) {
|
||||
CHECK(Context::GetByToken(iso, token).IsEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class MetricsRecorder : public v8::metrics::Recorder {
|
||||
public:
|
||||
v8::Isolate* isolate_;
|
||||
size_t count_ = 0;
|
||||
size_t module_count_ = 0;
|
||||
int64_t time_in_us_ = -1;
|
||||
|
||||
explicit MetricsRecorder(v8::Isolate* isolate) : isolate_(isolate) {}
|
||||
|
||||
void AddMainThreadEvent(const v8::metrics::WasmModuleDecoded& event,
|
||||
Context::Token token) override {
|
||||
if (Context::GetByToken(isolate_, token).IsEmpty()) return;
|
||||
++count_;
|
||||
time_in_us_ = event.wall_clock_time_in_us;
|
||||
}
|
||||
|
||||
void AddThreadSafeEvent(
|
||||
const v8::metrics::WasmModulesPerIsolate& event) override {
|
||||
++count_;
|
||||
module_count_ = event.count;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(TriggerMainThreadMetricsEvent) {
|
||||
using v8::Context;
|
||||
using v8::Local;
|
||||
using v8::MaybeLocal;
|
||||
|
||||
// Set up isolate and context.
|
||||
v8::Isolate* iso = CcTest::isolate();
|
||||
i::Isolate* i_iso = reinterpret_cast<i::Isolate*>(iso);
|
||||
CHECK(i_iso->metrics_recorder());
|
||||
v8::metrics::WasmModuleDecoded event;
|
||||
Context::Token token;
|
||||
std::shared_ptr<MetricsRecorder> recorder =
|
||||
std::make_shared<MetricsRecorder>(iso);
|
||||
iso->SetMetricsRecorder(recorder);
|
||||
{
|
||||
v8::HandleScope scope(iso);
|
||||
Local<Context> context = Context::New(iso);
|
||||
token = context->GetToken();
|
||||
|
||||
// Check that event submission works.
|
||||
{
|
||||
i::metrics::TimedScope<v8::metrics::WasmModuleDecoded> timed_scope(
|
||||
&event, &v8::metrics::WasmModuleDecoded::wall_clock_time_in_us);
|
||||
v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(100));
|
||||
}
|
||||
i_iso->metrics_recorder()->AddMainThreadEvent(event, token);
|
||||
CHECK_EQ(recorder->count_, 1); // Increased.
|
||||
CHECK_GT(recorder->time_in_us_, 100);
|
||||
}
|
||||
|
||||
CcTest::PreciseCollectAllGarbage();
|
||||
|
||||
// Check that event submission doesn't break even if the token is invalid.
|
||||
i_iso->metrics_recorder()->AddMainThreadEvent(event, token);
|
||||
CHECK_EQ(recorder->count_, 1); // Unchanged.
|
||||
}
|
||||
|
||||
TEST(TriggerDelayedMainThreadMetricsEvent) {
|
||||
using v8::Context;
|
||||
using v8::Local;
|
||||
using v8::MaybeLocal;
|
||||
|
||||
// Set up isolate and context.
|
||||
v8::Isolate* iso = CcTest::isolate();
|
||||
i::Isolate* i_iso = reinterpret_cast<i::Isolate*>(iso);
|
||||
CHECK(i_iso->metrics_recorder());
|
||||
v8::metrics::WasmModuleDecoded event;
|
||||
Context::Token token;
|
||||
std::shared_ptr<MetricsRecorder> recorder =
|
||||
std::make_shared<MetricsRecorder>(iso);
|
||||
iso->SetMetricsRecorder(recorder);
|
||||
{
|
||||
v8::HandleScope scope(iso);
|
||||
Local<Context> context = Context::New(iso);
|
||||
token = context->GetToken();
|
||||
|
||||
// Check that event submission works.
|
||||
{
|
||||
i::metrics::TimedScope<v8::metrics::WasmModuleDecoded> timed_scope(
|
||||
&event, &v8::metrics::WasmModuleDecoded::wall_clock_time_in_us);
|
||||
v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(100));
|
||||
}
|
||||
i_iso->metrics_recorder()->DelayMainThreadEvent(event, token);
|
||||
CHECK_EQ(recorder->count_, 0); // Unchanged.
|
||||
CHECK_EQ(recorder->time_in_us_, -1); // Unchanged.
|
||||
v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(1100));
|
||||
v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(), iso);
|
||||
CHECK_EQ(recorder->count_, 1); // Increased.
|
||||
CHECK_GT(recorder->time_in_us_, 100);
|
||||
}
|
||||
|
||||
CcTest::PreciseCollectAllGarbage();
|
||||
|
||||
// Check that event submission doesn't break even if the token is invalid.
|
||||
i_iso->metrics_recorder()->DelayMainThreadEvent(event, token);
|
||||
v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(1100));
|
||||
v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(), iso);
|
||||
CHECK_EQ(recorder->count_, 1); // Unchanged.
|
||||
}
|
||||
|
||||
TEST(TriggerThreadSafeMetricsEvent) {
|
||||
// Set up isolate and context.
|
||||
v8::Isolate* iso = CcTest::isolate();
|
||||
i::Isolate* i_iso = reinterpret_cast<i::Isolate*>(iso);
|
||||
CHECK(i_iso->metrics_recorder());
|
||||
v8::metrics::WasmModulesPerIsolate event;
|
||||
std::shared_ptr<MetricsRecorder> recorder =
|
||||
std::make_shared<MetricsRecorder>(iso);
|
||||
iso->SetMetricsRecorder(recorder);
|
||||
|
||||
// Check that event submission works.
|
||||
event.count = 42;
|
||||
i_iso->metrics_recorder()->AddThreadSafeEvent(event);
|
||||
CHECK_EQ(recorder->count_, 1); // Increased.
|
||||
CHECK_EQ(recorder->module_count_, 42);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user