Commit f507bc1c authored by yangguo's avatar yangguo Committed by Commit bot

[serializer] Add API to warm up startup snapshot with an additional script.

A startup snapshot is considered cold when it does not contain any
function code. We can now create a warm startup snapshot from a cold one
by running a warm-up script. Functions exercised by the warm-up script
are compiled and its code included in the warm startup snapshot. Side
effects caused by the warm-up script does not persist.

R=vogelheim@chromium.org
BUG=v8:4836
LOG=Y

Review URL: https://codereview.chromium.org/1805903002

Cr-Commit-Position: refs/heads/master@{#34849}
parent c2e82d6e
......@@ -162,6 +162,9 @@ endif
ifdef embedscript
GYPFLAGS += -Dembed_script=$(embedscript)
endif
ifdef warmupscript
GYPFLAGS += -Dwarmup_script=$(warmupscript)
endif
ifeq ($(goma), on)
GYPFLAGS += -Duse_goma=1
endif
......
......@@ -6276,11 +6276,23 @@ class V8_EXPORT V8 {
static void SetSnapshotDataBlob(StartupData* startup_blob);
/**
* Create a new isolate and context for the purpose of capturing a snapshot
* Bootstrap an isolate and a context from scratch to create a startup
* snapshot. Include the side-effects of running the optional script.
* Returns { NULL, 0 } on failure.
* The caller owns the data array in the return value.
* The caller acquires ownership of the data array in the return value.
*/
static StartupData CreateSnapshotDataBlob(const char* custom_source = NULL);
static StartupData CreateSnapshotDataBlob(const char* embedded_source = NULL);
/**
* Bootstrap an isolate and a context from the cold startup blob, run the
* warm-up script to trigger code compilation. The side effects are then
* discarded. The resulting startup snapshot will include compiled code.
* Returns { NULL, 0 } on failure.
* The caller acquires ownership of the data array in the return value.
* The argument startup blob is untouched.
*/
static StartupData WarmUpSnapshotDataBlob(StartupData cold_startup_blob,
const char* warmup_source);
/**
* Adds a message listener.
......
......@@ -341,12 +341,23 @@ void V8::SetSnapshotDataBlob(StartupData* snapshot_blob) {
i::V8::SetSnapshotBlob(snapshot_blob);
}
namespace {
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) {
void* data = AllocateUninitialized(length);
return data == NULL ? data : memset(data, 0, length);
}
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t) { free(data); }
};
bool RunExtraCode(Isolate* isolate, Local<Context> context,
const char* utf8_source) {
// Run custom script if provided.
const char* utf8_source, const char* name) {
base::ElapsedTimer timer;
timer.Start();
Context::Scope context_scope(context);
TryCatch try_catch(isolate);
Local<String> source_string;
if (!String::NewFromUtf8(isolate, utf8_source, NewStringType::kNormal)
......@@ -354,7 +365,7 @@ bool RunExtraCode(Isolate* isolate, Local<Context> context,
return false;
}
Local<String> resource_name =
String::NewFromUtf8(isolate, "<embedded script>", NewStringType::kNormal)
String::NewFromUtf8(isolate, name, NewStringType::kNormal)
.ToLocalChecked();
ScriptOrigin origin(resource_name);
ScriptCompiler::Source source(source_string, origin);
......@@ -362,7 +373,7 @@ bool RunExtraCode(Isolate* isolate, Local<Context> context,
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script)) return false;
if (script->Run(context).IsEmpty()) return false;
if (i::FLAG_profile_deserialization) {
i::PrintF("Executing custom snapshot script took %0.3f ms\n",
i::PrintF("Executing custom snapshot script %s took %0.3f ms\n", name,
timer.Elapsed().InMillisecondsF());
}
timer.Stop();
......@@ -370,92 +381,148 @@ bool RunExtraCode(Isolate* isolate, Local<Context> context,
return true;
}
StartupData SerializeIsolateAndContext(
Isolate* isolate, Persistent<Context>* context,
i::Snapshot::Metadata metadata,
i::StartupSerializer::FunctionCodeHandling function_code_handling) {
if (context->IsEmpty()) return {NULL, 0};
namespace {
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) {
void* data = AllocateUninitialized(length);
return data == NULL ? data : memset(data, 0, length);
// If we don't do this then we end up with a stray root pointing at the
// context even after we have disposed of the context.
internal_isolate->heap()->CollectAllAvailableGarbage("mksnapshot");
// GC may have cleared weak cells, so compact any WeakFixedArrays
// found on the heap.
i::HeapIterator iterator(internal_isolate->heap(),
i::HeapIterator::kFilterUnreachable);
for (i::HeapObject* o = iterator.next(); o != NULL; o = iterator.next()) {
if (o->IsPrototypeInfo()) {
i::Object* prototype_users = i::PrototypeInfo::cast(o)->prototype_users();
if (prototype_users->IsWeakFixedArray()) {
i::WeakFixedArray* array = i::WeakFixedArray::cast(prototype_users);
array->Compact<i::JSObject::PrototypeRegistryCompactionCallback>();
}
} else if (o->IsScript()) {
i::Object* shared_list = i::Script::cast(o)->shared_function_infos();
if (shared_list->IsWeakFixedArray()) {
i::WeakFixedArray* array = i::WeakFixedArray::cast(shared_list);
array->Compact<i::WeakFixedArray::NullCallback>();
}
}
}
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t) { free(data); }
};
i::Object* raw_context = *v8::Utils::OpenPersistent(*context);
context->Reset();
i::SnapshotByteSink snapshot_sink;
i::StartupSerializer ser(internal_isolate, &snapshot_sink,
function_code_handling);
ser.SerializeStrongReferences();
i::SnapshotByteSink context_sink;
i::PartialSerializer context_ser(internal_isolate, &ser, &context_sink);
context_ser.Serialize(&raw_context);
ser.SerializeWeakReferencesAndDeferred();
return i::Snapshot::CreateSnapshotBlob(ser, context_ser, metadata);
}
} // namespace
StartupData V8::CreateSnapshotDataBlob(const char* embedded_source) {
// Create a new isolate and a new context from scratch, optionally run
// a script to embed, and serialize to create a snapshot blob.
StartupData result = {NULL, 0};
base::ElapsedTimer timer;
timer.Start();
StartupData V8::CreateSnapshotDataBlob(const char* custom_source) {
i::Isolate* internal_isolate = new i::Isolate(true);
ArrayBufferAllocator allocator;
i::Isolate* internal_isolate = new i::Isolate(true);
internal_isolate->set_array_buffer_allocator(&allocator);
Isolate* isolate = reinterpret_cast<Isolate*>(internal_isolate);
StartupData result = {NULL, 0};
{
base::ElapsedTimer timer;
timer.Start();
Isolate::Scope isolate_scope(isolate);
internal_isolate->Init(NULL);
Persistent<Context> context;
i::Snapshot::Metadata metadata;
{
HandleScope handle_scope(isolate);
Local<Context> new_context = Context::New(isolate);
context.Reset(isolate, new_context);
if (custom_source != NULL) {
metadata.set_embeds_script(true);
Context::Scope context_scope(new_context);
if (!RunExtraCode(isolate, new_context, custom_source)) context.Reset();
if (embedded_source != NULL &&
!RunExtraCode(isolate, new_context, embedded_source, "<embedded>")) {
context.Reset();
}
}
if (!context.IsEmpty()) {
// If we don't do this then we end up with a stray root pointing at the
// context even after we have disposed of the context.
internal_isolate->heap()->CollectAllAvailableGarbage("mksnapshot");
// GC may have cleared weak cells, so compact any WeakFixedArrays
// found on the heap.
i::HeapIterator iterator(internal_isolate->heap(),
i::HeapIterator::kFilterUnreachable);
for (i::HeapObject* o = iterator.next(); o != NULL; o = iterator.next()) {
if (o->IsPrototypeInfo()) {
i::Object* prototype_users =
i::PrototypeInfo::cast(o)->prototype_users();
if (prototype_users->IsWeakFixedArray()) {
i::WeakFixedArray* array = i::WeakFixedArray::cast(prototype_users);
array->Compact<i::JSObject::PrototypeRegistryCompactionCallback>();
}
} else if (o->IsScript()) {
i::Object* shared_list = i::Script::cast(o)->shared_function_infos();
if (shared_list->IsWeakFixedArray()) {
i::WeakFixedArray* array = i::WeakFixedArray::cast(shared_list);
array->Compact<i::WeakFixedArray::NullCallback>();
}
}
}
i::Object* raw_context = *v8::Utils::OpenPersistent(context);
context.Reset();
i::Snapshot::Metadata metadata;
metadata.set_embeds_script(embedded_source != NULL);
i::SnapshotByteSink snapshot_sink;
i::StartupSerializer ser(internal_isolate, &snapshot_sink);
ser.SerializeStrongReferences();
result = SerializeIsolateAndContext(
isolate, &context, metadata, i::StartupSerializer::CLEAR_FUNCTION_CODE);
DCHECK(context.IsEmpty());
}
isolate->Dispose();
i::SnapshotByteSink context_sink;
i::PartialSerializer context_ser(internal_isolate, &ser, &context_sink);
context_ser.Serialize(&raw_context);
ser.SerializeWeakReferencesAndDeferred();
if (i::FLAG_profile_deserialization) {
i::PrintF("Creating snapshot took %0.3f ms\n",
timer.Elapsed().InMillisecondsF());
}
timer.Stop();
return result;
}
result = i::Snapshot::CreateSnapshotBlob(ser, context_ser, metadata);
}
if (i::FLAG_profile_deserialization) {
i::PrintF("Creating snapshot took %0.3f ms\n",
timer.Elapsed().InMillisecondsF());
StartupData V8::WarmUpSnapshotDataBlob(StartupData cold_snapshot_blob,
const char* warmup_source) {
CHECK(cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != NULL);
CHECK(warmup_source != NULL);
// Use following steps to create a warmed up snapshot blob from a cold one:
// - Create a new isolate from the cold snapshot.
// - Create a new context to run the warmup script. This will trigger
// compilation of executed functions.
// - Create a new context. This context will be unpolluted.
// - Serialize the isolate and the second context into a new snapshot blob.
StartupData result = {NULL, 0};
base::ElapsedTimer timer;
timer.Start();
ArrayBufferAllocator allocator;
i::Isolate* internal_isolate = new i::Isolate(true);
internal_isolate->set_array_buffer_allocator(&allocator);
internal_isolate->set_snapshot_blob(&cold_snapshot_blob);
Isolate* isolate = reinterpret_cast<Isolate*>(internal_isolate);
{
Isolate::Scope isolate_scope(isolate);
i::Snapshot::Initialize(internal_isolate);
Persistent<Context> context;
{
HandleScope handle_scope(isolate);
Local<Context> warmup_context = Context::New(isolate);
if (RunExtraCode(isolate, warmup_context, warmup_source, "<warm-up>")) {
Local<Context> fresh_context = Context::New(isolate);
context.Reset(isolate, fresh_context);
}
}
timer.Stop();
i::Snapshot::Metadata metadata;
metadata.set_embeds_script(i::Snapshot::EmbedsScript(internal_isolate));
result = SerializeIsolateAndContext(
isolate, &context, metadata, i::StartupSerializer::KEEP_FUNCTION_CODE);
DCHECK(context.IsEmpty());
}
isolate->Dispose();
if (i::FLAG_profile_deserialization) {
i::PrintF("Warming up snapshot took %0.3f ms\n",
timer.Elapsed().InMillisecondsF());
}
timer.Stop();
return result;
}
......
......@@ -475,7 +475,8 @@ enum VisitMode {
VISIT_ALL,
VISIT_ALL_IN_SCAVENGE,
VISIT_ALL_IN_SWEEP_NEWSPACE,
VISIT_ONLY_STRONG
VISIT_ONLY_STRONG,
VISIT_ONLY_STRONG_FOR_SERIALIZATION
};
// Flag indicating whether code is built into the VM (one of the natives files).
......
......@@ -4766,6 +4766,7 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
// Iterate over global handles.
switch (mode) {
case VISIT_ONLY_STRONG:
case VISIT_ONLY_STRONG_FOR_SERIALIZATION:
isolate_->global_handles()->IterateStrongRoots(v);
break;
case VISIT_ALL_IN_SCAVENGE:
......@@ -4796,15 +4797,10 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
}
v->Synchronize(VisitorSynchronization::kStrongRoots);
// Iterate over the pointers the Serialization/Deserialization code is
// holding.
// During garbage collection this keeps the partial snapshot cache alive.
// During deserialization of the startup snapshot this creates the partial
// snapshot cache and deserializes the objects it refers to. During
// serialization this does nothing, since the partial snapshot cache is
// empty. However the next thing we do is create the partial snapshot,
// filling up the partial snapshot cache with objects it needs as we go.
SerializerDeserializer::Iterate(isolate_, v);
// Iterate over the partial snapshot cache unless serializing.
if (mode != VISIT_ONLY_STRONG_FOR_SERIALIZATION) {
SerializerDeserializer::Iterate(isolate_, v);
}
// We don't do a v->Synchronize call here, because in debug mode that will
// output a flag to the snapshot. However at this point the serializer and
// deserializer are deliberately a little unsynchronized (see above) so the
......
......@@ -2228,7 +2228,7 @@ bool Isolate::Init(Deserializer* des) {
}
if (create_heap_objects) {
// Terminate the cache array with the sentinel so we can iterate.
// Terminate the partial snapshot cache so we can iterate.
partial_snapshot_cache_.Add(heap_.undefined_value());
}
......
......@@ -1358,6 +1358,8 @@ class Isolate {
friend class v8::Locker;
friend class v8::Unlocker;
friend v8::StartupData v8::V8::CreateSnapshotDataBlob(const char*);
friend v8::StartupData v8::V8::WarmUpSnapshotDataBlob(v8::StartupData,
const char*);
DISALLOW_COPY_AND_ASSIGN(Isolate);
};
......
......@@ -77,6 +77,8 @@ void Deserializer::Deserialize(Isolate* isolate) {
DCHECK_NULL(isolate_->thread_manager()->FirstThreadStateInUse());
// No active handles.
DCHECK(isolate_->handle_scope_implementer()->blocks()->is_empty());
// Partial snapshot cache is not yet populated.
DCHECK(isolate_->partial_snapshot_cache()->is_empty());
{
DisallowHeapAllocation no_gc;
......
......@@ -109,10 +109,10 @@ class SnapshotWriter {
FILE* startup_blob_file_;
};
char* GetExtraCode(char* filename) {
char* GetExtraCode(char* filename, const char* description) {
if (filename == NULL || strlen(filename) == 0) return NULL;
::printf("Embedding extra script: %s\n", filename);
if (strcmp(filename, "-") == 0) return NULL;
::printf("Loading script for %s: %s\n", description, filename);
FILE* file = base::OS::FOpen(filename, "rb");
if (file == NULL) {
fprintf(stderr, "Failed to open '%s': errno %d\n", filename, errno);
......@@ -144,7 +144,7 @@ int main(int argc, char** argv) {
// Print the usage if an error occurs when parsing the command line
// flags or if the help flag is set.
int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
if (result > 0 || (argc != 1 && argc != 2) || i::FLAG_help) {
if (result > 0 || (argc > 3) || i::FLAG_help) {
::printf("Usage: %s --startup_src=... --startup_blob=... [extras]\n",
argv[0]);
i::FlagList::PrintHelp();
......@@ -161,11 +161,21 @@ int main(int argc, char** argv) {
SnapshotWriter writer;
if (i::FLAG_startup_src) writer.SetSnapshotFile(i::FLAG_startup_src);
if (i::FLAG_startup_blob) writer.SetStartupBlobFile(i::FLAG_startup_blob);
char* extra_code = GetExtraCode(argc == 2 ? argv[1] : NULL);
StartupData blob = v8::V8::CreateSnapshotDataBlob(extra_code);
char* embed_script = GetExtraCode(argc >= 2 ? argv[1] : NULL, "embedding");
StartupData blob = v8::V8::CreateSnapshotDataBlob(embed_script);
delete[] embed_script;
char* warmup_script = GetExtraCode(argc >= 3 ? argv[2] : NULL, "warm up");
if (warmup_script) {
StartupData cold = blob;
blob = v8::V8::WarmUpSnapshotDataBlob(cold, warmup_script);
delete[] cold.data;
delete[] warmup_script;
}
CHECK(blob.data);
writer.WriteSnapshot(blob);
delete[] extra_code;
delete[] blob.data;
}
......
......@@ -14,7 +14,8 @@ PartialSerializer::PartialSerializer(Isolate* isolate,
SnapshotByteSink* sink)
: Serializer(isolate, sink),
startup_serializer_(startup_snapshot_serializer),
global_object_(NULL) {
global_object_(NULL),
next_partial_cache_index_(0) {
InitializeCodeAddressMap();
}
......@@ -94,20 +95,14 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
}
int PartialSerializer::PartialSnapshotCacheIndex(HeapObject* heap_object) {
Isolate* isolate = this->isolate();
List<Object*>* cache = isolate->partial_snapshot_cache();
int new_index = cache->length();
int index = partial_cache_index_map_.LookupOrInsert(heap_object, new_index);
int index = partial_cache_index_map_.LookupOrInsert(
heap_object, next_partial_cache_index_);
if (index == PartialCacheIndexMap::kInvalidIndex) {
// We didn't find the object in the cache. So we add it to the cache and
// then visit the pointer so that it becomes part of the startup snapshot
// and we can refer to it from the partial snapshot.
cache->Add(heap_object);
// This object is not part of the partial snapshot cache yet. Add it to the
// startup snapshot so we can refer to it via partial snapshot index from
// the partial snapshot.
startup_serializer_->VisitPointer(reinterpret_cast<Object**>(&heap_object));
// We don't recurse from the startup snapshot generator into the partial
// snapshot generator.
return new_index;
return next_partial_cache_index_++;
}
return index;
}
......
......@@ -52,6 +52,7 @@ class PartialSerializer : public Serializer {
Serializer* startup_serializer_;
Object* global_object_;
PartialCacheIndexMap partial_cache_index_map_;
int next_partial_cache_index_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
};
......
......@@ -390,23 +390,19 @@ void SerializedData::AllocateData(int size) {
DCHECK(IsAligned(reinterpret_cast<intptr_t>(data_), kPointerAlignment));
}
// This ensures that the partial snapshot cache keeps things alive during GC and
// tracks their movement. When it is called during serialization of the startup
// snapshot nothing happens. When the partial (context) snapshot is created,
// this array is populated with the pointers that the partial snapshot will
// need. As that happens we emit serialized objects to the startup snapshot
// that correspond to the elements of this cache array. On deserialization we
// therefore need to visit the cache array. This fills it up with pointers to
// deserialized objects.
// The partial snapshot cache is terminated by undefined. We visit the
// partial snapshot...
// - during deserialization to populate it.
// - during normal GC to keep its content alive.
// - not during serialization. The partial serializer adds to it explicitly.
void SerializerDeserializer::Iterate(Isolate* isolate, ObjectVisitor* visitor) {
if (isolate->serializer_enabled()) return;
List<Object*>* cache = isolate->partial_snapshot_cache();
for (int i = 0;; ++i) {
// Extend the array ready to get a value when deserializing.
if (cache->length() <= i) cache->Add(Smi::FromInt(0));
// During deserialization, the visitor populates the partial snapshot cache
// and eventually terminates the cache with undefined.
visitor->VisitPointer(&cache->at(i));
// Sentinel is the undefined object, which is a root so it will not normally
// be found in the cache.
if (cache->at(i)->IsUndefined()) break;
}
}
......
......@@ -10,15 +10,13 @@
namespace v8 {
namespace internal {
StartupSerializer::StartupSerializer(Isolate* isolate, SnapshotByteSink* sink)
StartupSerializer::StartupSerializer(
Isolate* isolate, SnapshotByteSink* sink,
FunctionCodeHandling function_code_handling)
: Serializer(isolate, sink),
root_index_wave_front_(0),
serializing_builtins_(false) {
// Clear the cache of objects used by the partial snapshot. After the
// strong roots have been serialized we can create a partial snapshot
// which will repopulate the cache with objects needed by that partial
// snapshot.
isolate->partial_snapshot_cache()->Clear();
serializing_builtins_(false),
function_code_handling_(function_code_handling) {
InitializeCodeAddressMap();
}
......@@ -35,8 +33,9 @@ void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
// If the function code is compiled (either as native code or bytecode),
// replace it with lazy-compile builtin. Only exception is when we are
// serializing the canonical interpreter-entry-trampoline builtin.
if (code->kind() == Code::FUNCTION ||
(!serializing_builtins_ && code->is_interpreter_entry_trampoline())) {
if (function_code_handling_ == CLEAR_FUNCTION_CODE &&
(code->kind() == Code::FUNCTION ||
(!serializing_builtins_ && code->is_interpreter_entry_trampoline()))) {
obj = isolate()->builtins()->builtin(Builtins::kCompileLazy);
}
} else if (obj->IsBytecodeArray()) {
......@@ -75,11 +74,9 @@ void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
}
void StartupSerializer::SerializeWeakReferencesAndDeferred() {
// This phase comes right after the serialization (of the snapshot).
// After we have done the partial serialization the partial snapshot cache
// will contain some references needed to decode the partial snapshot. We
// add one entry with 'undefined' which is the sentinel that the deserializer
// uses to know it is done deserializing the array.
// This comes right after serialization of the partial snapshot, where we
// add entries to the partial snapshot cache of the startup snapshot. Add
// one entry with 'undefined' to terminate the partial snapshot cache.
Object* undefined = isolate()->heap()->undefined_value();
VisitPointer(&undefined);
isolate()->heap()->IterateWeakRoots(this, VISIT_ALL);
......@@ -105,7 +102,8 @@ void StartupSerializer::SerializeStrongReferences() {
// We don't support serializing installed extensions.
CHECK(!isolate->has_installed_extensions());
isolate->heap()->IterateSmiRoots(this);
isolate->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG);
isolate->heap()->IterateStrongRoots(this,
VISIT_ONLY_STRONG_FOR_SERIALIZATION);
}
void StartupSerializer::VisitPointers(Object** start, Object** end) {
......
......@@ -12,7 +12,11 @@ namespace internal {
class StartupSerializer : public Serializer {
public:
StartupSerializer(Isolate* isolate, SnapshotByteSink* sink);
enum FunctionCodeHandling { CLEAR_FUNCTION_CODE, KEEP_FUNCTION_CODE };
StartupSerializer(
Isolate* isolate, SnapshotByteSink* sink,
FunctionCodeHandling function_code_handling = CLEAR_FUNCTION_CODE);
~StartupSerializer() override;
// Serialize the current state of the heap. The order is:
......@@ -32,6 +36,7 @@ class StartupSerializer : public Serializer {
intptr_t root_index_wave_front_;
bool serializing_builtins_;
FunctionCodeHandling function_code_handling_;
DISALLOW_COPY_AND_ASSIGN(StartupSerializer);
};
......
......@@ -783,6 +783,76 @@ TEST(CustomSnapshotDataBlobStackOverflow) {
isolate->Dispose();
}
bool IsCompiled(const char* name) {
return i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*CompileRun(name)))
->shared()
->is_compiled();
}
TEST(SnapshotDataBlobWithWarmup) {
DisableTurbofan();
const char* warmup = "Math.tan(1); Math.sin = 1;";
v8::StartupData cold = v8::V8::CreateSnapshotDataBlob();
v8::StartupData warm = v8::V8::WarmUpSnapshotDataBlob(cold, warmup);
delete[] cold.data;
v8::Isolate::CreateParams params;
params.snapshot_blob = &warm;
params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(params);
{
v8::Isolate::Scope i_scope(isolate);
v8::HandleScope h_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
delete[] warm.data;
v8::Context::Scope c_scope(context);
// Running the warmup script has effect on whether functions are
// pre-compiled, but does not pollute the context.
CHECK(IsCompiled("Math.tan"));
CHECK(!IsCompiled("Math.cos"));
CHECK(CompileRun("Math.sin")->IsFunction());
}
isolate->Dispose();
}
TEST(CustomSnapshotDataBlobWithWarmup) {
DisableTurbofan();
const char* source =
"function f() { return Math.sin(1); }\n"
"function g() { return Math.cos(1); }\n"
"Math.tan(1);"
"var a = 5";
const char* warmup = "a = f()";
v8::StartupData cold = v8::V8::CreateSnapshotDataBlob(source);
v8::StartupData warm = v8::V8::WarmUpSnapshotDataBlob(cold, warmup);
delete[] cold.data;
v8::Isolate::CreateParams params;
params.snapshot_blob = &warm;
params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(params);
{
v8::Isolate::Scope i_scope(isolate);
v8::HandleScope h_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
delete[] warm.data;
v8::Context::Scope c_scope(context);
// Running the warmup script has effect on whether functions are
// pre-compiled, but does not pollute the context.
CHECK(IsCompiled("f"));
CHECK(IsCompiled("Math.sin"));
CHECK(!IsCompiled("g"));
CHECK(!IsCompiled("Math.cos"));
CHECK(!IsCompiled("Math.tan"));
CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust());
}
isolate->Dispose();
}
TEST(TestThatAlwaysSucceeds) {
}
......
......@@ -31,7 +31,8 @@
'v8_code': 1,
'v8_random_seed%': 314159265,
'v8_vector_stores%': 0,
'embed_script%': "",
'embed_script%': "-",
'warmup_script%': "-",
'v8_extra_library_files%': [],
'v8_experimental_extra_library_files%': [],
'mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mksnapshot<(EXECUTABLE_SUFFIX)',
......@@ -204,6 +205,7 @@
'inputs': [
'<(mksnapshot_exec)',
'<(embed_script)',
'<(warmup_script)',
],
'outputs': [
'<(INTERMEDIATE_DIR)/snapshot.cc',
......@@ -227,6 +229,7 @@
'<@(mksnapshot_flags)',
'--startup_src', '<@(INTERMEDIATE_DIR)/snapshot.cc',
'<(embed_script)',
'<(warmup_script)',
],
},
],
......@@ -330,6 +333,7 @@
'<@(mksnapshot_flags_ignition)',
'--startup_blob', '<(PRODUCT_DIR)/snapshot_blob_ignition_host.bin',
'<(embed_script)',
'<(warmup_script)',
],
}, {
'outputs': ['<(PRODUCT_DIR)/snapshot_blob_ignition.bin'],
......@@ -338,6 +342,7 @@
'<@(mksnapshot_flags_ignition)',
'--startup_blob', '<(PRODUCT_DIR)/snapshot_blob_ignition.bin',
'<(embed_script)',
'<(warmup_script)',
],
}],
],
......@@ -348,6 +353,7 @@
'<@(mksnapshot_flags_ignition)',
'--startup_blob', '<(PRODUCT_DIR)/snapshot_blob_ignition.bin',
'<(embed_script)',
'<(warmup_script)',
],
}],
],
......@@ -393,6 +399,7 @@
'<@(mksnapshot_flags)',
'--startup_blob', '<(PRODUCT_DIR)/snapshot_blob_host.bin',
'<(embed_script)',
'<(warmup_script)',
],
}, {
'outputs': ['<(PRODUCT_DIR)/snapshot_blob.bin'],
......@@ -401,6 +408,7 @@
'<@(mksnapshot_flags)',
'--startup_blob', '<(PRODUCT_DIR)/snapshot_blob.bin',
'<(embed_script)',
'<(warmup_script)',
],
}],
],
......@@ -411,6 +419,7 @@
'<@(mksnapshot_flags)',
'--startup_blob', '<(PRODUCT_DIR)/snapshot_blob.bin',
'<(embed_script)',
'<(warmup_script)',
],
}],
],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment