diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-29 10:46:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-02 12:02:10 +0000 |
commit | 99677208ff3b216fdfec551fbe548da5520cd6fb (patch) | |
tree | 476a4865c10320249360e859d8fdd3e01833b03a /chromium/v8/src/d8 | |
parent | c30a6232df03e1efbd9f3b226777b07e087a1122 (diff) | |
download | qtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz |
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/v8/src/d8')
-rw-r--r-- | chromium/v8/src/d8/async-hooks-wrapper.cc | 1 | ||||
-rw-r--r-- | chromium/v8/src/d8/cov.cc | 58 | ||||
-rw-r--r-- | chromium/v8/src/d8/cov.h | 7 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8-platforms.cc | 52 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8.cc | 337 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8.h | 48 |
6 files changed, 392 insertions, 111 deletions
diff --git a/chromium/v8/src/d8/async-hooks-wrapper.cc b/chromium/v8/src/d8/async-hooks-wrapper.cc index f96aad41233..1d160cdb0da 100644 --- a/chromium/v8/src/d8/async-hooks-wrapper.cc +++ b/chromium/v8/src/d8/async-hooks-wrapper.cc @@ -128,6 +128,7 @@ void AsyncHooks::ShellPromiseHook(PromiseHookType type, Local<Promise> promise, HandleScope handle_scope(hooks->isolate_); Local<Context> currentContext = hooks->isolate_->GetCurrentContext(); + DCHECK(!currentContext.IsEmpty()); if (type == PromiseHookType::kInit) { ++hooks->current_async_id; diff --git a/chromium/v8/src/d8/cov.cc b/chromium/v8/src/d8/cov.cc index 47e2af599c0..d95134a9958 100644 --- a/chromium/v8/src/d8/cov.cc +++ b/chromium/v8/src/d8/cov.cc @@ -24,10 +24,13 @@ struct shmem_data { struct shmem_data* shmem; -uint32_t *__edges_start, *__edges_stop; -void __sanitizer_cov_reset_edgeguards() { +uint32_t *edges_start, *edges_stop; +uint32_t builtins_start; +uint32_t builtins_edge_count; + +void sanitizer_cov_reset_edgeguards() { uint32_t N = 0; - for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++) + for (uint32_t* x = edges_start; x < edges_stop && N < MAX_EDGES; x++) *x = ++N; } @@ -53,15 +56,27 @@ extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, } } - __edges_start = start; - __edges_stop = stop; - __sanitizer_cov_reset_edgeguards(); + edges_start = start; + edges_stop = stop; + sanitizer_cov_reset_edgeguards(); shmem->num_edges = static_cast<uint32_t>(stop - start); + builtins_start = 1 + shmem->num_edges; printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n", shm_key, shmem->num_edges); } +uint32_t sanitizer_cov_count_discovered_edges() { + uint32_t on_edges_counter = 0; + for (uint32_t i = 1; i < builtins_start; ++i) { + // TODO(ralbovsky): Can be optimized for fewer divisions. + if (shmem->edges[i / 8] & (1 << (i % 8))) { + ++on_edges_counter; + } + } + return on_edges_counter; +} + extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) { // There's a small race condition here: if this function executes in two // threads for the same edge at the same time, the first thread might disable @@ -72,3 +87,34 @@ extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) { shmem->edges[index / 8] |= 1 << (index % 8); *guard = 0; } + +void cov_init_builtins_edges(uint32_t num_edges) { + if (num_edges + shmem->num_edges > MAX_EDGES) { + printf( + "[COV] Error: Insufficient amount of edges left for builtins " + "coverage.\n"); + exit(-1); + } + builtins_edge_count = num_edges; + builtins_start = 1 + shmem->num_edges; + shmem->num_edges += builtins_edge_count; + printf("[COV] Additional %d edges for builtins initialized.\n", num_edges); +} + +// This function is ran once per REPRL loop. In case of crash the coverage of +// crash will not be stored in shared memory. Therefore, it would be useful, if +// we could store these coverage information into shared memory in real time. +void cov_update_builtins_basic_block_coverage( + const std::vector<bool>& cov_map) { + if (cov_map.size() != builtins_edge_count) { + printf("[COV] Error: Size of builtins cov map changed.\n"); + exit(-1); + } + for (uint32_t i = 0; i < cov_map.size(); ++i) { + if (cov_map[i]) { + // TODO(ralbovsky): Can be optimized for fewer divisions. + shmem->edges[(i + builtins_start) / 8] |= + (1 << ((i + builtins_start) % 8)); + } + } +} diff --git a/chromium/v8/src/d8/cov.h b/chromium/v8/src/d8/cov.h index d2d26752337..0c7dc6bac42 100644 --- a/chromium/v8/src/d8/cov.h +++ b/chromium/v8/src/d8/cov.h @@ -10,6 +10,11 @@ // memory // https://clang.llvm.org/docs/SanitizerCoverage.html -void __sanitizer_cov_reset_edgeguards(); +#include <vector> + +void sanitizer_cov_reset_edgeguards(); +uint32_t sanitizer_cov_count_discovered_edges(); +void cov_init_builtins_edges(uint32_t num_edges); +void cov_update_builtins_basic_block_coverage(const std::vector<bool>& cov_map); #endif // V8_D8_COV_H_ diff --git a/chromium/v8/src/d8/d8-platforms.cc b/chromium/v8/src/d8/d8-platforms.cc index 8f528e2d43c..a170e39c923 100644 --- a/chromium/v8/src/d8/d8-platforms.cc +++ b/chromium/v8/src/d8/d8-platforms.cc @@ -16,7 +16,7 @@ namespace v8 { -class PredictablePlatform : public Platform { +class PredictablePlatform final : public Platform { public: explicit PredictablePlatform(std::unique_ptr<Platform> platform) : platform_(std::move(platform)) { @@ -63,6 +63,11 @@ class PredictablePlatform : public Platform { bool IdleTasksEnabled(Isolate* isolate) override { return false; } + std::unique_ptr<JobHandle> PostJob( + TaskPriority priority, std::unique_ptr<JobTask> job_task) override { + return platform_->PostJob(priority, std::move(job_task)); + } + double MonotonicallyIncreasingTime() override { return synthetic_time_in_sec_ += 0.00001; } @@ -89,7 +94,7 @@ std::unique_ptr<Platform> MakePredictablePlatform( return std::make_unique<PredictablePlatform>(std::move(platform)); } -class DelayedTasksPlatform : public Platform { +class DelayedTasksPlatform final : public Platform { public: explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform) : platform_(std::move(platform)) { @@ -159,6 +164,11 @@ class DelayedTasksPlatform : public Platform { return platform_->IdleTasksEnabled(isolate); } + std::unique_ptr<JobHandle> PostJob( + TaskPriority priority, std::unique_ptr<JobTask> job_task) override { + return platform_->PostJob(priority, MakeDelayedJob(std::move(job_task))); + } + double MonotonicallyIncreasingTime() override { return platform_->MonotonicallyIncreasingTime(); } @@ -222,11 +232,12 @@ class DelayedTasksPlatform : public Platform { } }; - class DelayedTask : public Task { + class DelayedTask final : public Task { public: DelayedTask(std::unique_ptr<Task> task, int32_t delay_ms) : task_(std::move(task)), delay_ms_(delay_ms) {} - void Run() final { + + void Run() override { base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_)); task_->Run(); } @@ -236,11 +247,12 @@ class DelayedTasksPlatform : public Platform { int32_t delay_ms_; }; - class DelayedIdleTask : public IdleTask { + class DelayedIdleTask final : public IdleTask { public: DelayedIdleTask(std::unique_ptr<IdleTask> task, int32_t delay_ms) : task_(std::move(task)), delay_ms_(delay_ms) {} - void Run(double deadline_in_seconds) final { + + void Run(double deadline_in_seconds) override { base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_)); task_->Run(deadline_in_seconds); } @@ -250,6 +262,29 @@ class DelayedTasksPlatform : public Platform { int32_t delay_ms_; }; + class DelayedJob final : public JobTask { + public: + DelayedJob(std::unique_ptr<JobTask> job_task, int32_t delay_ms) + : job_task_(std::move(job_task)), delay_ms_(delay_ms) {} + + void Run(JobDelegate* delegate) override { + // If this job is being executed via worker tasks (as e.g. the + // {DefaultJobHandle} implementation does it), the worker task would + // already include a delay. In order to not depend on that, we add our own + // delay here anyway. + base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_)); + job_task_->Run(delegate); + } + + size_t GetMaxConcurrency() const override { + return job_task_->GetMaxConcurrency(); + } + + private: + std::unique_ptr<JobTask> job_task_; + int32_t delay_ms_; + }; + std::unique_ptr<Platform> platform_; // The Mutex protects the RNG, which is used by foreground and background @@ -279,6 +314,11 @@ class DelayedTasksPlatform : public Platform { GetRandomDelayInMilliseconds()); } + std::unique_ptr<JobTask> MakeDelayedJob(std::unique_ptr<JobTask> task) { + return std::make_unique<DelayedJob>(std::move(task), + GetRandomDelayInMilliseconds()); + } + DISALLOW_COPY_AND_ASSIGN(DelayedTasksPlatform); }; diff --git a/chromium/v8/src/d8/d8.cc b/chromium/v8/src/d8/d8.cc index 117df1cc526..26ccb62c681 100644 --- a/chromium/v8/src/d8/d8.cc +++ b/chromium/v8/src/d8/d8.cc @@ -36,7 +36,9 @@ #include "src/d8/d8.h" #include "src/debug/debug-interface.h" #include "src/deoptimizer/deoptimizer.h" +#include "src/diagnostics/basic-block-profiler.h" #include "src/execution/vm-state-inl.h" +#include "src/flags/flags.h" #include "src/handles/maybe-handles.h" #include "src/init/v8.h" #include "src/interpreter/interpreter.h" @@ -50,6 +52,7 @@ #include "src/profiler/profile-generator.h" #include "src/sanitizer/msan.h" #include "src/snapshot/snapshot.h" +#include "src/tasks/cancelable-task.h" #include "src/trap-handler/trap-handler.h" #include "src/utils/ostreams.h" #include "src/utils/utils.h" @@ -336,10 +339,9 @@ static MaybeLocal<Value> TryGetValue(v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object, const char* property) { - Local<String> v8_str = - String::NewFromUtf8(isolate, property).FromMaybe(Local<String>()); - if (v8_str.IsEmpty()) return Local<Value>(); - return object->Get(context, v8_str); + MaybeLocal<String> v8_str = String::NewFromUtf8(isolate, property); + if (v8_str.IsEmpty()) return {}; + return object->Get(context, v8_str.ToLocalChecked()); } static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context, @@ -347,7 +349,8 @@ static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context, return TryGetValue(isolate, context, object, property).ToLocalChecked(); } -Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) { +std::shared_ptr<Worker> GetWorkerFromInternalField(Isolate* isolate, + Local<Object> object) { if (object->InternalFieldCount() != 1) { Throw(isolate, "this is not a Worker"); return nullptr; @@ -359,7 +362,7 @@ Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) { return nullptr; } auto managed = i::Handle<i::Managed<Worker>>::cast(handle); - return managed->raw(); + return managed->get(); } base::Thread::Options GetThreadOptions(const char* name) { @@ -1182,10 +1185,9 @@ void PerIsolateData::AddUnhandledPromise(Local<Promise> promise, Local<Message> message, Local<Value> exception) { DCHECK_EQ(promise->GetIsolate(), isolate_); - unhandled_promises_.push_back( - std::make_tuple(v8::Global<v8::Promise>(isolate_, promise), - v8::Global<v8::Message>(isolate_, message), - v8::Global<v8::Value>(isolate_, exception))); + unhandled_promises_.emplace_back(v8::Global<v8::Promise>(isolate_, promise), + v8::Global<v8::Message>(isolate_, message), + v8::Global<v8::Value>(isolate_, exception)); } size_t PerIsolateData::GetUnhandledPromiseCount() { @@ -1193,16 +1195,17 @@ size_t PerIsolateData::GetUnhandledPromiseCount() { } int PerIsolateData::HandleUnhandledPromiseRejections() { - int unhandled_promises_count = 0; v8::HandleScope scope(isolate_); - for (auto& tuple : unhandled_promises_) { + // Ignore promises that get added during error reporting. + size_t unhandled_promises_count = unhandled_promises_.size(); + for (size_t i = 0; i < unhandled_promises_count; i++) { + const auto& tuple = unhandled_promises_[i]; Local<v8::Message> message = std::get<1>(tuple).Get(isolate_); Local<v8::Value> value = std::get<2>(tuple).Get(isolate_); Shell::ReportException(isolate_, message, value); - unhandled_promises_count++; } unhandled_promises_.clear(); - return unhandled_promises_count; + return static_cast<int>(unhandled_promises_count); } PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) { @@ -1684,8 +1687,10 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { if (args.Length() > 1 && args[1]->IsObject()) { Local<Object> object = args[1].As<Object>(); Local<Context> context = isolate->GetCurrentContext(); - Local<Value> value = GetValue(args.GetIsolate(), context, object, "type"); - if (value->IsString()) { + Local<Value> value; + if (TryGetValue(args.GetIsolate(), context, object, "type") + .ToLocal(&value) && + value->IsString()) { Local<String> worker_type = value->ToString(context).ToLocalChecked(); String::Utf8Value str(isolate, worker_type); if (strcmp("string", *str) == 0) { @@ -1758,8 +1763,9 @@ void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { return; } - Worker* worker = GetWorkerFromInternalField(isolate, args.Holder()); - if (!worker) { + std::shared_ptr<Worker> worker = + GetWorkerFromInternalField(isolate, args.Holder()); + if (!worker.get()) { return; } @@ -1776,8 +1782,9 @@ void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); HandleScope handle_scope(isolate); - Worker* worker = GetWorkerFromInternalField(isolate, args.Holder()); - if (!worker) { + std::shared_ptr<Worker> worker = + GetWorkerFromInternalField(isolate, args.Holder()); + if (!worker.get()) { return; } @@ -1793,14 +1800,28 @@ void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); HandleScope handle_scope(isolate); - Worker* worker = GetWorkerFromInternalField(isolate, args.Holder()); - if (!worker) { + std::shared_ptr<Worker> worker = + GetWorkerFromInternalField(isolate, args.Holder()); + if (!worker.get()) { return; } worker->Terminate(); } +void Shell::WorkerTerminateAndWait( + const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope handle_scope(isolate); + std::shared_ptr<Worker> worker = + GetWorkerFromInternalField(isolate, args.Holder()); + if (!worker.get()) { + return; + } + + worker->TerminateAndWaitForThread(); +} + void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) { int exit_code = (*args)[0] ->Int32Value(args->GetIsolate()->GetCurrentContext()) @@ -2131,6 +2152,10 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) { FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(), worker_signature)); worker_fun_template->PrototypeTemplate()->Set( + isolate, "terminateAndWait", + FunctionTemplate::New(isolate, WorkerTerminateAndWait, Local<Value>(), + worker_signature)); + worker_fun_template->PrototypeTemplate()->Set( isolate, "postMessage", FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(), worker_signature)); @@ -2267,6 +2292,12 @@ void Shell::Initialize(Isolate* isolate, D8Console* console, #ifdef V8_FUZZILLI // Let the parent process (Fuzzilli) know we are ready. + if (options.fuzzilli_enable_builtins_coverage) { + cov_init_builtins_edges(static_cast<uint32_t>( + i::BasicBlockProfiler::Get() + ->GetCoverageBitmap(reinterpret_cast<i::Isolate*>(isolate)) + .size())); + } char helo[] = "HELO"; if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) { fuzzilli_reprl = false; @@ -2934,14 +2965,13 @@ void SerializationDataQueue::Clear() { data_.clear(); } -Worker::Worker(const char* script) - : in_semaphore_(0), - out_semaphore_(0), - thread_(nullptr), - script_(i::StrDup(script)), - running_(false) {} +Worker::Worker(const char* script) : script_(i::StrDup(script)) { + running_.store(false); +} Worker::~Worker() { + DCHECK_NULL(isolate_); + delete thread_; thread_ = nullptr; delete[] script_; @@ -2949,10 +2979,12 @@ Worker::~Worker() { } bool Worker::StartWorkerThread(std::shared_ptr<Worker> worker) { - worker->running_ = true; + worker->running_.store(true); auto thread = new WorkerThread(worker); worker->thread_ = thread; if (thread->Start()) { + // Wait until the worker is ready to receive messages. + worker->started_semaphore_.Wait(); Shell::AddRunningWorker(std::move(worker)); return true; } @@ -2971,54 +3003,156 @@ void Worker::WorkerThread::Run() { Shell::RemoveRunningWorker(worker); } +class ProcessMessageTask : public i::CancelableTask { + public: + ProcessMessageTask(i::CancelableTaskManager* task_manager, + std::shared_ptr<Worker> worker, + std::unique_ptr<SerializationData> data) + : i::CancelableTask(task_manager), + worker_(worker), + data_(std::move(data)) {} + + void RunInternal() override { worker_->ProcessMessage(std::move(data_)); } + + private: + std::shared_ptr<Worker> worker_; + std::unique_ptr<SerializationData> data_; +}; + void Worker::PostMessage(std::unique_ptr<SerializationData> data) { - in_queue_.Enqueue(std::move(data)); - in_semaphore_.Signal(); + // Hold the worker_mutex_ so that the worker thread can't delete task_runner_ + // after we've checked running_. + base::MutexGuard lock_guard(&worker_mutex_); + if (!running_.load()) { + return; + } + std::unique_ptr<v8::Task> task(new ProcessMessageTask( + task_manager_, shared_from_this(), std::move(data))); + task_runner_->PostNonNestableTask(std::move(task)); } +class TerminateTask : public i::CancelableTask { + public: + TerminateTask(i::CancelableTaskManager* task_manager, + std::shared_ptr<Worker> worker) + : i::CancelableTask(task_manager), worker_(worker) {} + + void RunInternal() override { + // Make sure the worker doesn't enter the task loop after processing this + // task. + worker_->running_.store(false); + } + + private: + std::shared_ptr<Worker> worker_; +}; + std::unique_ptr<SerializationData> Worker::GetMessage() { std::unique_ptr<SerializationData> result; while (!out_queue_.Dequeue(&result)) { // If the worker is no longer running, and there are no messages in the // queue, don't expect any more messages from it. - if (!base::Relaxed_Load(&running_)) break; + if (!running_.load()) { + break; + } out_semaphore_.Wait(); } return result; } void Worker::Terminate() { - base::Relaxed_Store(&running_, false); - // Post nullptr to wake the Worker thread message loop, and tell it to stop - // running. - PostMessage(nullptr); + // Hold the worker_mutex_ so that the worker thread can't delete task_runner_ + // after we've checked running_. + base::MutexGuard lock_guard(&worker_mutex_); + if (!running_.load()) { + return; + } + // Post a task to wake up the worker thread. + std::unique_ptr<v8::Task> task( + new TerminateTask(task_manager_, shared_from_this())); + task_runner_->PostTask(std::move(task)); } -void Worker::WaitForThread() { +void Worker::TerminateAndWaitForThread() { Terminate(); thread_->Join(); } +void Worker::ProcessMessage(std::unique_ptr<SerializationData> data) { + if (!running_.load()) { + return; + } + + DCHECK_NOT_NULL(isolate_); + HandleScope scope(isolate_); + Local<Context> context = context_.Get(isolate_); + Context::Scope cscope(context); + Local<Object> global = context->Global(); + + // Get the message handler. + Local<Value> onmessage = global + ->Get(context, String::NewFromUtf8Literal( + isolate_, "onmessage", + NewStringType::kInternalized)) + .ToLocalChecked(); + if (!onmessage->IsFunction()) { + return; + } + Local<Function> onmessage_fun = Local<Function>::Cast(onmessage); + + v8::TryCatch try_catch(isolate_); + try_catch.SetVerbose(true); + Local<Value> value; + if (Shell::DeserializeValue(isolate_, std::move(data)).ToLocal(&value)) { + Local<Value> argv[] = {value}; + MaybeLocal<Value> result = onmessage_fun->Call(context, global, 1, argv); + USE(result); + } +} + +void Worker::ProcessMessages() { + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate_); + i::SaveAndSwitchContext saved_context(i_isolate, i::Context()); + SealHandleScope shs(isolate_); + while (running_.load() && v8::platform::PumpMessageLoop( + g_default_platform, isolate_, + platform::MessageLoopBehavior::kWaitForWork)) { + if (running_.load()) { + MicrotasksScope::PerformCheckpoint(isolate_); + } + } +} + void Worker::ExecuteInThread() { Isolate::CreateParams create_params; create_params.array_buffer_allocator = Shell::array_buffer_allocator; - Isolate* isolate = Isolate::New(create_params); - D8Console console(isolate); - Shell::Initialize(isolate, &console, false); + isolate_ = Isolate::New(create_params); { - Isolate::Scope iscope(isolate); + base::MutexGuard lock_guard(&worker_mutex_); + task_runner_ = g_default_platform->GetForegroundTaskRunner(isolate_); + task_manager_ = + reinterpret_cast<i::Isolate*>(isolate_)->cancelable_task_manager(); + } + // The Worker is now ready to receive messages. + started_semaphore_.Signal(); + + D8Console console(isolate_); + Shell::Initialize(isolate_, &console, false); + { + Isolate::Scope iscope(isolate_); { - HandleScope scope(isolate); - PerIsolateData data(isolate); - Local<Context> context = Shell::CreateEvaluationContext(isolate); + HandleScope scope(isolate_); + PerIsolateData data(isolate_); + Local<Context> context = Shell::CreateEvaluationContext(isolate_); + context_.Reset(isolate_, context); { Context::Scope cscope(context); - PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); + PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate_)); Local<Object> global = context->Global(); - Local<Value> this_value = External::New(isolate, this); + Local<Value> this_value = External::New(isolate_, this); Local<FunctionTemplate> postmessage_fun_template = - FunctionTemplate::New(isolate, PostMessageOut, this_value); + FunctionTemplate::New(isolate_, PostMessageOut, this_value); Local<Function> postmessage_fun; if (postmessage_fun_template->GetFunction(context).ToLocal( @@ -3026,56 +3160,49 @@ void Worker::ExecuteInThread() { global ->Set(context, v8::String::NewFromUtf8Literal( - isolate, "postMessage", NewStringType::kInternalized), + isolate_, "postMessage", NewStringType::kInternalized), postmessage_fun) .FromJust(); } // First run the script Local<String> file_name = - String::NewFromUtf8Literal(isolate, "unnamed"); + String::NewFromUtf8Literal(isolate_, "unnamed"); Local<String> source = - String::NewFromUtf8(isolate, script_).ToLocalChecked(); + String::NewFromUtf8(isolate_, script_).ToLocalChecked(); if (Shell::ExecuteString( - isolate, source, file_name, Shell::kNoPrintResult, + isolate_, source, file_name, Shell::kNoPrintResult, Shell::kReportExceptions, Shell::kProcessMessageQueue)) { - // Get the message handler + // Check that there's a message handler Local<Value> onmessage = global - ->Get(context, - String::NewFromUtf8Literal( - isolate, "onmessage", NewStringType::kInternalized)) + ->Get(context, String::NewFromUtf8Literal( + isolate_, "onmessage", + NewStringType::kInternalized)) .ToLocalChecked(); if (onmessage->IsFunction()) { - Local<Function> onmessage_fun = Local<Function>::Cast(onmessage); - SealHandleScope shs(isolate); // Now wait for messages - while (true) { - in_semaphore_.Wait(); - std::unique_ptr<SerializationData> data; - if (!in_queue_.Dequeue(&data)) continue; - if (!data) break; - v8::TryCatch try_catch(isolate); - try_catch.SetVerbose(true); - HandleScope scope(isolate); - Local<Value> value; - if (Shell::DeserializeValue(isolate, std::move(data)) - .ToLocal(&value)) { - Local<Value> argv[] = {value}; - MaybeLocal<Value> result = - onmessage_fun->Call(context, global, 1, argv); - USE(result); - } - } - // TODO(cbruni): Check for unhandled promises here. + ProcessMessages(); } } } DisposeModuleEmbedderData(context); } - Shell::CollectGarbage(isolate); + Shell::CollectGarbage(isolate_); } - isolate->Dispose(); + // TODO(cbruni): Check for unhandled promises here. + { + // Hold the mutex to ensure running_ and task_runner_ change state + // atomically (see Worker::PostMessage which reads them). + base::MutexGuard lock_guard(&worker_mutex_); + running_.store(false); + task_runner_.reset(); + task_manager_ = nullptr; + } + context_.Reset(); + platform::NotifyIsolateShutdown(g_default_platform, isolate_); + isolate_->Dispose(); + isolate_ = nullptr; // Post nullptr to wake the thread waiting on GetMessage() if there is one. out_queue_.Enqueue(nullptr); @@ -3159,10 +3286,10 @@ bool Shell::SetOptions(int argc, char* argv[]) { } else if (strcmp(argv[i], "--omit-quit") == 0) { options.omit_quit = true; argv[i] = nullptr; - } else if (strcmp(argv[i], "--no-wait-for-wasm") == 0) { + } else if (strcmp(argv[i], "--no-wait-for-background-tasks") == 0) { // TODO(herhut) Remove this flag once wasm compilation is fully // isolate-independent. - options.wait_for_wasm = false; + options.wait_for_background_tasks = false; argv[i] = nullptr; } else if (strcmp(argv[i], "-f") == 0) { // Ignore any -f flags for compatibility with other stand-alone @@ -3261,13 +3388,30 @@ bool Shell::SetOptions(int argc, char* argv[]) { options.cpu_profiler = true; options.cpu_profiler_print = true; argv[i] = nullptr; +#ifdef V8_FUZZILLI + } else if (strcmp(argv[i], "--no-fuzzilli-enable-builtins-coverage") == 0) { + options.fuzzilli_enable_builtins_coverage = false; + argv[i] = nullptr; + } else if (strcmp(argv[i], "--fuzzilli-coverage-statistics") == 0) { + options.fuzzilli_coverage_statistics = true; + argv[i] = nullptr; +#endif } else if (strcmp(argv[i], "--fuzzy-module-file-extensions") == 0) { options.fuzzy_module_file_extensions = true; argv[i] = nullptr; } } - v8::V8::SetFlagsFromCommandLine(&argc, argv, true); + const char* usage = + "Synopsis:\n" + " shell [options] [--shell] [<file>...]\n" + " d8 [options] [-e <string>] [--shell] [[--module] <file>...]\n\n" + " -e execute a string in V8\n" + " --shell run an interactive JavaScript shell\n" + " --module execute a file as a JavaScript module\n\n"; + using HelpOptions = i::FlagList::HelpOptions; + i::FlagList::SetFlagsFromCommandLine(&argc, argv, true, + HelpOptions(HelpOptions::kExit, usage)); options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator; options.mock_arraybuffer_allocator_limit = i::FLAG_mock_arraybuffer_allocator_limit; @@ -3457,10 +3601,8 @@ bool Shell::CompleteMessageLoop(Isolate* isolate) { auto get_waiting_behaviour = [isolate]() { base::MutexGuard guard(isolate_status_lock_.Pointer()); DCHECK_GT(isolate_status_.count(isolate), 0); - i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); - i::wasm::WasmEngine* wasm_engine = i_isolate->wasm_engine(); - bool should_wait = (options.wait_for_wasm && - wasm_engine->HasRunningCompileJob(i_isolate)) || + bool should_wait = (options.wait_for_background_tasks && + isolate->HasPendingBackgroundTasks()) || isolate_status_[isolate] || isolate_running_streaming_tasks_[isolate] > 0; return should_wait ? platform::MessageLoopBehavior::kWaitForWork @@ -3790,7 +3932,7 @@ void Shell::WaitForRunningWorkers() { } for (auto& worker : workers_copy) { - worker->WaitForThread(); + worker->TerminateAndWaitForThread(); } // Now that all workers are terminated, we can re-enable Worker creation. @@ -4055,13 +4197,34 @@ int Shell::Main(int argc, char* argv[]) { evaluation_context_.Reset(); stringify_function_.Reset(); CollectGarbage(isolate); - #ifdef V8_FUZZILLI // Send result to parent (fuzzilli) and reset edge guards. if (fuzzilli_reprl) { int status = result << 8; + std::vector<bool> bitmap; + if (options.fuzzilli_enable_builtins_coverage) { + bitmap = i::BasicBlockProfiler::Get()->GetCoverageBitmap( + reinterpret_cast<i::Isolate*>(isolate)); + cov_update_builtins_basic_block_coverage(bitmap); + } + if (options.fuzzilli_coverage_statistics) { + int tot = 0; + for (bool b : bitmap) { + if (b) tot++; + } + static int iteration_counter = 0; + std::ofstream covlog("covlog.txt", std::ios::app); + covlog << iteration_counter << "\t" << tot << "\t" + << sanitizer_cov_count_discovered_edges() << "\t" + << bitmap.size() << std::endl; + iteration_counter++; + } CHECK_EQ(write(REPRL_CWFD, &status, 4), 4); - __sanitizer_cov_reset_edgeguards(); + sanitizer_cov_reset_edgeguards(); + if (options.fuzzilli_enable_builtins_coverage) { + i::BasicBlockProfiler::Get()->ResetCounts( + reinterpret_cast<i::Isolate*>(isolate)); + } } #endif // V8_FUZZILLI } while (fuzzilli_reprl); diff --git a/chromium/v8/src/d8/d8.h b/chromium/v8/src/d8/d8.h index 203e9edb0cb..6b2c08fd6ea 100644 --- a/chromium/v8/src/d8/d8.h +++ b/chromium/v8/src/d8/d8.h @@ -25,6 +25,10 @@ namespace v8 { class D8Console; +namespace internal { +class CancelableTaskManager; +} // namespace internal + // A single counter in a counter collection. class Counter { public: @@ -158,14 +162,14 @@ class SerializationDataQueue { std::vector<std::unique_ptr<SerializationData>> data_; }; -class Worker { +class Worker : public std::enable_shared_from_this<Worker> { public: explicit Worker(const char* script); ~Worker(); - // Post a message to the worker's incoming message queue. The worker will - // take ownership of the SerializationData. - // This function should only be called by the thread that created the Worker. + // Post a message to the worker. The worker will take ownership of the + // SerializationData. This function should only be called by the thread that + // created the Worker. void PostMessage(std::unique_ptr<SerializationData> data); // Synchronously retrieve messages from the worker's outgoing message queue. // If there is no message in the queue, block until a message is available. @@ -179,12 +183,18 @@ class Worker { void Terminate(); // Terminate and join the thread. // This function can be called by any thread. - void WaitForThread(); + void TerminateAndWaitForThread(); // Start running the given worker in another thread. static bool StartWorkerThread(std::shared_ptr<Worker> worker); private: + friend class ProcessMessageTask; + friend class TerminateTask; + + void ProcessMessage(std::unique_ptr<SerializationData> data); + void ProcessMessages(); + class WorkerThread : public base::Thread { public: explicit WorkerThread(std::shared_ptr<Worker> worker) @@ -200,13 +210,25 @@ class Worker { void ExecuteInThread(); static void PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args); - base::Semaphore in_semaphore_; - base::Semaphore out_semaphore_; - SerializationDataQueue in_queue_; + base::Semaphore out_semaphore_{0}; SerializationDataQueue out_queue_; - base::Thread* thread_; + base::Thread* thread_ = nullptr; char* script_; - base::Atomic32 running_; + std::atomic<bool> running_; + // For signalling that the worker has started. + base::Semaphore started_semaphore_{0}; + + // For posting tasks to the worker + std::shared_ptr<TaskRunner> task_runner_; + i::CancelableTaskManager* task_manager_; + + // Protects reading / writing task_runner_. (The TaskRunner itself doesn't + // need locking, but accessing the Worker's data member does.) + base::Mutex worker_mutex_; + + // Only accessed by the worker thread. + Isolate* isolate_ = nullptr; + v8::Persistent<v8::Context> context_; }; class PerIsolateData { @@ -270,10 +292,12 @@ class ShellOptions { ~ShellOptions() { delete[] isolate_sources; } + bool fuzzilli_coverage_statistics = false; + bool fuzzilli_enable_builtins_coverage = true; bool send_idle_notification = false; bool invoke_weak_callbacks = false; bool omit_quit = false; - bool wait_for_wasm = true; + bool wait_for_background_tasks = true; bool stress_opt = false; int stress_runs = 1; bool stress_snapshot = false; @@ -405,6 +429,8 @@ class Shell : public i::AllStatic { const v8::FunctionCallbackInfo<v8::Value>& args); static void WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args); static void WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args); + static void WorkerTerminateAndWait( + const v8::FunctionCallbackInfo<v8::Value>& args); // The OS object on the global object contains methods for performing // operating system calls: // |