v8/test/inspector/isolate-data.cc
Dominik Inführ b9eeaf1b88 Reland: [heap] Load MarkingBarrier from thread local on main thread
Reland of https://crrev.com/c/3998633.

Each thread has its own MarkingBarrier instance for incremental
marking. A thread local variable is used to get the current thread's
instance on background threads.

However on main threads this thread local variable was always
set to nullptr. The main thread would get to its own instance through
the heap_ field in the host object's page header. This was solved this
way because setting current_marking_barrier on the main thread
seemed quite complex. Multiple isolates may be run on the same thread
and isolates may even be migrated between threads.

However, with --shared-space loading the heap_ field for a shared
object would return the main isolate's heap and we end up with
the wrong MarkingBarrier instance on client isolates. So this
CL makes main and background threads more uniform by setting the
thread local field also on the main thread. The field is set by
the already existing v8::Isolate::Scope API. Some embedders might have
to add these scopes if they don't use them properly already.

Bug: v8:13267
Change-Id: Idc257ecf6b6af09a379bdd7cd7c1d4a5e46689c9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4016715
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84237}
2022-11-14 12:18:18 +00:00

559 lines
20 KiB
C++

// Copyright 2017 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 "test/inspector/isolate-data.h"
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-microtask-queue.h"
#include "include/v8-template.h"
#include "src/init/v8.h"
#include "src/inspector/test-interface.h"
#include "test/inspector/task-runner.h"
#include "test/inspector/utils.h"
namespace v8 {
namespace internal {
namespace {
const int kIsolateDataIndex = 2;
const int kContextGroupIdIndex = 3;
void Print(v8::Isolate* isolate, const v8_inspector::StringView& string) {
v8::Local<v8::String> v8_string = ToV8String(isolate, string);
v8::String::Utf8Value utf8_string(isolate, v8_string);
fwrite(*utf8_string, sizeof(**utf8_string), utf8_string.length(), stdout);
}
class Inspectable : public v8_inspector::V8InspectorSession::Inspectable {
public:
Inspectable(v8::Isolate* isolate, v8::Local<v8::Value> object)
: object_(isolate, object) {}
~Inspectable() override = default;
v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
return object_.Get(context->GetIsolate());
}
private:
v8::Global<v8::Value> object_;
};
} // namespace
InspectorIsolateData::InspectorIsolateData(
TaskRunner* task_runner,
InspectorIsolateData::SetupGlobalTasks setup_global_tasks,
v8::StartupData* startup_data, WithInspector with_inspector)
: task_runner_(task_runner),
setup_global_tasks_(std::move(setup_global_tasks)) {
v8::Isolate::CreateParams params;
array_buffer_allocator_.reset(
v8::ArrayBuffer::Allocator::NewDefaultAllocator());
params.array_buffer_allocator = array_buffer_allocator_.get();
params.snapshot_blob = startup_data;
params.only_terminate_in_safe_scope = true;
isolate_.reset(v8::Isolate::New(params));
isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kScoped);
if (with_inspector) {
isolate_->AddMessageListener(&InspectorIsolateData::MessageHandler);
isolate_->SetPromiseRejectCallback(
&InspectorIsolateData::PromiseRejectHandler);
inspector_ = v8_inspector::V8Inspector::create(isolate_.get(), this);
}
v8::Isolate::Scope isolate_scope(isolate_.get());
v8::HandleScope handle_scope(isolate_.get());
not_inspectable_private_.Reset(
isolate_.get(),
v8::Private::ForApi(
isolate_.get(),
v8::String::NewFromUtf8Literal(isolate_.get(), "notInspectable")));
}
InspectorIsolateData* InspectorIsolateData::FromContext(
v8::Local<v8::Context> context) {
return static_cast<InspectorIsolateData*>(
context->GetAlignedPointerFromEmbedderData(kIsolateDataIndex));
}
int InspectorIsolateData::CreateContextGroup() {
int context_group_id = ++last_context_group_id_;
if (!CreateContext(context_group_id, v8_inspector::StringView())) {
DCHECK(isolate_->IsExecutionTerminating());
return -1;
}
return context_group_id;
}
bool InspectorIsolateData::CreateContext(int context_group_id,
v8_inspector::StringView name) {
v8::HandleScope handle_scope(isolate_.get());
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate_.get());
for (auto it = setup_global_tasks_.begin(); it != setup_global_tasks_.end();
++it) {
(*it)->Run(isolate_.get(), global_template);
}
v8::Local<v8::Context> context =
v8::Context::New(isolate_.get(), nullptr, global_template);
if (context.IsEmpty()) return false;
context->SetAlignedPointerInEmbedderData(kIsolateDataIndex, this);
// Should be 2-byte aligned.
context->SetAlignedPointerInEmbedderData(
kContextGroupIdIndex, reinterpret_cast<void*>(context_group_id * 2));
contexts_[context_group_id].emplace_back(isolate_.get(), context);
if (inspector_) FireContextCreated(context, context_group_id, name);
return true;
}
v8::Local<v8::Context> InspectorIsolateData::GetDefaultContext(
int context_group_id) {
return contexts_[context_group_id].begin()->Get(isolate_.get());
}
void InspectorIsolateData::ResetContextGroup(int context_group_id) {
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->resetContextGroup(context_group_id);
}
int InspectorIsolateData::GetContextGroupId(v8::Local<v8::Context> context) {
return static_cast<int>(
reinterpret_cast<intptr_t>(
context->GetAlignedPointerFromEmbedderData(kContextGroupIdIndex)) /
2);
}
void InspectorIsolateData::RegisterModule(v8::Local<v8::Context> context,
std::vector<uint16_t> name,
v8::ScriptCompiler::Source* source) {
v8::Local<v8::Module> module;
if (!v8::ScriptCompiler::CompileModule(isolate(), source).ToLocal(&module))
return;
if (!module
->InstantiateModule(context,
&InspectorIsolateData::ModuleResolveCallback)
.FromMaybe(false)) {
return;
}
v8::Local<v8::Value> result;
if (!module->Evaluate(context).ToLocal(&result)) return;
modules_[name] = v8::Global<v8::Module>(isolate_.get(), module);
}
// static
v8::MaybeLocal<v8::Module> InspectorIsolateData::ModuleResolveCallback(
v8::Local<v8::Context> context, v8::Local<v8::String> specifier,
v8::Local<v8::FixedArray> import_assertions,
v8::Local<v8::Module> referrer) {
// TODO(v8:11189) Consider JSON modules support in the InspectorClient
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
std::string str = *v8::String::Utf8Value(data->isolate(), specifier);
v8::MaybeLocal<v8::Module> maybe_module =
data->modules_[ToVector(data->isolate(), specifier)].Get(data->isolate());
if (maybe_module.IsEmpty()) {
data->isolate()->ThrowError(v8::String::Concat(
data->isolate(),
ToV8String(data->isolate(), "Failed to resolve module: "), specifier));
}
return maybe_module;
}
int InspectorIsolateData::ConnectSession(
int context_group_id, const v8_inspector::StringView& state,
v8_inspector::V8Inspector::Channel* channel) {
v8::SealHandleScope seal_handle_scope(isolate());
int session_id = ++last_session_id_;
sessions_[session_id] = inspector_->connect(
context_group_id, channel, state,
v8_inspector::V8Inspector::kFullyTrusted,
waiting_for_debugger_
? v8_inspector::V8Inspector::kWaitingForDebugger
: v8_inspector::V8Inspector::kNotWaitingForDebugger);
context_group_by_session_[sessions_[session_id].get()] = context_group_id;
return session_id;
}
std::vector<uint8_t> InspectorIsolateData::DisconnectSession(int session_id) {
v8::SealHandleScope seal_handle_scope(isolate());
auto it = sessions_.find(session_id);
CHECK(it != sessions_.end());
context_group_by_session_.erase(it->second.get());
std::vector<uint8_t> result = it->second->state();
sessions_.erase(it);
return result;
}
void InspectorIsolateData::SendMessage(
int session_id, const v8_inspector::StringView& message) {
v8::SealHandleScope seal_handle_scope(isolate());
auto it = sessions_.find(session_id);
if (it != sessions_.end()) it->second->dispatchProtocolMessage(message);
}
void InspectorIsolateData::BreakProgram(
int context_group_id, const v8_inspector::StringView& reason,
const v8_inspector::StringView& details) {
v8::SealHandleScope seal_handle_scope(isolate());
for (int session_id : GetSessionIds(context_group_id)) {
auto it = sessions_.find(session_id);
if (it != sessions_.end()) it->second->breakProgram(reason, details);
}
}
void InspectorIsolateData::SchedulePauseOnNextStatement(
int context_group_id, const v8_inspector::StringView& reason,
const v8_inspector::StringView& details) {
v8::SealHandleScope seal_handle_scope(isolate());
for (int session_id : GetSessionIds(context_group_id)) {
auto it = sessions_.find(session_id);
if (it != sessions_.end())
it->second->schedulePauseOnNextStatement(reason, details);
}
}
void InspectorIsolateData::CancelPauseOnNextStatement(int context_group_id) {
v8::SealHandleScope seal_handle_scope(isolate());
for (int session_id : GetSessionIds(context_group_id)) {
auto it = sessions_.find(session_id);
if (it != sessions_.end()) it->second->cancelPauseOnNextStatement();
}
}
void InspectorIsolateData::AsyncTaskScheduled(
const v8_inspector::StringView& name, void* task, bool recurring) {
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->asyncTaskScheduled(name, task, recurring);
}
void InspectorIsolateData::AsyncTaskStarted(void* task) {
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->asyncTaskStarted(task);
}
void InspectorIsolateData::AsyncTaskFinished(void* task) {
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->asyncTaskFinished(task);
}
v8_inspector::V8StackTraceId InspectorIsolateData::StoreCurrentStackTrace(
const v8_inspector::StringView& description) {
v8::SealHandleScope seal_handle_scope(isolate());
return inspector_->storeCurrentStackTrace(description);
}
void InspectorIsolateData::ExternalAsyncTaskStarted(
const v8_inspector::V8StackTraceId& parent) {
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->externalAsyncTaskStarted(parent);
}
void InspectorIsolateData::ExternalAsyncTaskFinished(
const v8_inspector::V8StackTraceId& parent) {
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->externalAsyncTaskFinished(parent);
}
void InspectorIsolateData::AddInspectedObject(int session_id,
v8::Local<v8::Value> object) {
v8::SealHandleScope seal_handle_scope(isolate());
auto it = sessions_.find(session_id);
if (it == sessions_.end()) return;
std::unique_ptr<Inspectable> inspectable(
new Inspectable(isolate_.get(), object));
it->second->addInspectedObject(std::move(inspectable));
}
void InspectorIsolateData::SetMaxAsyncTaskStacksForTest(int limit) {
v8::SealHandleScope seal_handle_scope(isolate());
v8_inspector::SetMaxAsyncTaskStacksForTest(inspector_.get(), limit);
}
void InspectorIsolateData::DumpAsyncTaskStacksStateForTest() {
v8::SealHandleScope seal_handle_scope(isolate());
v8_inspector::DumpAsyncTaskStacksStateForTest(inspector_.get());
}
// static
int InspectorIsolateData::HandleMessage(v8::Local<v8::Message> message,
v8::Local<v8::Value> exception) {
v8::Isolate* isolate = message->GetIsolate();
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
if (context.IsEmpty()) return 0;
v8_inspector::V8Inspector* inspector =
InspectorIsolateData::FromContext(context)->inspector_.get();
v8::Local<v8::StackTrace> stack = message->GetStackTrace();
int script_id = message->GetScriptOrigin().ScriptId();
if (!stack.IsEmpty() && stack->GetFrameCount() > 0) {
int top_script_id = stack->GetFrame(isolate, 0)->GetScriptId();
if (top_script_id == script_id) script_id = 0;
}
int line_number = message->GetLineNumber(context).FromMaybe(0);
int column_number = 0;
if (message->GetStartColumn(context).IsJust())
column_number = message->GetStartColumn(context).FromJust() + 1;
v8_inspector::StringView detailed_message;
std::vector<uint16_t> message_text_string = ToVector(isolate, message->Get());
v8_inspector::StringView message_text(message_text_string.data(),
message_text_string.size());
std::vector<uint16_t> url_string;
if (message->GetScriptOrigin().ResourceName()->IsString()) {
url_string = ToVector(
isolate, message->GetScriptOrigin().ResourceName().As<v8::String>());
}
v8_inspector::StringView url(url_string.data(), url_string.size());
v8::SealHandleScope seal_handle_scope(isolate);
return inspector->exceptionThrown(
context, message_text, exception, detailed_message, url, line_number,
column_number, inspector->createStackTrace(stack), script_id);
}
// static
void InspectorIsolateData::MessageHandler(v8::Local<v8::Message> message,
v8::Local<v8::Value> exception) {
HandleMessage(message, exception);
}
// static
void InspectorIsolateData::PromiseRejectHandler(v8::PromiseRejectMessage data) {
v8::Isolate* isolate = data.GetPromise()->GetIsolate();
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
if (context.IsEmpty()) return;
v8::Local<v8::Promise> promise = data.GetPromise();
v8::Local<v8::Private> id_private = v8::Private::ForApi(
isolate, v8::String::NewFromUtf8Literal(isolate, "id"));
if (data.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
v8::Local<v8::Value> id;
if (!promise->GetPrivate(context, id_private).ToLocal(&id)) return;
if (!id->IsInt32()) return;
v8_inspector::V8Inspector* inspector =
InspectorIsolateData::FromContext(context)->inspector_.get();
v8::SealHandleScope seal_handle_scope(isolate);
const char* reason_str = "Handler added to rejected promise";
inspector->exceptionRevoked(
context, id.As<v8::Int32>()->Value(),
v8_inspector::StringView(reinterpret_cast<const uint8_t*>(reason_str),
strlen(reason_str)));
return;
}
v8::Local<v8::Value> exception = data.GetValue();
int exception_id = HandleMessage(
v8::Exception::CreateMessage(isolate, exception), exception);
if (exception_id) {
if (promise
->SetPrivate(isolate->GetCurrentContext(), id_private,
v8::Int32::New(isolate, exception_id))
.IsNothing()) {
// Handling the |message| above calls back into JavaScript (by reporting
// it via CDP) in case of `inspector-test`, and can lead to terminating
// execution on the |isolate|, in which case the API call above will
// return immediately.
DCHECK(isolate->IsExecutionTerminating());
}
}
}
void InspectorIsolateData::FireContextCreated(v8::Local<v8::Context> context,
int context_group_id,
v8_inspector::StringView name) {
v8_inspector::V8ContextInfo info(context, context_group_id, name);
info.hasMemoryOnConsole = true;
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->contextCreated(info);
}
void InspectorIsolateData::FireContextDestroyed(
v8::Local<v8::Context> context) {
v8::SealHandleScope seal_handle_scope(isolate());
inspector_->contextDestroyed(context);
}
void InspectorIsolateData::FreeContext(v8::Local<v8::Context> context) {
int context_group_id = GetContextGroupId(context);
auto it = contexts_.find(context_group_id);
if (it == contexts_.end()) return;
contexts_.erase(it);
}
std::vector<int> InspectorIsolateData::GetSessionIds(int context_group_id) {
std::vector<int> result;
for (auto& it : sessions_) {
if (context_group_by_session_[it.second.get()] == context_group_id)
result.push_back(it.first);
}
return result;
}
bool InspectorIsolateData::isInspectableHeapObject(
v8::Local<v8::Object> object) {
v8::Local<v8::Context> context = isolate()->GetCurrentContext();
v8::MicrotasksScope microtasks_scope(
context, v8::MicrotasksScope::kDoNotRunMicrotasks);
return !object->HasPrivate(context, not_inspectable_private_.Get(isolate()))
.FromMaybe(false);
}
v8::Local<v8::Context> InspectorIsolateData::ensureDefaultContextInGroup(
int context_group_id) {
return GetDefaultContext(context_group_id);
}
void InspectorIsolateData::SetCurrentTimeMS(double time) {
current_time_ = time;
current_time_set_ = true;
}
double InspectorIsolateData::currentTimeMS() {
if (current_time_set_) return current_time_;
return V8::GetCurrentPlatform()->CurrentClockTimeMillis();
}
void InspectorIsolateData::SetMemoryInfo(v8::Local<v8::Value> memory_info) {
memory_info_.Reset(isolate_.get(), memory_info);
}
void InspectorIsolateData::SetLogConsoleApiMessageCalls(bool log) {
log_console_api_message_calls_ = log;
}
void InspectorIsolateData::SetLogMaxAsyncCallStackDepthChanged(bool log) {
log_max_async_call_stack_depth_changed_ = log;
}
void InspectorIsolateData::SetAdditionalConsoleApi(
v8_inspector::StringView api_script) {
v8::HandleScope handle_scope(isolate());
additional_console_api_.Reset(isolate(), ToV8String(isolate(), api_script));
}
v8::MaybeLocal<v8::Value> InspectorIsolateData::memoryInfo(
v8::Isolate* isolate, v8::Local<v8::Context>) {
if (memory_info_.IsEmpty()) return v8::MaybeLocal<v8::Value>();
return memory_info_.Get(isolate);
}
void InspectorIsolateData::runMessageLoopOnPause(int) {
v8::SealHandleScope seal_handle_scope(isolate());
task_runner_->RunMessageLoop(true);
}
void InspectorIsolateData::runIfWaitingForDebugger(int) {
quitMessageLoopOnPause();
}
void InspectorIsolateData::quitMessageLoopOnPause() {
v8::SealHandleScope seal_handle_scope(isolate());
task_runner_->QuitMessageLoop();
}
void InspectorIsolateData::installAdditionalCommandLineAPI(
v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
if (additional_console_api_.IsEmpty()) return;
CHECK(context->GetIsolate() == isolate());
v8::HandleScope handle_scope(isolate());
v8::Context::Scope context_scope(context);
v8::ScriptOrigin origin(isolate(), v8::String::NewFromUtf8Literal(
isolate(), "internal-console-api"));
v8::ScriptCompiler::Source scriptSource(
additional_console_api_.Get(isolate()), origin);
v8::MaybeLocal<v8::Script> script =
v8::ScriptCompiler::Compile(context, &scriptSource);
CHECK(!script.ToLocalChecked()->Run(context).IsEmpty());
}
void InspectorIsolateData::consoleAPIMessage(
int contextGroupId, v8::Isolate::MessageErrorLevel level,
const v8_inspector::StringView& message,
const v8_inspector::StringView& url, unsigned lineNumber,
unsigned columnNumber, v8_inspector::V8StackTrace* stack) {
if (!log_console_api_message_calls_) return;
switch (level) {
case v8::Isolate::kMessageLog:
fprintf(stdout, "log: ");
break;
case v8::Isolate::kMessageDebug:
fprintf(stdout, "debug: ");
break;
case v8::Isolate::kMessageInfo:
fprintf(stdout, "info: ");
break;
case v8::Isolate::kMessageError:
fprintf(stdout, "error: ");
break;
case v8::Isolate::kMessageWarning:
fprintf(stdout, "warning: ");
break;
case v8::Isolate::kMessageAll:
break;
}
Print(isolate_.get(), message);
fprintf(stdout, " (");
Print(isolate_.get(), url);
fprintf(stdout, ":%d:%d)", lineNumber, columnNumber);
Print(isolate_.get(), stack->toString()->string());
fprintf(stdout, "\n");
}
void InspectorIsolateData::maxAsyncCallStackDepthChanged(int depth) {
if (!log_max_async_call_stack_depth_changed_) return;
fprintf(stdout, "maxAsyncCallStackDepthChanged: %d\n", depth);
}
void InspectorIsolateData::SetResourceNamePrefix(v8::Local<v8::String> prefix) {
resource_name_prefix_.Reset(isolate(), prefix);
}
bool InspectorIsolateData::AssociateExceptionData(
v8::Local<v8::Value> exception, v8::Local<v8::Name> key,
v8::Local<v8::Value> value) {
return inspector_->associateExceptionData(
this->isolate()->GetCurrentContext(), exception, key, value);
}
void InspectorIsolateData::WaitForDebugger(int context_group_id) {
DCHECK(!waiting_for_debugger_);
waiting_for_debugger_ = true;
runMessageLoopOnPause(context_group_id);
waiting_for_debugger_ = false;
}
namespace {
class StringBufferImpl : public v8_inspector::StringBuffer {
public:
StringBufferImpl(v8::Isolate* isolate, v8::Local<v8::String> string)
: data_(ToVector(isolate, string)) {}
v8_inspector::StringView string() const override {
return v8_inspector::StringView(data_.data(), data_.size());
}
private:
std::vector<uint16_t> data_;
};
} // anonymous namespace
std::unique_ptr<v8_inspector::StringBuffer>
InspectorIsolateData::resourceNameToUrl(
const v8_inspector::StringView& resourceName) {
if (resource_name_prefix_.IsEmpty()) return nullptr;
v8::HandleScope handle_scope(isolate());
v8::Local<v8::String> name = ToV8String(isolate(), resourceName);
v8::Local<v8::String> prefix = resource_name_prefix_.Get(isolate());
v8::Local<v8::String> url = v8::String::Concat(isolate(), prefix, name);
return std::make_unique<StringBufferImpl>(isolate(), url);
}
int64_t InspectorIsolateData::generateUniqueId() {
static int64_t last_unique_id = 0L;
// Keep it not too random for tests.
return ++last_unique_id;
}
} // namespace internal
} // namespace v8