[d8] Add --stress-delay-tasks flag

This flag stresses different interleavings of background and foreground
tasks by delaying the execution of each task by a random value between
0 and 100ms (with a quadratic distribution favoring smaller delayes).

The implementation is encapsulated in the new {DelayedTasksPlatform}
class, which wraps each task in a {DelayedTask} which first sleeps for
the given number of microseconds, then executes the actual task.

Both the old {PredictablePlatform} and the new {DelayedTasksPlatform}
are moved to the new d8-platforms.cc file with an interface to create
them in d8-platforms.h.

R=yangguo@chromium.org, mslekova@chromium.org

Bug: v8:8278
Change-Id: I5847fb2da31ffde773195da7ad3f56a0390cc05b
Reviewed-on: https://chromium-review.googlesource.com/c/1270592
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56496}
This commit is contained in:
Clemens Hammacher 2018-10-09 15:04:29 +02:00 committed by Commit Bot
parent 96fba5f37f
commit 7c2aef087e
5 changed files with 363 additions and 94 deletions

View File

@ -3550,6 +3550,8 @@ v8_executable("d8") {
"src/async-hooks-wrapper.h",
"src/d8-console.cc",
"src/d8-console.h",
"src/d8-platforms.cc",
"src/d8-platforms.h",
"src/d8.cc",
"src/d8.h",
]

308
src/d8-platforms.cc Normal file
View File

@ -0,0 +1,308 @@
// Copyright 2018 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 <memory>
#include <unordered_map>
#include "include/v8-platform.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/base/template-utils.h"
#include "src/base/utils/random-number-generator.h"
#include "src/d8-platforms.h"
namespace v8 {
class PredictablePlatform : public Platform {
public:
explicit PredictablePlatform(std::unique_ptr<Platform> platform)
: platform_(std::move(platform)) {
DCHECK_NOT_NULL(platform_);
}
PageAllocator* GetPageAllocator() override {
return platform_->GetPageAllocator();
}
void OnCriticalMemoryPressure() override {
platform_->OnCriticalMemoryPressure();
}
bool OnCriticalMemoryPressure(size_t length) override {
return platform_->OnCriticalMemoryPressure(length);
}
std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
v8::Isolate* isolate) override {
return platform_->GetForegroundTaskRunner(isolate);
}
int NumberOfWorkerThreads() override { return 0; }
void CallOnWorkerThread(std::unique_ptr<Task> task) override {
// It's not defined when background tasks are being executed, so we can just
// execute them right away.
task->Run();
}
void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
double delay_in_seconds) override {
// Never run delayed tasks.
}
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
// This is a deprecated function and should not be called anymore.
UNREACHABLE();
}
void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
double delay_in_seconds) override {
// This is a deprecated function and should not be called anymore.
UNREACHABLE();
}
void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
UNREACHABLE();
}
bool IdleTasksEnabled(Isolate* isolate) override { return false; }
double MonotonicallyIncreasingTime() override {
return synthetic_time_in_sec_ += 0.00001;
}
double CurrentClockTimeMillis() override {
return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond;
}
v8::TracingController* GetTracingController() override {
return platform_->GetTracingController();
}
Platform* platform() const { return platform_.get(); }
private:
double synthetic_time_in_sec_ = 0.0;
std::unique_ptr<Platform> platform_;
DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
};
std::unique_ptr<Platform> MakePredictablePlatform(
std::unique_ptr<Platform> platform) {
return base::make_unique<PredictablePlatform>(std::move(platform));
}
class DelayedTasksPlatform : public Platform {
public:
explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform)
: platform_(std::move(platform)) {
DCHECK_NOT_NULL(platform_);
}
explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform,
int64_t random_seed)
: platform_(std::move(platform)), rng_(random_seed) {
DCHECK_NOT_NULL(platform_);
}
~DelayedTasksPlatform() {
// When the platform shuts down, all task runners must be freed.
DCHECK_EQ(0, delayed_task_runners_.size());
}
PageAllocator* GetPageAllocator() override {
return platform_->GetPageAllocator();
}
void OnCriticalMemoryPressure() override {
platform_->OnCriticalMemoryPressure();
}
bool OnCriticalMemoryPressure(size_t length) override {
return platform_->OnCriticalMemoryPressure(length);
}
std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
v8::Isolate* isolate) override {
std::shared_ptr<TaskRunner> runner =
platform_->GetForegroundTaskRunner(isolate);
base::LockGuard<base::Mutex> lock_guard(&mutex_);
// Check if we can re-materialize the weak ptr in our map.
std::weak_ptr<DelayedTaskRunner>& weak_delayed_runner =
delayed_task_runners_[runner.get()];
std::shared_ptr<DelayedTaskRunner> delayed_runner =
weak_delayed_runner.lock();
if (!delayed_runner) {
// Create a new {DelayedTaskRunner} and keep a weak reference in our map.
delayed_runner.reset(new DelayedTaskRunner(runner, this),
DelayedTaskRunnerDeleter{});
weak_delayed_runner = delayed_runner;
}
return std::move(delayed_runner);
}
int NumberOfWorkerThreads() override {
return platform_->NumberOfWorkerThreads();
}
void CallOnWorkerThread(std::unique_ptr<Task> task) override {
platform_->CallOnWorkerThread(MakeDelayedTask(std::move(task)));
}
void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
double delay_in_seconds) override {
platform_->CallDelayedOnWorkerThread(MakeDelayedTask(std::move(task)),
delay_in_seconds);
}
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
// This is a deprecated function and should not be called anymore.
UNREACHABLE();
}
void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
double delay_in_seconds) override {
// This is a deprecated function and should not be called anymore.
UNREACHABLE();
}
void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
// This is a deprecated function and should not be called anymore.
UNREACHABLE();
}
bool IdleTasksEnabled(Isolate* isolate) override {
return platform_->IdleTasksEnabled(isolate);
}
double MonotonicallyIncreasingTime() override {
return platform_->MonotonicallyIncreasingTime();
}
double CurrentClockTimeMillis() override {
return platform_->CurrentClockTimeMillis();
}
v8::TracingController* GetTracingController() override {
return platform_->GetTracingController();
}
private:
class DelayedTaskRunnerDeleter;
class DelayedTaskRunner final : public TaskRunner {
public:
DelayedTaskRunner(std::shared_ptr<TaskRunner> task_runner,
DelayedTasksPlatform* platform)
: task_runner_(task_runner), platform_(platform) {}
void PostTask(std::unique_ptr<Task> task) final {
task_runner_->PostTask(platform_->MakeDelayedTask(std::move(task)));
}
void PostDelayedTask(std::unique_ptr<Task> task,
double delay_in_seconds) final {
task_runner_->PostDelayedTask(platform_->MakeDelayedTask(std::move(task)),
delay_in_seconds);
}
void PostIdleTask(std::unique_ptr<IdleTask> task) final {
task_runner_->PostIdleTask(
platform_->MakeDelayedIdleTask(std::move(task)));
}
bool IdleTasksEnabled() final { return task_runner_->IdleTasksEnabled(); }
private:
friend class DelayedTaskRunnerDeleter;
std::shared_ptr<TaskRunner> task_runner_;
DelayedTasksPlatform* platform_;
};
class DelayedTaskRunnerDeleter {
public:
void operator()(DelayedTaskRunner* runner) const {
TaskRunner* original_runner = runner->task_runner_.get();
auto& delayed_task_runners = runner->platform_->delayed_task_runners_;
DCHECK_EQ(1, delayed_task_runners.count(original_runner));
delayed_task_runners.erase(original_runner);
}
};
class DelayedTask : public Task {
public:
DelayedTask(std::unique_ptr<Task> task, int32_t delay_ms)
: task_(std::move(task)), delay_ms_(delay_ms) {}
void Run() final {
base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_));
task_->Run();
}
private:
std::unique_ptr<Task> task_;
int32_t delay_ms_;
};
class DelayedIdleTask : 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 {
base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_));
task_->Run(deadline_in_seconds);
}
private:
std::unique_ptr<IdleTask> task_;
int32_t delay_ms_;
};
std::unique_ptr<Platform> platform_;
// The Mutex protects the RNG, which is used by foreground and background
// threads, and the {delayed_task_runners_} map might be accessed concurrently
// by the shared_ptr destructor.
base::Mutex mutex_;
base::RandomNumberGenerator rng_;
std::unordered_map<TaskRunner*, std::weak_ptr<DelayedTaskRunner>>
delayed_task_runners_;
int32_t GetRandomDelayInMilliseconds() {
base::LockGuard<base::Mutex> lock_guard(&mutex_);
double delay_fraction = rng_.NextDouble();
// Sleep up to 100ms (100000us). Square {delay_fraction} to shift
// distribution towards shorter sleeps.
return 1e5 * (delay_fraction * delay_fraction);
}
std::unique_ptr<Task> MakeDelayedTask(std::unique_ptr<Task> task) {
return base::make_unique<DelayedTask>(std::move(task),
GetRandomDelayInMilliseconds());
}
std::unique_ptr<IdleTask> MakeDelayedIdleTask(
std::unique_ptr<IdleTask> task) {
return base::make_unique<DelayedIdleTask>(std::move(task),
GetRandomDelayInMilliseconds());
}
DISALLOW_COPY_AND_ASSIGN(DelayedTasksPlatform);
};
std::unique_ptr<Platform> MakeDelayedTasksPlatform(
std::unique_ptr<Platform> platform, int64_t random_seed) {
if (random_seed) {
return base::make_unique<DelayedTasksPlatform>(std::move(platform),
random_seed);
}
return base::make_unique<DelayedTasksPlatform>(std::move(platform));
}
} // namespace v8

29
src/d8-platforms.h Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2018 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_D8_PLATFORMS_H_
#define V8_D8_PLATFORMS_H_
#include <cstdint>
#include <memory>
namespace v8 {
class Platform;
// Returns a predictable v8::Platform implementation.
// orker threads are disabled, idle tasks are disallowed, and the time reported
// by {MonotonicallyIncreasingTime} is deterministic.
std::unique_ptr<Platform> MakePredictablePlatform(
std::unique_ptr<Platform> platform);
// Returns a v8::Platform implementation which randomly delays tasks (both
// foreground and background) for stress-testing different interleavings.
// If {random_seed} is 0, a random seed is chosen.
std::unique_ptr<Platform> MakeDelayedTasksPlatform(
std::unique_ptr<Platform> platform, int64_t random_seed);
} // namespace v8
#endif // V8_D8_PLATFORMS_H_

113
src/d8.cc
View File

@ -1,4 +1,4 @@
/// Copyright 2012 the V8 project authors. All rights reserved.
// Copyright 2012 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.
@ -17,10 +17,6 @@
#include "src/third_party/vtune/v8-vtune.h"
#endif
#include "src/d8-console.h"
#include "src/d8.h"
#include "src/ostreams.h"
#include "include/libplatform/libplatform.h"
#include "include/libplatform/v8-tracing.h"
#include "include/v8-inspector.h"
@ -31,11 +27,15 @@
#include "src/base/platform/time.h"
#include "src/base/sys-info.h"
#include "src/basic-block-profiler.h"
#include "src/d8-console.h"
#include "src/d8-platforms.h"
#include "src/d8.h"
#include "src/debug/debug-interface.h"
#include "src/interpreter/interpreter.h"
#include "src/msan.h"
#include "src/objects-inl.h"
#include "src/objects.h"
#include "src/ostreams.h"
#include "src/snapshot/natives.h"
#include "src/trap-handler/trap-handler.h"
#include "src/utils.h"
@ -190,92 +190,9 @@ class MockArrayBufferAllocatiorWithLimit : public MockArrayBufferAllocator {
std::atomic<size_t> space_left_;
};
// Predictable v8::Platform implementation. Worker threads are disabled, idle
// tasks are disallowed, and the time reported by {MonotonicallyIncreasingTime}
// is deterministic.
class PredictablePlatform : public Platform {
public:
explicit PredictablePlatform(std::unique_ptr<Platform> platform)
: platform_(std::move(platform)) {
DCHECK_NOT_NULL(platform_);
}
PageAllocator* GetPageAllocator() override {
return platform_->GetPageAllocator();
}
void OnCriticalMemoryPressure() override {
platform_->OnCriticalMemoryPressure();
}
bool OnCriticalMemoryPressure(size_t length) override {
return platform_->OnCriticalMemoryPressure(length);
}
std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
v8::Isolate* isolate) override {
return platform_->GetForegroundTaskRunner(isolate);
}
int NumberOfWorkerThreads() override { return 0; }
void CallOnWorkerThread(std::unique_ptr<Task> task) override {
// It's not defined when background tasks are being executed, so we can just
// execute them right away.
task->Run();
}
void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
double delay_in_seconds) override {
// Never run delayed tasks.
}
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
// This is a deprecated function and should not be called anymore.
UNREACHABLE();
}
void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
double delay_in_seconds) override {
// This is a deprecated function and should not be called anymore.
UNREACHABLE();
}
void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
UNREACHABLE();
}
bool IdleTasksEnabled(Isolate* isolate) override { return false; }
double MonotonicallyIncreasingTime() override {
return synthetic_time_in_sec_ += 0.00001;
}
double CurrentClockTimeMillis() override {
return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond;
}
v8::TracingController* GetTracingController() override {
return platform_->GetTracingController();
}
Platform* platform() const { return platform_.get(); }
private:
double synthetic_time_in_sec_ = 0.0;
std::unique_ptr<Platform> platform_;
DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
};
v8::Platform* g_default_platform;
std::unique_ptr<v8::Platform> g_platform;
v8::Platform* GetDefaultPlatform() {
return i::FLAG_verify_predictable
? static_cast<PredictablePlatform*>(g_platform.get())->platform()
: g_platform.get();
}
static Local<Value> Throw(Isolate* isolate, const char* message) {
return isolate->ThrowException(
String::NewFromUtf8(isolate, message, NewStringType::kNormal)
@ -3077,17 +2994,17 @@ namespace {
bool ProcessMessages(
Isolate* isolate,
const std::function<platform::MessageLoopBehavior()>& behavior) {
Platform* platform = GetDefaultPlatform();
while (true) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::SaveContext saved_context(i_isolate);
i_isolate->set_context(nullptr);
SealHandleScope shs(isolate);
while (v8::platform::PumpMessageLoop(platform, isolate, behavior())) {
while (v8::platform::PumpMessageLoop(g_default_platform, isolate,
behavior())) {
isolate->RunMicrotasks();
}
if (platform->IdleTasksEnabled(isolate)) {
v8::platform::RunIdleTasks(platform, isolate,
if (g_default_platform->IdleTasksEnabled(isolate)) {
v8::platform::RunIdleTasks(g_default_platform, isolate,
50.0 / base::Time::kMillisecondsPerSecond);
}
HandleScope handle_scope(isolate);
@ -3426,8 +3343,16 @@ int Shell::Main(int argc, char* argv[]) {
g_platform = v8::platform::NewDefaultPlatform(
options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
in_process_stack_dumping, std::move(tracing));
g_default_platform = g_platform.get();
if (i::FLAG_verify_predictable) {
g_platform.reset(new PredictablePlatform(std::move(g_platform)));
g_platform = MakePredictablePlatform(std::move(g_platform));
}
if (i::FLAG_stress_delay_tasks) {
int64_t random_seed = i::FLAG_fuzzer_random_seed;
if (!random_seed) random_seed = i::FLAG_random_seed;
// If random_seed is still 0 here, the {DelayedTasksPlatform} will choose a
// random seed.
g_platform = MakeDelayedTasksPlatform(std::move(g_platform), random_seed);
}
if (i::FLAG_trace_turbo_cfg_file == nullptr) {

View File

@ -335,6 +335,11 @@ DEFINE_VALUE_IMPLICATION(optimize_for_size, max_semi_space_size, 1)
DEFINE_BOOL(unbox_double_arrays, true, "automatically unbox arrays of doubles")
DEFINE_BOOL_READONLY(string_slices, true, "use string slices")
// Flag to stress different interleavings of tasks.
DEFINE_BOOL(
stress_delay_tasks, false,
"delay execution of tasks by 0-100ms randomly (based on --random-seed)")
// Flags for Ignition for no-snapshot builds.
#undef FLAG
#ifndef V8_USE_SNAPSHOT