Reland "[profiler] Surface VM & Embedder State"

This is a reland of 2d087f237e

The changes are :
* Fix redundant reinterpret_cast in test file for MSVC failure
https://crbug.com/v8/12476
* Fix flaky test
https://crbug.com/v8/12475
If a sample is captured during a GC, no embedder context is obtained
defaulting to EMPTY. This is the expected behavior, made it in clear
in implementation and in test.
* Synchronized the embedder context filter behavior with existing
native context filter.

Original change's description:

> Add APIs to surface VMState and new EmbedderState to CpuProfile samples.
>
> EmbedderState:
> * An EmbedderState is defined as a value uint8_t and a v8::context used
> for filtering.
> * EmbedderStates are stack allocated by the embedder, construction and
> destruction set/unset the state to the isolate thread local top.
> * A v8::context is used to filter states that are added to a CpuProfile,
> if the CpuProfile do not have a ContextFilter set or if contexts do not
> match, state defaults to Empty.
>
> * v8:StateTag is already propagated all the way to a Sample, simply add
> an API to surface it.
>
> VMState:
> Change-Id: I7eed08907360b99b0ad20ddcff59c95c7076c85e
> Bug: chromium:1263871
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3188072
> Auto-Submit: Corentin Pescheloche <cpescheloche@fb.com>
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Commit-Queue: Camillo Bruni <cbruni@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#78250}

Bug: chromium:1263871
Change-Id: Ief891b05da99c695e9fb70f94ed7ebdecc6c3b7b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3320037
Auto-Submit: Corentin Pescheloche <cpescheloche@fb.com>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78281}
This commit is contained in:
Corentin Pescheloche 2021-12-06 23:28:08 -08:00 committed by V8 LUCI CQ
parent dc01b43616
commit e155881f24
19 changed files with 471 additions and 45 deletions

View File

@ -445,6 +445,7 @@ filegroup(
"include/v8-date.h",
"include/v8-debug.h",
"include/v8-embedder-heap.h",
"include/v8-embedder-state-scope.h,
"include/v8-exception.h",
"include/v8-extension.h",
"include/v8-external.h",
@ -1214,6 +1215,8 @@ filegroup(
"src/execution/arguments.h",
"src/execution/encoded-c-signature.cc",
"src/execution/encoded-c-signature.h",
"src/execution/embedder-state.h",
"src/execution/embedder-state.cc",
"src/execution/execution.cc",
"src/execution/execution.h",
"src/execution/frame-constants.h",

View File

@ -2487,6 +2487,7 @@ v8_header_set("v8_headers") {
"include/v8-date.h",
"include/v8-debug.h",
"include/v8-embedder-heap.h",
"include/v8-embedder-state-scope.h",
"include/v8-exception.h",
"include/v8-extension.h",
"include/v8-external.h",
@ -2864,6 +2865,7 @@ v8_header_set("v8_internal_headers") {
"src/diagnostics/unwinder.h",
"src/execution/arguments-inl.h",
"src/execution/arguments.h",
"src/execution/embedder-state.h",
"src/execution/encoded-c-signature.h",
"src/execution/execution.h",
"src/execution/frame-constants.h",
@ -4079,6 +4081,7 @@ v8_source_set("v8_base_without_compiler") {
"src/diagnostics/perf-jit.cc",
"src/diagnostics/unwinder.cc",
"src/execution/arguments.cc",
"src/execution/embedder-state.cc",
"src/execution/encoded-c-signature.cc",
"src/execution/execution.cc",
"src/execution/frames.cc",

View File

@ -0,0 +1,48 @@
// Copyright 2021 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 INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_
#define INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_
#include <memory>
#include "v8-context.h" // NOLINT(build/include_directory)
#include "v8-internal.h" // NOLINT(build/include_directory)
#include "v8-local-handle.h" // NOLINT(build/include_directory)
namespace v8 {
namespace internal {
class EmbedderState;
} // namespace internal
// A StateTag represents a possible state of the embedder.
enum class EmbedderStateTag : uint8_t {
EMPTY = 0,
// embedder can define any state in between
OTHER = UINT8_MAX,
};
// A stack-allocated class that manages an embedder state on the isolate.
// After an EmbedderState scope has been created, a new embedder state will be
// pushed on the isolate stack.
class V8_EXPORT EmbedderStateScope {
public:
EmbedderStateScope(Isolate* isolate, Local<v8::Context> context,
EmbedderStateTag tag);
private:
// Declaring operator new and delete as deleted is not spec compliant.
// Therefore declare them private instead to disable dynamic alloc
void* operator new(size_t size);
void* operator new[](size_t size);
void operator delete(void*, size_t);
void operator delete[](void*, size_t);
std::unique_ptr<internal::EmbedderState> embedder_state_;
};
} // namespace v8
#endif // INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_

View File

@ -20,9 +20,11 @@
*/
namespace v8 {
enum class EmbedderStateTag : uint8_t;
class HeapGraphNode;
struct HeapStatsUpdate;
class Object;
enum StateTag : int;
using NativeObject = void*;
using SnapshotObjectId = uint32_t;
@ -210,6 +212,16 @@ class V8_EXPORT CpuProfile {
*/
int64_t GetStartTime() const;
/**
* Returns state of the vm when sample was captured.
*/
StateTag GetSampleState(int index) const;
/**
* Returns state of the embedder when sample was captured.
*/
EmbedderStateTag GetSampleEmbedderState(int index) const;
/**
* Returns time when the profile recording was stopped (in microseconds)
* since some unspecified starting point.

View File

@ -7,7 +7,8 @@
#include <memory>
#include "v8config.h" // NOLINT(build/include_directory)
#include "v8-embedder-state-scope.h" // NOLINT(build/include_directory)
#include "v8config.h" // NOLINT(build/include_directory)
namespace v8 {
// Holds the callee saved registers needed for the stack unwinder. It is the
@ -32,7 +33,7 @@ struct V8_EXPORT RegisterState {
};
// A StateTag represents a possible state of the VM.
enum StateTag {
enum StateTag : int {
JS,
GC,
PARSER,
@ -46,11 +47,13 @@ enum StateTag {
// The output structure filled up by GetStackSample API function.
struct SampleInfo {
size_t frames_count; // Number of frames collected.
StateTag vm_state; // Current VM state.
void* external_callback_entry; // External callback address if VM is
// executing an external callback.
void* context; // Incumbent native context address.
size_t frames_count; // Number of frames collected.
void* external_callback_entry; // External callback address if VM is
// executing an external callback.
void* context; // Incumbent native context address.
void* embedder_context; // Native context address for embedder state
StateTag vm_state; // Current VM state.
EmbedderStateTag embedder_state; // Current Embedder state
};
struct MemoryRange {

View File

@ -15,6 +15,7 @@
#include "include/v8-callbacks.h"
#include "include/v8-cppgc.h"
#include "include/v8-date.h"
#include "include/v8-embedder-state-scope.h"
#include "include/v8-extension.h"
#include "include/v8-fast-api-calls.h"
#include "include/v8-function.h"
@ -46,6 +47,7 @@
#include "src/debug/liveedit.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/diagnostics/gdb-jit.h"
#include "src/execution/embedder-state.h"
#include "src/execution/execution.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
@ -9864,6 +9866,16 @@ int64_t CpuProfile::GetSampleTimestamp(int index) const {
return profile->sample(index).timestamp.since_origin().InMicroseconds();
}
StateTag CpuProfile::GetSampleState(int index) const {
const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
return profile->sample(index).state_tag;
}
EmbedderStateTag CpuProfile::GetSampleEmbedderState(int index) const {
const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
return profile->sample(index).embedder_state_tag;
}
int64_t CpuProfile::GetStartTime() const {
const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
return profile->start_time().since_origin().InMicroseconds();
@ -10332,6 +10344,11 @@ void EmbedderHeapTracer::ResetHandleInNonTracingGC(
UNREACHABLE();
}
EmbedderStateScope::EmbedderStateScope(Isolate* isolate,
Local<v8::Context> context,
EmbedderStateTag tag)
: embedder_state_(new internal::EmbedderState(isolate, context, tag)) {}
void TracedReferenceBase::CheckValue() const {
#ifdef V8_HOST_ARCH_64_BIT
if (!val_) return;

View File

@ -0,0 +1,45 @@
// Copyright 2021 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/execution/embedder-state.h"
#include "src/api/api-inl.h"
#include "src/base/logging.h"
namespace v8 {
namespace internal {
EmbedderState::EmbedderState(v8::Isolate* isolate, Local<v8::Context> context,
EmbedderStateTag tag)
: isolate_(reinterpret_cast<i::Isolate*>(isolate)),
tag_(tag),
previous_embedder_state_(isolate_->current_embedder_state()) {
if (!context.IsEmpty()) {
native_context_address_ =
v8::Utils::OpenHandle(*context)->native_context().address();
}
DCHECK_NE(this, isolate_->current_embedder_state());
isolate_->set_current_embedder_state(this);
}
EmbedderState::~EmbedderState() {
DCHECK_EQ(this, isolate_->current_embedder_state());
isolate_->set_current_embedder_state(previous_embedder_state_);
}
void EmbedderState::OnMoveEvent(Address from, Address to) {
EmbedderState* state = this;
do {
if (state->native_context_address_ == from) {
native_context_address_ = to;
}
state = state->previous_embedder_state_;
} while (state != nullptr);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,39 @@
// Copyright 2021 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_EXECUTION_EMBEDDER_STATE_H_
#define V8_EXECUTION_EMBEDDER_STATE_H_
#include "include/v8-local-handle.h"
#include "src/execution/isolate.h"
namespace v8 {
enum class EmbedderStateTag : uint8_t;
namespace internal {
class V8_EXPORT_PRIVATE EmbedderState {
public:
EmbedderState(v8::Isolate* isolate, Local<v8::Context> context,
EmbedderStateTag tag);
~EmbedderState();
EmbedderStateTag GetState() const { return tag_; }
Address native_context_address() const { return native_context_address_; }
void OnMoveEvent(Address from, Address to);
private:
Isolate* isolate_;
EmbedderStateTag tag_;
Address native_context_address_ = kNullAddress;
EmbedderState* previous_embedder_state_;
};
} // namespace internal
} // namespace v8
#endif // V8_EXECUTION_EMBEDDER_STATE_H_

View File

@ -65,6 +65,8 @@ class V8Inspector;
namespace v8 {
class EmbedderState;
namespace base {
class RandomNumberGenerator;
} // namespace base
@ -1303,6 +1305,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
THREAD_LOCAL_TOP_ACCESSOR(ExternalCallbackScope*, external_callback_scope)
THREAD_LOCAL_TOP_ACCESSOR(StateTag, current_vm_state)
THREAD_LOCAL_TOP_ACCESSOR(EmbedderState*, current_embedder_state)
void SetData(uint32_t slot, void* data) {
DCHECK_LT(slot, Internals::kNumIsolateDataSlots);

View File

@ -31,6 +31,7 @@ void ThreadLocalTop::Clear() {
js_entry_sp_ = kNullAddress;
external_callback_scope_ = nullptr;
current_vm_state_ = EXTERNAL;
current_embedder_state_ = nullptr;
failed_access_check_callback_ = nullptr;
thread_in_wasm_flag_address_ = kNullAddress;
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING

View File

@ -23,6 +23,7 @@ class TryCatch;
namespace internal {
class EmbedderState;
class ExternalCallbackScope;
class Isolate;
class PromiseOnStack;
@ -34,9 +35,9 @@ class ThreadLocalTop {
// refactor this to really consist of just Addresses and 32-bit
// integer fields.
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
static constexpr uint32_t kSizeInBytes = 25 * kSystemPointerSize;
static constexpr uint32_t kSizeInBytes = 26 * kSystemPointerSize;
#else
static constexpr uint32_t kSizeInBytes = 24 * kSystemPointerSize;
static constexpr uint32_t kSizeInBytes = 25 * kSystemPointerSize;
#endif
// Does early low-level initialization that does not depend on the
@ -151,6 +152,7 @@ class ThreadLocalTop {
// The external callback we're currently in.
ExternalCallbackScope* external_callback_scope_;
StateTag current_vm_state_;
EmbedderState* current_embedder_state_;
// Call back function to report unsafe JS accesses.
v8::FailedAccessCheckCallback failed_access_check_callback_;

View File

@ -27,6 +27,7 @@
#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
#include "src/debug/debug.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/embedder-state.h"
#include "src/execution/isolate-utils-inl.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/runtime-profiler.h"
@ -3323,6 +3324,10 @@ void Heap::OnMoveEvent(HeapObject target, HeapObject source,
LOG_CODE_EVENT(isolate_, SharedFunctionInfoMoveEvent(source.address(),
target.address()));
} else if (target.IsNativeContext()) {
if (isolate_->current_embedder_state() != nullptr) {
isolate_->current_embedder_state()->OnMoveEvent(source.address(),
target.address());
}
PROFILE(isolate_,
NativeContextMoveEvent(source.address(), target.address()));
}

View File

@ -230,12 +230,15 @@ void ProfilerEventsProcessor::CodeEventHandler(
void SamplingEventsProcessor::SymbolizeAndAddToProfiles(
const TickSampleEventRecord* record) {
const TickSample& tick_sample = record->sample;
Symbolizer::SymbolizedSample symbolized =
symbolizer_->SymbolizeTickSample(record->sample);
symbolizer_->SymbolizeTickSample(tick_sample);
profiles_->AddPathToCurrentProfiles(
record->sample.timestamp, symbolized.stack_trace, symbolized.src_line,
record->sample.update_stats_, record->sample.sampling_interval_,
reinterpret_cast<Address>(record->sample.context));
tick_sample.timestamp, symbolized.stack_trace, symbolized.src_line,
tick_sample.update_stats_, tick_sample.sampling_interval_,
tick_sample.state, tick_sample.embedder_state,
reinterpret_cast<Address>(tick_sample.context),
reinterpret_cast<Address>(tick_sample.embedder_context));
}
ProfilerEventsProcessor::SampleProcessingResult

View File

@ -620,7 +620,9 @@ bool CpuProfile::CheckSubsample(base::TimeDelta source_sampling_interval) {
void CpuProfile::AddPath(base::TimeTicks timestamp,
const ProfileStackTrace& path, int src_line,
bool update_stats, base::TimeDelta sampling_interval) {
bool update_stats, base::TimeDelta sampling_interval,
StateTag state_tag,
EmbedderStateTag embedder_state_tag) {
if (!CheckSubsample(sampling_interval)) return;
ProfileNode* top_frame_node =
@ -632,7 +634,8 @@ void CpuProfile::AddPath(base::TimeTicks timestamp,
samples_.size() < options_.max_samples());
if (should_record_sample) {
samples_.push_back({top_frame_node, timestamp, src_line});
samples_.push_back(
{top_frame_node, timestamp, src_line, state_tag, embedder_state_tag});
}
if (!should_record_sample && delegate_ != nullptr) {
@ -988,19 +991,24 @@ base::TimeDelta CpuProfilesCollection::GetCommonSamplingInterval() const {
void CpuProfilesCollection::AddPathToCurrentProfiles(
base::TimeTicks timestamp, const ProfileStackTrace& path, int src_line,
bool update_stats, base::TimeDelta sampling_interval,
Address native_context_address) {
bool update_stats, base::TimeDelta sampling_interval, StateTag state,
EmbedderStateTag embedder_state_tag, Address native_context_address,
Address embedder_native_context_address) {
// As starting / stopping profiles is rare relatively to this
// method, we don't bother minimizing the duration of lock holding,
// e.g. copying contents of the list to a local vector.
current_profiles_semaphore_.Wait();
const ProfileStackTrace empty_path;
for (const std::unique_ptr<CpuProfile>& profile : current_profiles_) {
ContextFilter& context_filter = profile->context_filter();
// If the context filter check failed, omit the contents of the stack.
bool accepts_context =
profile->context_filter().Accept(native_context_address);
bool accepts_context = context_filter.Accept(native_context_address);
bool accepts_embedder_context =
context_filter.Accept(embedder_native_context_address);
profile->AddPath(timestamp, accepts_context ? path : empty_path, src_line,
update_stats, sampling_interval);
update_stats, sampling_interval, state,
accepts_embedder_context ? embedder_state_tag
: EmbedderStateTag::EMPTY);
}
current_profiles_semaphore_.Signal();
}

View File

@ -17,6 +17,7 @@
#include "include/v8-profiler.h"
#include "src/base/platform/time.h"
#include "src/builtins/builtins.h"
#include "src/execution/vm-state.h"
#include "src/logging/code-events.h"
#include "src/profiler/strings-storage.h"
#include "src/utils/allocation.h"
@ -405,6 +406,8 @@ class CpuProfile {
ProfileNode* node;
base::TimeTicks timestamp;
int line;
StateTag state_tag;
EmbedderStateTag embedder_state_tag;
};
V8_EXPORT_PRIVATE CpuProfile(
@ -419,7 +422,8 @@ class CpuProfile {
// Add pc -> ... -> main() call path to the profile.
void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path,
int src_line, bool update_stats,
base::TimeDelta sampling_interval);
base::TimeDelta sampling_interval, StateTag state,
EmbedderStateTag embedder_state);
void FinishProfile();
const char* title() const { return title_; }
@ -554,11 +558,12 @@ class V8_EXPORT_PRIVATE CpuProfilesCollection {
base::TimeDelta GetCommonSamplingInterval() const;
// Called from profile generator thread.
void AddPathToCurrentProfiles(base::TimeTicks timestamp,
const ProfileStackTrace& path, int src_line,
bool update_stats,
base::TimeDelta sampling_interval,
Address native_context_address = kNullAddress);
void AddPathToCurrentProfiles(
base::TimeTicks timestamp, const ProfileStackTrace& path, int src_line,
bool update_stats, base::TimeDelta sampling_interval, StateTag state,
EmbedderStateTag embedder_state_tag,
Address native_context_address = kNullAddress,
Address native_embedder_context_address = kNullAddress);
// Called from profile generator thread.
void UpdateNativeContextAddressForCurrentProfiles(Address from, Address to);

View File

@ -9,6 +9,7 @@
#include "include/v8-profiler.h"
#include "src/base/sanitizer/asan.h"
#include "src/base/sanitizer/msan.h"
#include "src/execution/embedder-state.h"
#include "src/execution/frames-inl.h"
#include "src/execution/simulator.h"
#include "src/execution/vm-state-inl.h"
@ -178,6 +179,8 @@ DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
frames_count = static_cast<unsigned>(info.frames_count);
has_external_callback = info.external_callback_entry != nullptr;
context = info.context;
embedder_context = info.embedder_context;
embedder_state = info.embedder_state;
if (has_external_callback) {
external_callback_entry = info.external_callback_entry;
} else if (frames_count) {
@ -210,9 +213,19 @@ bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
sample_info->frames_count = 0;
sample_info->vm_state = isolate->current_vm_state();
sample_info->external_callback_entry = nullptr;
sample_info->embedder_state = EmbedderStateTag::EMPTY;
sample_info->embedder_context = nullptr;
sample_info->context = nullptr;
if (sample_info->vm_state == GC) return true;
EmbedderState* embedder_state = isolate->current_embedder_state();
if (embedder_state != nullptr) {
sample_info->embedder_context =
reinterpret_cast<void*>(embedder_state->native_context_address());
sample_info->embedder_state = embedder_state->GetState();
}
i::Address js_entry_sp = isolate->js_entry_sp();
if (js_entry_sp == 0) return true; // Not executing JS now.

View File

@ -23,6 +23,7 @@ struct V8_EXPORT TickSample {
TickSample()
: state(OTHER),
embedder_state(EmbedderStateTag::EMPTY),
pc(nullptr),
external_callback_entry(nullptr),
frames_count(0),
@ -82,6 +83,7 @@ struct V8_EXPORT TickSample {
void print() const;
StateTag state; // The state of the VM.
EmbedderStateTag embedder_state;
void* pc; // Instruction pointer.
union {
void* tos; // Top stack value (*sp).
@ -91,6 +93,7 @@ struct V8_EXPORT TickSample {
static const unsigned kMaxFramesCount = (1 << kMaxFramesCountLog2) - 1;
void* stack[kMaxFramesCount]; // Call stack.
void* context = nullptr; // Address of the incumbent native context.
void* embedder_context = nullptr; // Address of the embedder native context.
unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames.
bool has_external_callback : 1;
bool update_stats_ : 1; // Whether the sample should update aggregated stats.

View File

@ -41,6 +41,7 @@
#include "src/codegen/compilation-cache.h"
#include "src/codegen/source-position-table.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/embedder-state.h"
#include "src/heap/spaces.h"
#include "src/init/v8.h"
#include "src/libplatform/default-platform.h"
@ -3922,6 +3923,217 @@ TEST(ContextIsolation) {
}
}
void ValidateEmbedderState(v8::CpuProfile* profile,
EmbedderStateTag expected_tag) {
for (int i = 0; i < profile->GetSamplesCount(); i++) {
if (profile->GetSampleState(i) == StateTag::GC) {
// Samples captured during a GC do not have an EmbedderState
CHECK_EQ(profile->GetSampleEmbedderState(i), EmbedderStateTag::EMPTY);
} else {
CHECK_EQ(profile->GetSampleEmbedderState(i), expected_tag);
}
}
}
// Tests that embedder states from other contexts aren't recorded
TEST(EmbedderContextIsolation) {
i::FLAG_allow_natives_syntax = true;
LocalContext execution_env;
i::HandleScope scope(CcTest::i_isolate());
v8::Isolate* isolate = execution_env.local()->GetIsolate();
// Install CollectSample callback for more deterministic sampling.
v8::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(isolate, CallCollectSample);
v8::Local<v8::Function> func =
func_template->GetFunction(execution_env.local()).ToLocalChecked();
func->SetName(v8_str("CallCollectSample"));
execution_env->Global()
->Set(execution_env.local(), v8_str("CallCollectSample"), func)
.FromJust();
v8::Local<v8::Context> diff_context = v8::Context::New(isolate);
{
CHECK_NULL(CcTest::i_isolate()->current_embedder_state());
// prepare other embedder state
EmbedderStateScope scope(isolate, diff_context, EmbedderStateTag::OTHER);
CHECK_EQ(CcTest::i_isolate()->current_embedder_state()->GetState(),
EmbedderStateTag::OTHER);
ProfilerHelper helper(execution_env.local());
CompileRun(R"(
function optimized() {
CallCollectSample();
}
function unoptimized() {
CallCollectSample();
}
function start() {
// Test optimized functions
%PrepareFunctionForOptimization(optimized);
optimized();
optimized();
%OptimizeFunctionOnNextCall(optimized);
optimized();
// Test unoptimized functions
%NeverOptimizeFunction(unoptimized);
unoptimized();
// Test callback
CallCollectSample();
}
)");
v8::Local<v8::Function> function =
GetFunction(execution_env.local(), "start");
v8::CpuProfile* profile = helper.Run(
function, nullptr, 0, 0, 0, v8::CpuProfilingMode::kLeafNodeLineNumbers,
v8::CpuProfilingOptions::kNoSampleLimit, execution_env.local());
ValidateEmbedderState(profile, EmbedderStateTag::EMPTY);
}
CHECK_NULL(CcTest::i_isolate()->current_embedder_state());
}
// Tests that embedder states from same context are recorded
TEST(EmbedderStatePropagate) {
i::FLAG_allow_natives_syntax = true;
LocalContext execution_env;
i::HandleScope scope(CcTest::i_isolate());
v8::Isolate* isolate = execution_env.local()->GetIsolate();
// Install CollectSample callback for more deterministic sampling.
v8::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(isolate, CallCollectSample);
v8::Local<v8::Function> func =
func_template->GetFunction(execution_env.local()).ToLocalChecked();
func->SetName(v8_str("CallCollectSample"));
execution_env->Global()
->Set(execution_env.local(), v8_str("CallCollectSample"), func)
.FromJust();
{
// prepare embedder state
EmbedderState embedderState(isolate, execution_env.local(),
EmbedderStateTag::OTHER);
CHECK_EQ(CcTest::i_isolate()->current_embedder_state(), &embedderState);
ProfilerHelper helper(execution_env.local());
CompileRun(R"(
function optimized() {
CallCollectSample();
}
function unoptimized() {
CallCollectSample();
}
function start() {
// Test optimized functions
%PrepareFunctionForOptimization(optimized);
optimized();
optimized();
%OptimizeFunctionOnNextCall(optimized);
optimized();
// Test unoptimized functions
%NeverOptimizeFunction(unoptimized);
unoptimized();
// Test callback
CallCollectSample();
}
)");
v8::Local<v8::Function> function =
GetFunction(execution_env.local(), "start");
v8::CpuProfile* profile = helper.Run(
function, nullptr, 0, 0, 0, v8::CpuProfilingMode::kLeafNodeLineNumbers,
v8::CpuProfilingOptions::kNoSampleLimit, execution_env.local());
ValidateEmbedderState(profile, EmbedderStateTag::OTHER);
}
CHECK_NULL(CcTest::i_isolate()->current_embedder_state());
}
// Tests that embedder states from same context are recorded
// even after native context move
TEST(EmbedderStatePropagateNativeContextMove) {
// Reusing context addresses will cause this test to fail.
if (i::FLAG_gc_global || i::FLAG_stress_compaction ||
i::FLAG_stress_incremental_marking || i::FLAG_enable_third_party_heap) {
return;
}
i::FLAG_allow_natives_syntax = true;
i::FLAG_manual_evacuation_candidates_selection = true;
LocalContext execution_env;
i::HandleScope scope(CcTest::i_isolate());
v8::Isolate* isolate = execution_env.local()->GetIsolate();
// Install CollectSample callback for more deterministic sampling.
v8::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(isolate, CallCollectSample);
v8::Local<v8::Function> func =
func_template->GetFunction(execution_env.local()).ToLocalChecked();
func->SetName(v8_str("CallCollectSample"));
execution_env->Global()
->Set(execution_env.local(), v8_str("CallCollectSample"), func)
.FromJust();
{
// prepare embedder state
EmbedderState embedderState(isolate, execution_env.local(),
EmbedderStateTag::OTHER);
CHECK_EQ(CcTest::i_isolate()->current_embedder_state(), &embedderState);
i::Address initial_address =
CcTest::i_isolate()->current_embedder_state()->native_context_address();
// Install a function that triggers the native context to be moved.
v8::Local<v8::FunctionTemplate> move_func_template =
v8::FunctionTemplate::New(
execution_env.local()->GetIsolate(),
[](const v8::FunctionCallbackInfo<v8::Value>& info) {
i::Isolate* isolate =
reinterpret_cast<i::Isolate*>(info.GetIsolate());
i::heap::ForceEvacuationCandidate(
i::Page::FromHeapObject(isolate->raw_native_context()));
CcTest::CollectAllGarbage();
});
v8::Local<v8::Function> move_func =
move_func_template->GetFunction(execution_env.local()).ToLocalChecked();
move_func->SetName(v8_str("ForceNativeContextMove"));
execution_env->Global()
->Set(execution_env.local(), v8_str("ForceNativeContextMove"),
move_func)
.FromJust();
ProfilerHelper helper(execution_env.local());
CompileRun(R"(
function start() {
ForceNativeContextMove();
CallCollectSample();
}
)");
v8::Local<v8::Function> function =
GetFunction(execution_env.local(), "start");
v8::CpuProfile* profile = helper.Run(
function, nullptr, 0, 0, 0, v8::CpuProfilingMode::kLeafNodeLineNumbers,
v8::CpuProfilingOptions::kNoSampleLimit, execution_env.local());
ValidateEmbedderState(profile, EmbedderStateTag::OTHER);
i::Address new_address =
CcTest::i_isolate()->current_embedder_state()->native_context_address();
CHECK_NE(initial_address, new_address);
}
CHECK_NULL(CcTest::i_isolate()->current_embedder_state());
}
// Tests that when a native context that's being filtered is moved, we continue
// to track its execution.
TEST(ContextFilterMovedNativeContext) {

View File

@ -482,9 +482,9 @@ TEST(SampleIds) {
sample1.stack[0] = ToPointer(0x1510);
sample1.frames_count = 1;
auto symbolized = symbolizer.SymbolizeTickSample(sample1);
profiles.AddPathToCurrentProfiles(sample1.timestamp, symbolized.stack_trace,
symbolized.src_line, true,
base::TimeDelta());
profiles.AddPathToCurrentProfiles(
sample1.timestamp, symbolized.stack_trace, symbolized.src_line, true,
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
TickSample sample2;
sample2.timestamp = v8::base::TimeTicks::HighResolutionNow();
@ -494,9 +494,9 @@ TEST(SampleIds) {
sample2.stack[2] = ToPointer(0x1620);
sample2.frames_count = 3;
symbolized = symbolizer.SymbolizeTickSample(sample2);
profiles.AddPathToCurrentProfiles(sample2.timestamp, symbolized.stack_trace,
symbolized.src_line, true,
base::TimeDelta());
profiles.AddPathToCurrentProfiles(
sample2.timestamp, symbolized.stack_trace, symbolized.src_line, true,
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
TickSample sample3;
sample3.timestamp = v8::base::TimeTicks::HighResolutionNow();
@ -505,9 +505,9 @@ TEST(SampleIds) {
sample3.stack[1] = ToPointer(0x1610);
sample3.frames_count = 2;
symbolized = symbolizer.SymbolizeTickSample(sample3);
profiles.AddPathToCurrentProfiles(sample3.timestamp, symbolized.stack_trace,
symbolized.src_line, true,
base::TimeDelta());
profiles.AddPathToCurrentProfiles(
sample3.timestamp, symbolized.stack_trace, symbolized.src_line, true,
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
CpuProfile* profile = profiles.StopProfiling("");
unsigned nodeId = 1;
@ -603,9 +603,9 @@ TEST(MaxSamplesCallback) {
sample1.stack[0] = ToPointer(0x1510);
sample1.frames_count = 1;
auto symbolized = symbolizer.SymbolizeTickSample(sample1);
profiles.AddPathToCurrentProfiles(sample1.timestamp, symbolized.stack_trace,
symbolized.src_line, true,
base::TimeDelta());
profiles.AddPathToCurrentProfiles(
sample1.timestamp, symbolized.stack_trace, symbolized.src_line, true,
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
CHECK_EQ(0, mock_platform->posted_count());
TickSample sample2;
sample2.timestamp = v8::base::TimeTicks::HighResolutionNow();
@ -613,18 +613,18 @@ TEST(MaxSamplesCallback) {
sample2.stack[0] = ToPointer(0x1780);
sample2.frames_count = 2;
symbolized = symbolizer.SymbolizeTickSample(sample2);
profiles.AddPathToCurrentProfiles(sample2.timestamp, symbolized.stack_trace,
symbolized.src_line, true,
base::TimeDelta());
profiles.AddPathToCurrentProfiles(
sample2.timestamp, symbolized.stack_trace, symbolized.src_line, true,
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
CHECK_EQ(1, mock_platform->posted_count());
TickSample sample3;
sample3.timestamp = v8::base::TimeTicks::HighResolutionNow();
sample3.pc = ToPointer(0x1510);
sample3.frames_count = 3;
symbolized = symbolizer.SymbolizeTickSample(sample3);
profiles.AddPathToCurrentProfiles(sample3.timestamp, symbolized.stack_trace,
symbolized.src_line, true,
base::TimeDelta());
profiles.AddPathToCurrentProfiles(
sample3.timestamp, symbolized.stack_trace, symbolized.src_line, true,
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
CHECK_EQ(1, mock_platform->posted_count());
// Teardown
@ -654,7 +654,8 @@ TEST(NoSamples) {
auto symbolized = symbolizer.SymbolizeTickSample(sample1);
profiles.AddPathToCurrentProfiles(v8::base::TimeTicks::HighResolutionNow(),
symbolized.stack_trace, symbolized.src_line,
true, base::TimeDelta());
true, base::TimeDelta(), StateTag::JS,
EmbedderStateTag::EMPTY);
CpuProfile* profile = profiles.StopProfiling("");
unsigned nodeId = 1;