Commit 533453f9 authored by yangguo's avatar yangguo Committed by Commit bot

[snapshot] support multiple contexts in the same snapshot.

R=jochen@chromium.org, vogelheim@chromium.org
BUG=chromium:617892

Review-Url: https://codereview.chromium.org/2055203002
Cr-Commit-Position: refs/heads/master@{#37008}
parent 85bef237
......@@ -6787,8 +6787,9 @@ class SnapshotCreator {
/**
* Add a context to be included in the snapshot blob.
* \returns the index of the context in the snapshot blob.
*/
void AddContext(Local<Context> context);
size_t AddContext(Local<Context> context);
/**
* Created a snapshot data blob.
......@@ -7092,7 +7093,8 @@ class V8_EXPORT Context {
static Local<Context> New(
Isolate* isolate, ExtensionConfiguration* extensions = NULL,
Local<ObjectTemplate> global_template = Local<ObjectTemplate>(),
Local<Value> global_object = Local<Value>());
Local<Value> global_object = Local<Value>(),
size_t context_snapshot_index = 0);
/**
* Sets the security token for the context. To access an object in
......
......@@ -429,13 +429,15 @@ Isolate* SnapshotCreator::GetIsolate() {
return SnapshotCreatorData::cast(data_)->isolate_;
}
void SnapshotCreator::AddContext(Local<Context> context) {
size_t SnapshotCreator::AddContext(Local<Context> context) {
DCHECK(!context.IsEmpty());
SnapshotCreatorData* data = SnapshotCreatorData::cast(data_);
DCHECK(!data->created_);
Isolate* isolate = data->isolate_;
CHECK_EQ(isolate, context->GetIsolate());
size_t index = static_cast<int>(data->contexts_.Size());
data->contexts_.Append(context);
return index;
}
StartupData SnapshotCreator::CreateBlob(
......@@ -464,13 +466,25 @@ StartupData SnapshotCreator::CreateBlob(
i::StartupSerializer startup_serializer(isolate, function_code_handling);
startup_serializer.SerializeStrongReferences();
i::PartialSerializer context_serializer(isolate, &startup_serializer);
context_serializer.Serialize(&contexts[0]);
// Serialize each context with a new partial serializer.
i::List<i::SnapshotData*> context_snapshots(num_contexts);
for (int i = 0; i < num_contexts; i++) {
i::PartialSerializer partial_serializer(isolate, &startup_serializer);
partial_serializer.Serialize(&contexts[i]);
context_snapshots.Add(new i::SnapshotData(&partial_serializer));
}
startup_serializer.SerializeWeakReferencesAndDeferred();
i::SnapshotData startup_snapshot(&startup_serializer);
StartupData result =
i::Snapshot::CreateSnapshotBlob(&startup_snapshot, &context_snapshots);
// Delete heap-allocated context snapshot instances.
for (const auto& context_snapshot : context_snapshots) {
delete context_snapshot;
}
data->created_ = true;
return i::Snapshot::CreateSnapshotBlob(&startup_serializer,
&context_serializer);
return result;
}
StartupData V8::CreateSnapshotDataBlob(const char* embedded_source) {
......@@ -5532,11 +5546,10 @@ const char* v8::V8::GetVersion() {
return i::Version::GetVersion();
}
static i::Handle<i::Context> CreateEnvironment(
i::Isolate* isolate, v8::ExtensionConfiguration* extensions,
v8::Local<ObjectTemplate> global_template,
v8::Local<Value> maybe_global_proxy) {
v8::Local<Value> maybe_global_proxy, size_t context_snapshot_index) {
i::Handle<i::Context> env;
// Enter V8 via an ENTER_V8 scope.
......@@ -5581,7 +5594,7 @@ static i::Handle<i::Context> CreateEnvironment(
}
// Create the environment.
env = isolate->bootstrapper()->CreateEnvironment(
maybe_proxy, proxy_template, extensions);
maybe_proxy, proxy_template, extensions, context_snapshot_index);
// Restore the access check info on the global template.
if (!global_template.IsEmpty()) {
......@@ -5601,14 +5614,16 @@ static i::Handle<i::Context> CreateEnvironment(
Local<Context> v8::Context::New(v8::Isolate* external_isolate,
v8::ExtensionConfiguration* extensions,
v8::Local<ObjectTemplate> global_template,
v8::Local<Value> global_object) {
v8::Local<Value> global_object,
size_t context_snapshot_index) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(external_isolate);
LOG_API(isolate, Context, New);
i::HandleScope scope(isolate);
ExtensionConfiguration no_extensions;
if (extensions == NULL) extensions = &no_extensions;
i::Handle<i::Context> env =
CreateEnvironment(isolate, extensions, global_template, global_object);
CreateEnvironment(isolate, extensions, global_template, global_object,
context_snapshot_index);
if (env.is_null()) {
if (isolate->has_pending_exception()) {
isolate->OptionalRescheduleException(true);
......
......@@ -138,7 +138,7 @@ class Genesis BASE_EMBEDDED {
public:
Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions,
v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
GlobalContextType context_type);
~Genesis() { }
......@@ -324,10 +324,11 @@ void Bootstrapper::Iterate(ObjectVisitor* v) {
Handle<Context> Bootstrapper::CreateEnvironment(
MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions, GlobalContextType context_type) {
v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
GlobalContextType context_type) {
HandleScope scope(isolate_);
Genesis genesis(isolate_, maybe_global_proxy, global_proxy_template,
extensions, context_type);
extensions, context_snapshot_index, context_type);
Handle<Context> env = genesis.result();
if (env.is_null() ||
(context_type != THIN_CONTEXT && !InstallExtensions(env, extensions))) {
......@@ -3800,7 +3801,7 @@ Genesis::Genesis(Isolate* isolate,
MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions,
GlobalContextType context_type)
size_t context_snapshot_index, GlobalContextType context_type)
: isolate_(isolate), active_(isolate->bootstrapper()) {
NoTrackDoubleFieldsForSerializerScope disable_scope(isolate);
result_ = Handle<Context>::null();
......@@ -3829,7 +3830,8 @@ Genesis::Genesis(Isolate* isolate,
// a snapshot. Otherwise we have to build the context from scratch.
// Also create a context from scratch to expose natives, if required by flag.
if (!isolate->initialized_from_snapshot() ||
!Snapshot::NewContextFromSnapshot(isolate, global_proxy)
!Snapshot::NewContextFromSnapshot(isolate, global_proxy,
context_snapshot_index)
.ToHandle(&native_context_)) {
native_context_ = Handle<Context>();
}
......
......@@ -79,7 +79,7 @@ class Bootstrapper final {
Handle<Context> CreateEnvironment(
MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_object_template,
v8::ExtensionConfiguration* extensions,
v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
GlobalContextType context_type = FULL_CONTEXT);
// Detach the environment from its outer global object.
......
......@@ -536,9 +536,13 @@ bool Debug::Load() {
// Create the debugger context.
HandleScope scope(isolate_);
ExtensionConfiguration no_extensions;
// TODO(yangguo): we rely on the fact that first context snapshot is usable
// as debug context. This dependency is gone once we remove
// debug context completely.
static const int kFirstContextSnapshotIndex = 0;
Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment(
MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), &no_extensions,
DEBUG_CONTEXT);
kFirstContextSnapshotIndex, DEBUG_CONTEXT);
// Fail if no context could be created.
if (context.is_null()) return false;
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/snapshot/partial-serializer.h"
#include "src/snapshot/startup-serializer.h"
#include "src/objects-inl.h"
......@@ -10,10 +11,8 @@ namespace v8 {
namespace internal {
PartialSerializer::PartialSerializer(Isolate* isolate,
Serializer* startup_snapshot_serializer)
: Serializer(isolate),
startup_serializer_(startup_snapshot_serializer),
next_partial_cache_index_(0) {
StartupSerializer* startup_serializer)
: Serializer(isolate), startup_serializer_(startup_serializer) {
InitializeCodeAddressMap();
}
......@@ -61,7 +60,7 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
if (ShouldBeInThePartialSnapshotCache(obj)) {
FlushSkip(skip);
int cache_index = PartialSnapshotCacheIndex(obj);
int cache_index = startup_serializer_->PartialSnapshotCacheIndex(obj);
sink_.Put(kPartialSnapshotCache + how_to_code + where_to_point,
"PartialSnapshotCache");
sink_.PutInt(cache_index, "partial_snapshot_cache_index");
......@@ -93,19 +92,6 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
serializer.Serialize();
}
int PartialSerializer::PartialSnapshotCacheIndex(HeapObject* heap_object) {
int index = partial_cache_index_map_.LookupOrInsert(
heap_object, next_partial_cache_index_);
if (index == PartialCacheIndexMap::kInvalidIndex) {
// 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));
return next_partial_cache_index_++;
}
return index;
}
bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject* o) {
// Scripts should be referred only through shared function infos. We can't
// allow them to be part of the partial snapshot because they contain a
......
......@@ -11,9 +11,11 @@
namespace v8 {
namespace internal {
class StartupSerializer;
class PartialSerializer : public Serializer {
public:
PartialSerializer(Isolate* isolate, Serializer* startup_snapshot_serializer);
PartialSerializer(Isolate* isolate, StartupSerializer* startup_serializer);
~PartialSerializer() override;
......@@ -21,36 +23,12 @@ class PartialSerializer : public Serializer {
void Serialize(Object** o);
private:
class PartialCacheIndexMap : public AddressMapBase {
public:
PartialCacheIndexMap() : map_(base::HashMap::PointersMatch) {}
static const int kInvalidIndex = -1;
// Lookup object in the map. Return its index if found, or create
// a new entry with new_index as value, and return kInvalidIndex.
int LookupOrInsert(HeapObject* obj, int new_index) {
base::HashMap::Entry* entry = LookupEntry(&map_, obj, false);
if (entry != NULL) return GetValue(entry);
SetValue(LookupEntry(&map_, obj, true), static_cast<uint32_t>(new_index));
return kInvalidIndex;
}
private:
base::HashMap map_;
DISALLOW_COPY_AND_ASSIGN(PartialCacheIndexMap);
};
void SerializeObject(HeapObject* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) override;
int PartialSnapshotCacheIndex(HeapObject* o);
bool ShouldBeInThePartialSnapshotCache(HeapObject* o);
Serializer* startup_serializer_;
PartialCacheIndexMap partial_cache_index_map_;
int next_partial_cache_index_;
StartupSerializer* startup_serializer_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
};
......
This diff is collapsed.
......@@ -16,6 +16,41 @@ class Isolate;
class PartialSerializer;
class StartupSerializer;
// Wrapper around reservation sizes and the serialization payload.
class SnapshotData : public SerializedData {
public:
// Used when producing.
explicit SnapshotData(const Serializer* serializer);
// Used when consuming.
explicit SnapshotData(const Vector<const byte> snapshot)
: SerializedData(const_cast<byte*>(snapshot.begin()), snapshot.length()) {
CHECK(IsSane());
}
Vector<const Reservation> Reservations() const;
Vector<const byte> Payload() const;
Vector<const byte> RawData() const {
return Vector<const byte>(data_, size_);
}
private:
bool IsSane();
// The data header consists of uint32_t-sized entries:
// [0] magic number and external reference count
// [1] version hash
// [2] number of reservation size entries
// [3] payload length
// ... reservations
// ... serialized payload
static const int kCheckSumOffset = kMagicNumberOffset + kInt32Size;
static const int kNumReservationsOffset = kCheckSumOffset + kInt32Size;
static const int kPayloadLengthOffset = kNumReservationsOffset + kInt32Size;
static const int kHeaderSize = kPayloadLengthOffset + kInt32Size;
};
class Snapshot : public AllStatic {
public:
// Initialize the Isolate from the internal snapshot. Returns false if no
......@@ -23,7 +58,8 @@ class Snapshot : public AllStatic {
static bool Initialize(Isolate* isolate);
// Create a new context using the internal partial snapshot.
static MaybeHandle<Context> NewContextFromSnapshot(
Isolate* isolate, Handle<JSGlobalProxy> global_proxy);
Isolate* isolate, Handle<JSGlobalProxy> global_proxy,
size_t context_index);
static bool HaveASnapshotToStartFrom(Isolate* isolate);
......@@ -36,32 +72,44 @@ class Snapshot : public AllStatic {
static const v8::StartupData* DefaultSnapshotBlob();
static v8::StartupData CreateSnapshotBlob(
const StartupSerializer* startup_serializer,
const PartialSerializer* context_serializer);
const SnapshotData* startup_snapshot,
const List<SnapshotData*>* context_snapshots);
#ifdef DEBUG
static bool SnapshotIsValid(v8::StartupData* snapshot_blob);
#endif // DEBUG
private:
static int ExtractNumContexts(const v8::StartupData* data);
static Vector<const byte> ExtractStartupData(const v8::StartupData* data);
static Vector<const byte> ExtractContextData(const v8::StartupData* data);
static Vector<const byte> ExtractContextData(const v8::StartupData* data,
int index);
// Snapshot blob layout:
// [0 - 5] pre-calculated first page sizes for paged spaces
// [6] serialized start up data length
// ... serialized start up data
// ... serialized context data
// [6] number of contexts N
// [7] offset to context 0
// [8] offset to context 1
// ...
// ... offset to context N - 1
// ... startup snapshot data
// ... context 0 snapshot data
// ... context 1 snapshot data
static const int kNumPagedSpaces = LAST_PAGED_SPACE - FIRST_PAGED_SPACE + 1;
static const int kFirstPageSizesOffset = 0;
static const int kStartupLengthOffset =
static const int kNumberOfContextsOffset =
kFirstPageSizesOffset + kNumPagedSpaces * kInt32Size;
static const int kStartupDataOffset = kStartupLengthOffset + kInt32Size;
static const int kFirstContextOffsetOffset =
kNumberOfContextsOffset + kInt32Size;
static int StartupSnapshotOffset(int num_contexts) {
return kFirstContextOffsetOffset + num_contexts * kInt32Size;
}
static int ContextOffset(int startup_length) {
return kStartupDataOffset + startup_length;
static int ContextSnapshotOffsetOffset(int index) {
return kFirstContextOffsetOffset + index * kInt32Size;
}
DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot);
......@@ -71,41 +119,6 @@ class Snapshot : public AllStatic {
void SetSnapshotFromFile(StartupData* snapshot_blob);
#endif
// Wrapper around reservation sizes and the serialization payload.
class SnapshotData : public SerializedData {
public:
// Used when producing.
explicit SnapshotData(const Serializer* serializer);
// Used when consuming.
explicit SnapshotData(const Vector<const byte> snapshot)
: SerializedData(const_cast<byte*>(snapshot.begin()), snapshot.length()) {
CHECK(IsSane());
}
Vector<const Reservation> Reservations() const;
Vector<const byte> Payload() const;
Vector<const byte> RawData() const {
return Vector<const byte>(data_, size_);
}
private:
bool IsSane();
// The data header consists of uint32_t-sized entries:
// [0] magic number and external reference count
// [1] version hash
// [2] number of reservation size entries
// [3] payload length
// ... reservations
// ... serialized payload
static const int kCheckSumOffset = kMagicNumberOffset + kInt32Size;
static const int kNumReservationsOffset = kCheckSumOffset + kInt32Size;
static const int kPayloadLengthOffset = kNumReservationsOffset + kInt32Size;
static const int kHeaderSize = kPayloadLengthOffset + kInt32Size;
};
} // namespace internal
} // namespace v8
......
......@@ -90,6 +90,17 @@ void StartupSerializer::SerializeWeakReferencesAndDeferred() {
Pad();
}
int StartupSerializer::PartialSnapshotCacheIndex(HeapObject* heap_object) {
int index;
if (!partial_cache_index_map_.LookupOrInsert(heap_object, &index)) {
// 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.
VisitPointer(reinterpret_cast<Object**>(&heap_object));
}
return index;
}
void StartupSerializer::Synchronize(VisitorSynchronization::SyncTag tag) {
// We expect the builtins tag after builtins have been serialized.
DCHECK(!serializing_builtins_ || tag == VisitorSynchronization::kBuiltins);
......
......@@ -27,7 +27,34 @@ class StartupSerializer : public Serializer {
void SerializeStrongReferences();
void SerializeWeakReferencesAndDeferred();
int PartialSnapshotCacheIndex(HeapObject* o);
private:
class PartialCacheIndexMap : public AddressMapBase {
public:
PartialCacheIndexMap()
: map_(base::HashMap::PointersMatch), next_index_(0) {}
// Lookup object in the map. Return its index if found, or create
// a new entry with new_index as value, and return kInvalidIndex.
bool LookupOrInsert(HeapObject* obj, int* index_out) {
base::HashMap::Entry* entry = LookupEntry(&map_, obj, false);
if (entry != NULL) {
*index_out = GetValue(entry);
return true;
}
*index_out = next_index_;
SetValue(LookupEntry(&map_, obj, true), next_index_++);
return false;
}
private:
base::HashMap map_;
int next_index_;
DISALLOW_COPY_AND_ASSIGN(PartialCacheIndexMap);
};
// The StartupSerializer has to serialize the root array, which is slightly
// different.
void VisitPointers(Object** start, Object** end) override;
......@@ -45,6 +72,7 @@ class StartupSerializer : public Serializer {
bool serializing_builtins_;
bool serializing_immortal_immovables_roots_;
std::bitset<Heap::kStrongRootListLength> root_has_been_serialized_;
PartialCacheIndexMap partial_cache_index_map_;
DISALLOW_COPY_AND_ASSIGN(StartupSerializer);
};
......
......@@ -1882,6 +1882,71 @@ TEST(CodeSerializerCell) {
}
#endif // V8_TARGET_ARCH_X64
TEST(SnapshotCreatorMultipleContexts) {
DisableTurbofan();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun("var f = function() { return 1; }");
CHECK_EQ(0, creator.AddContext(context));
}
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun("var f = function() { return 2; }");
CHECK_EQ(1, creator.AddContext(context));
}
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK_EQ(2, creator.AddContext(context));
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams params;
params.snapshot_blob = &blob;
params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::ExtensionConfiguration* no_extension = nullptr;
v8::Local<v8::ObjectTemplate> no_template = v8::Local<v8::ObjectTemplate>();
v8::Local<v8::Value> no_object = v8::Local<v8::Value>();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, no_template, no_object, 0);
v8::Context::Scope context_scope(context);
ExpectInt32("f()", 1);
}
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, no_template, no_object, 1);
v8::Context::Scope context_scope(context);
ExpectInt32("f()", 2);
}
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, no_template, no_object, 2);
v8::Context::Scope context_scope(context);
ExpectUndefined("this.f");
}
}
isolate->Dispose();
delete[] blob.data;
}
TEST(SerializationMemoryStats) {
FLAG_profile_deserialization = true;
FLAG_always_opt = false;
......
......@@ -12,7 +12,7 @@
},
{
"name": "ReservedMemoryContext",
"results_regexp": "(\\d+) bytes per context$"
"results_regexp": "(\\d+) bytes per context #0$"
},
{
"name": "SnapshotSizeStartup",
......@@ -20,7 +20,7 @@
},
{
"name": "SnapshotSizeContext",
"results_regexp": "(\\d+) bytes for context$"
"results_regexp": "(\\d+) bytes for context #0$"
}
]
}
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