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 { ...@@ -6787,8 +6787,9 @@ class SnapshotCreator {
/** /**
* Add a context to be included in the snapshot blob. * 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. * Created a snapshot data blob.
...@@ -7092,7 +7093,8 @@ class V8_EXPORT Context { ...@@ -7092,7 +7093,8 @@ class V8_EXPORT Context {
static Local<Context> New( static Local<Context> New(
Isolate* isolate, ExtensionConfiguration* extensions = NULL, Isolate* isolate, ExtensionConfiguration* extensions = NULL,
Local<ObjectTemplate> global_template = Local<ObjectTemplate>(), 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 * Sets the security token for the context. To access an object in
......
...@@ -429,13 +429,15 @@ Isolate* SnapshotCreator::GetIsolate() { ...@@ -429,13 +429,15 @@ Isolate* SnapshotCreator::GetIsolate() {
return SnapshotCreatorData::cast(data_)->isolate_; return SnapshotCreatorData::cast(data_)->isolate_;
} }
void SnapshotCreator::AddContext(Local<Context> context) { size_t SnapshotCreator::AddContext(Local<Context> context) {
DCHECK(!context.IsEmpty()); DCHECK(!context.IsEmpty());
SnapshotCreatorData* data = SnapshotCreatorData::cast(data_); SnapshotCreatorData* data = SnapshotCreatorData::cast(data_);
DCHECK(!data->created_); DCHECK(!data->created_);
Isolate* isolate = data->isolate_; Isolate* isolate = data->isolate_;
CHECK_EQ(isolate, context->GetIsolate()); CHECK_EQ(isolate, context->GetIsolate());
size_t index = static_cast<int>(data->contexts_.Size());
data->contexts_.Append(context); data->contexts_.Append(context);
return index;
} }
StartupData SnapshotCreator::CreateBlob( StartupData SnapshotCreator::CreateBlob(
...@@ -464,13 +466,25 @@ StartupData SnapshotCreator::CreateBlob( ...@@ -464,13 +466,25 @@ StartupData SnapshotCreator::CreateBlob(
i::StartupSerializer startup_serializer(isolate, function_code_handling); i::StartupSerializer startup_serializer(isolate, function_code_handling);
startup_serializer.SerializeStrongReferences(); startup_serializer.SerializeStrongReferences();
i::PartialSerializer context_serializer(isolate, &startup_serializer); // Serialize each context with a new partial serializer.
context_serializer.Serialize(&contexts[0]); 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(); 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; data->created_ = true;
return i::Snapshot::CreateSnapshotBlob(&startup_serializer, return result;
&context_serializer);
} }
StartupData V8::CreateSnapshotDataBlob(const char* embedded_source) { StartupData V8::CreateSnapshotDataBlob(const char* embedded_source) {
...@@ -5532,11 +5546,10 @@ const char* v8::V8::GetVersion() { ...@@ -5532,11 +5546,10 @@ const char* v8::V8::GetVersion() {
return i::Version::GetVersion(); return i::Version::GetVersion();
} }
static i::Handle<i::Context> CreateEnvironment( static i::Handle<i::Context> CreateEnvironment(
i::Isolate* isolate, v8::ExtensionConfiguration* extensions, i::Isolate* isolate, v8::ExtensionConfiguration* extensions,
v8::Local<ObjectTemplate> global_template, 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; i::Handle<i::Context> env;
// Enter V8 via an ENTER_V8 scope. // Enter V8 via an ENTER_V8 scope.
...@@ -5581,7 +5594,7 @@ static i::Handle<i::Context> CreateEnvironment( ...@@ -5581,7 +5594,7 @@ static i::Handle<i::Context> CreateEnvironment(
} }
// Create the environment. // Create the environment.
env = isolate->bootstrapper()->CreateEnvironment( 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. // Restore the access check info on the global template.
if (!global_template.IsEmpty()) { if (!global_template.IsEmpty()) {
...@@ -5601,14 +5614,16 @@ static i::Handle<i::Context> CreateEnvironment( ...@@ -5601,14 +5614,16 @@ static i::Handle<i::Context> CreateEnvironment(
Local<Context> v8::Context::New(v8::Isolate* external_isolate, Local<Context> v8::Context::New(v8::Isolate* external_isolate,
v8::ExtensionConfiguration* extensions, v8::ExtensionConfiguration* extensions,
v8::Local<ObjectTemplate> global_template, 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); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(external_isolate);
LOG_API(isolate, Context, New); LOG_API(isolate, Context, New);
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
ExtensionConfiguration no_extensions; ExtensionConfiguration no_extensions;
if (extensions == NULL) extensions = &no_extensions; if (extensions == NULL) extensions = &no_extensions;
i::Handle<i::Context> env = 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 (env.is_null()) {
if (isolate->has_pending_exception()) { if (isolate->has_pending_exception()) {
isolate->OptionalRescheduleException(true); isolate->OptionalRescheduleException(true);
......
...@@ -138,7 +138,7 @@ class Genesis BASE_EMBEDDED { ...@@ -138,7 +138,7 @@ class Genesis BASE_EMBEDDED {
public: public:
Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy, Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template, v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions, v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
GlobalContextType context_type); GlobalContextType context_type);
~Genesis() { } ~Genesis() { }
...@@ -324,10 +324,11 @@ void Bootstrapper::Iterate(ObjectVisitor* v) { ...@@ -324,10 +324,11 @@ void Bootstrapper::Iterate(ObjectVisitor* v) {
Handle<Context> Bootstrapper::CreateEnvironment( Handle<Context> Bootstrapper::CreateEnvironment(
MaybeHandle<JSGlobalProxy> maybe_global_proxy, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template, 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_); HandleScope scope(isolate_);
Genesis genesis(isolate_, maybe_global_proxy, global_proxy_template, Genesis genesis(isolate_, maybe_global_proxy, global_proxy_template,
extensions, context_type); extensions, context_snapshot_index, context_type);
Handle<Context> env = genesis.result(); Handle<Context> env = genesis.result();
if (env.is_null() || if (env.is_null() ||
(context_type != THIN_CONTEXT && !InstallExtensions(env, extensions))) { (context_type != THIN_CONTEXT && !InstallExtensions(env, extensions))) {
...@@ -3800,7 +3801,7 @@ Genesis::Genesis(Isolate* isolate, ...@@ -3800,7 +3801,7 @@ Genesis::Genesis(Isolate* isolate,
MaybeHandle<JSGlobalProxy> maybe_global_proxy, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template, v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions, v8::ExtensionConfiguration* extensions,
GlobalContextType context_type) size_t context_snapshot_index, GlobalContextType context_type)
: isolate_(isolate), active_(isolate->bootstrapper()) { : isolate_(isolate), active_(isolate->bootstrapper()) {
NoTrackDoubleFieldsForSerializerScope disable_scope(isolate); NoTrackDoubleFieldsForSerializerScope disable_scope(isolate);
result_ = Handle<Context>::null(); result_ = Handle<Context>::null();
...@@ -3829,7 +3830,8 @@ Genesis::Genesis(Isolate* isolate, ...@@ -3829,7 +3830,8 @@ Genesis::Genesis(Isolate* isolate,
// a snapshot. Otherwise we have to build the context from scratch. // a snapshot. Otherwise we have to build the context from scratch.
// Also create a context from scratch to expose natives, if required by flag. // Also create a context from scratch to expose natives, if required by flag.
if (!isolate->initialized_from_snapshot() || if (!isolate->initialized_from_snapshot() ||
!Snapshot::NewContextFromSnapshot(isolate, global_proxy) !Snapshot::NewContextFromSnapshot(isolate, global_proxy,
context_snapshot_index)
.ToHandle(&native_context_)) { .ToHandle(&native_context_)) {
native_context_ = Handle<Context>(); native_context_ = Handle<Context>();
} }
......
...@@ -79,7 +79,7 @@ class Bootstrapper final { ...@@ -79,7 +79,7 @@ class Bootstrapper final {
Handle<Context> CreateEnvironment( Handle<Context> CreateEnvironment(
MaybeHandle<JSGlobalProxy> maybe_global_proxy, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_object_template, v8::Local<v8::ObjectTemplate> global_object_template,
v8::ExtensionConfiguration* extensions, v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
GlobalContextType context_type = FULL_CONTEXT); GlobalContextType context_type = FULL_CONTEXT);
// Detach the environment from its outer global object. // Detach the environment from its outer global object.
......
...@@ -536,9 +536,13 @@ bool Debug::Load() { ...@@ -536,9 +536,13 @@ bool Debug::Load() {
// Create the debugger context. // Create the debugger context.
HandleScope scope(isolate_); HandleScope scope(isolate_);
ExtensionConfiguration no_extensions; 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( Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment(
MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), &no_extensions, MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), &no_extensions,
DEBUG_CONTEXT); kFirstContextSnapshotIndex, DEBUG_CONTEXT);
// Fail if no context could be created. // Fail if no context could be created.
if (context.is_null()) return false; if (context.is_null()) return false;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/snapshot/partial-serializer.h" #include "src/snapshot/partial-serializer.h"
#include "src/snapshot/startup-serializer.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
...@@ -10,10 +11,8 @@ namespace v8 { ...@@ -10,10 +11,8 @@ namespace v8 {
namespace internal { namespace internal {
PartialSerializer::PartialSerializer(Isolate* isolate, PartialSerializer::PartialSerializer(Isolate* isolate,
Serializer* startup_snapshot_serializer) StartupSerializer* startup_serializer)
: Serializer(isolate), : Serializer(isolate), startup_serializer_(startup_serializer) {
startup_serializer_(startup_snapshot_serializer),
next_partial_cache_index_(0) {
InitializeCodeAddressMap(); InitializeCodeAddressMap();
} }
...@@ -61,7 +60,7 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, ...@@ -61,7 +60,7 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
if (ShouldBeInThePartialSnapshotCache(obj)) { if (ShouldBeInThePartialSnapshotCache(obj)) {
FlushSkip(skip); FlushSkip(skip);
int cache_index = PartialSnapshotCacheIndex(obj); int cache_index = startup_serializer_->PartialSnapshotCacheIndex(obj);
sink_.Put(kPartialSnapshotCache + how_to_code + where_to_point, sink_.Put(kPartialSnapshotCache + how_to_code + where_to_point,
"PartialSnapshotCache"); "PartialSnapshotCache");
sink_.PutInt(cache_index, "partial_snapshot_cache_index"); sink_.PutInt(cache_index, "partial_snapshot_cache_index");
...@@ -93,19 +92,6 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, ...@@ -93,19 +92,6 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
serializer.Serialize(); 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) { bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject* o) {
// Scripts should be referred only through shared function infos. We can't // 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 // allow them to be part of the partial snapshot because they contain a
......
...@@ -11,9 +11,11 @@ ...@@ -11,9 +11,11 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class StartupSerializer;
class PartialSerializer : public Serializer { class PartialSerializer : public Serializer {
public: public:
PartialSerializer(Isolate* isolate, Serializer* startup_snapshot_serializer); PartialSerializer(Isolate* isolate, StartupSerializer* startup_serializer);
~PartialSerializer() override; ~PartialSerializer() override;
...@@ -21,36 +23,12 @@ class PartialSerializer : public Serializer { ...@@ -21,36 +23,12 @@ class PartialSerializer : public Serializer {
void Serialize(Object** o); void Serialize(Object** o);
private: 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, void SerializeObject(HeapObject* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) override; WhereToPoint where_to_point, int skip) override;
int PartialSnapshotCacheIndex(HeapObject* o);
bool ShouldBeInThePartialSnapshotCache(HeapObject* o); bool ShouldBeInThePartialSnapshotCache(HeapObject* o);
Serializer* startup_serializer_; StartupSerializer* startup_serializer_;
PartialCacheIndexMap partial_cache_index_map_;
int next_partial_cache_index_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer); DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
}; };
......
...@@ -18,8 +18,7 @@ namespace internal { ...@@ -18,8 +18,7 @@ namespace internal {
#ifdef DEBUG #ifdef DEBUG
bool Snapshot::SnapshotIsValid(v8::StartupData* snapshot_blob) { bool Snapshot::SnapshotIsValid(v8::StartupData* snapshot_blob) {
return !Snapshot::ExtractStartupData(snapshot_blob).is_empty() && return Snapshot::ExtractNumContexts(snapshot_blob) > 0;
!Snapshot::ExtractContextData(snapshot_blob).is_empty();
} }
#endif // DEBUG #endif // DEBUG
...@@ -61,15 +60,16 @@ bool Snapshot::Initialize(Isolate* isolate) { ...@@ -61,15 +60,16 @@ bool Snapshot::Initialize(Isolate* isolate) {
return success; return success;
} }
MaybeHandle<Context> Snapshot::NewContextFromSnapshot( MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
Isolate* isolate, Handle<JSGlobalProxy> global_proxy) { Isolate* isolate, Handle<JSGlobalProxy> global_proxy,
size_t context_index) {
if (!isolate->snapshot_available()) return Handle<Context>(); if (!isolate->snapshot_available()) return Handle<Context>();
base::ElapsedTimer timer; base::ElapsedTimer timer;
if (FLAG_profile_deserialization) timer.Start(); if (FLAG_profile_deserialization) timer.Start();
const v8::StartupData* blob = isolate->snapshot_blob(); const v8::StartupData* blob = isolate->snapshot_blob();
Vector<const byte> context_data = ExtractContextData(blob); Vector<const byte> context_data =
ExtractContextData(blob, static_cast<int>(context_index));
SnapshotData snapshot_data(context_data); SnapshotData snapshot_data(context_data);
Deserializer deserializer(&snapshot_data); Deserializer deserializer(&snapshot_data);
...@@ -81,133 +81,163 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot( ...@@ -81,133 +81,163 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
if (FLAG_profile_deserialization) { if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF(); double ms = timer.Elapsed().InMillisecondsF();
int bytes = context_data.length(); int bytes = context_data.length();
PrintF("[Deserializing context (%d bytes) took %0.3f ms]\n", bytes, ms); PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
context_index, bytes, ms);
} }
return Handle<Context>::cast(result); return Handle<Context>::cast(result);
} }
void UpdateMaxRequirementPerPage(
uint32_t* requirements,
Vector<const SerializedData::Reservation> reservations) {
int space = 0;
uint32_t current_requirement = 0;
for (const auto& reservation : reservations) {
current_requirement += reservation.chunk_size();
if (reservation.is_last()) {
requirements[space] = std::max(requirements[space], current_requirement);
current_requirement = 0;
space++;
}
}
DCHECK_EQ(i::Serializer::kNumberOfSpaces, space);
}
void CalculateFirstPageSizes(const SnapshotData* startup_snapshot, void CalculateFirstPageSizes(const SnapshotData* startup_snapshot,
const SnapshotData* context_snapshot, const List<SnapshotData*>* context_snapshots,
uint32_t* sizes_out) { uint32_t* sizes_out) {
Vector<const SerializedData::Reservation> startup_reservations =
startup_snapshot->Reservations();
Vector<const SerializedData::Reservation> context_reservations =
context_snapshot->Reservations();
int startup_index = 0;
int context_index = 0;
if (FLAG_profile_deserialization) { if (FLAG_profile_deserialization) {
int startup_total = 0; int startup_total = 0;
int context_total = 0; PrintF("Deserialization will reserve:\n");
for (const auto& reservation : startup_reservations) { for (const auto& reservation : startup_snapshot->Reservations()) {
startup_total += reservation.chunk_size(); startup_total += reservation.chunk_size();
} }
for (const auto& reservation : context_reservations) { PrintF("%10d bytes per isolate\n", startup_total);
for (int i = 0; i < context_snapshots->length(); i++) {
int context_total = 0;
for (const auto& reservation : context_snapshots->at(i)->Reservations()) {
context_total += reservation.chunk_size(); context_total += reservation.chunk_size();
} }
PrintF( PrintF("%10d bytes per context #%d\n", context_total, i);
"Deserialization will reserve:\n" }
"%10d bytes per isolate\n"
"%10d bytes per context\n",
startup_total, context_total);
} }
uint32_t startup_requirements[i::Serializer::kNumberOfSpaces];
uint32_t context_requirements[i::Serializer::kNumberOfSpaces];
for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) { for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
bool single_chunk = true; startup_requirements[space] = 0;
while (!startup_reservations[startup_index].is_last()) { context_requirements[space] = 0;
single_chunk = false;
startup_index++;
} }
while (!context_reservations[context_index].is_last()) {
single_chunk = false; UpdateMaxRequirementPerPage(startup_requirements,
context_index++; startup_snapshot->Reservations());
for (const auto& context_snapshot : *context_snapshots) {
UpdateMaxRequirementPerPage(context_requirements,
context_snapshot->Reservations());
} }
uint32_t required = kMaxUInt32; for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
if (single_chunk) { // If the space requirement for a page is less than a page size, we consider
// If both the startup snapshot data and the context snapshot data on // limiting the size of the first page in order to save memory on startup.
// this space fit in a single page, then we consider limiting the size uint32_t required = startup_requirements[space] +
// of the first page. For this, we add the chunk sizes and some extra 2 * context_requirements[space] +
// allowance. This way we achieve a smaller startup memory footprint.
required = (startup_reservations[startup_index].chunk_size() +
2 * context_reservations[context_index].chunk_size()) +
Page::kObjectStartOffset; Page::kObjectStartOffset;
// Add a small allowance to the code space for small scripts. // Add a small allowance to the code space for small scripts.
if (space == CODE_SPACE) required += 32 * KB; if (space == CODE_SPACE) required += 32 * KB;
}
if (space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE) { if (space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE) {
uint32_t max_size = uint32_t max_size =
MemoryAllocator::PageAreaSize(static_cast<AllocationSpace>(space)); MemoryAllocator::PageAreaSize(static_cast<AllocationSpace>(space));
sizes_out[space - FIRST_PAGED_SPACE] = Min(required, max_size); sizes_out[space - FIRST_PAGED_SPACE] = std::min(required, max_size);
} else {
DCHECK(single_chunk);
} }
startup_index++;
context_index++;
} }
DCHECK_EQ(startup_reservations.length(), startup_index);
DCHECK_EQ(context_reservations.length(), context_index);
} }
v8::StartupData Snapshot::CreateSnapshotBlob( v8::StartupData Snapshot::CreateSnapshotBlob(
const StartupSerializer* startup_serializer, const SnapshotData* startup_snapshot,
const PartialSerializer* context_serializer) { const List<SnapshotData*>* context_snapshots) {
SnapshotData startup_snapshot(startup_serializer); int num_contexts = context_snapshots->length();
SnapshotData context_snapshot(context_serializer); int startup_snapshot_offset = StartupSnapshotOffset(num_contexts);
Vector<const byte> startup_data = startup_snapshot.RawData(); int total_length = startup_snapshot_offset;
Vector<const byte> context_data = context_snapshot.RawData(); total_length += startup_snapshot->RawData().length();
for (const auto& context_snapshot : *context_snapshots) {
total_length += context_snapshot->RawData().length();
}
uint32_t first_page_sizes[kNumPagedSpaces]; uint32_t first_page_sizes[kNumPagedSpaces];
CalculateFirstPageSizes(startup_snapshot, context_snapshots,
CalculateFirstPageSizes(&startup_snapshot, &context_snapshot,
first_page_sizes); first_page_sizes);
int startup_length = startup_data.length(); char* data = new char[total_length];
int context_length = context_data.length();
int context_offset = ContextOffset(startup_length);
int length = context_offset + context_length;
char* data = new char[length];
memcpy(data + kFirstPageSizesOffset, first_page_sizes, memcpy(data + kFirstPageSizesOffset, first_page_sizes,
kNumPagedSpaces * kInt32Size); kNumPagedSpaces * kInt32Size);
memcpy(data + kStartupLengthOffset, &startup_length, kInt32Size); memcpy(data + kNumberOfContextsOffset, &num_contexts, kInt32Size);
memcpy(data + kStartupDataOffset, startup_data.begin(), startup_length); int payload_offset = StartupSnapshotOffset(num_contexts);
memcpy(data + context_offset, context_data.begin(), context_length); int payload_length = startup_snapshot->RawData().length();
v8::StartupData result = {data, length}; memcpy(data + payload_offset, startup_snapshot->RawData().start(),
payload_length);
if (FLAG_profile_deserialization) {
PrintF("Snapshot blob consists of:\n%10d bytes for startup\n",
payload_length);
}
payload_offset += payload_length;
for (int i = 0; i < num_contexts; i++) {
memcpy(data + ContextSnapshotOffsetOffset(i), &payload_offset, kInt32Size);
SnapshotData* context_snapshot = context_snapshots->at(i);
payload_length = context_snapshot->RawData().length();
memcpy(data + payload_offset, context_snapshot->RawData().start(),
payload_length);
if (FLAG_profile_deserialization) { if (FLAG_profile_deserialization) {
PrintF( PrintF("%10d bytes for context #%d\n", payload_length, i);
"Snapshot blob consists of:\n"
"%10d bytes for startup\n"
"%10d bytes for context\n",
startup_length, context_length);
} }
payload_offset += payload_length;
}
v8::StartupData result = {data, total_length};
return result; return result;
} }
int Snapshot::ExtractNumContexts(const v8::StartupData* data) {
CHECK_LT(kNumberOfContextsOffset, data->raw_size);
int num_contexts;
memcpy(&num_contexts, data->data + kNumberOfContextsOffset, kInt32Size);
return num_contexts;
}
Vector<const byte> Snapshot::ExtractStartupData(const v8::StartupData* data) { Vector<const byte> Snapshot::ExtractStartupData(const v8::StartupData* data) {
DCHECK_LT(kIntSize, data->raw_size); int num_contexts = ExtractNumContexts(data);
int startup_length; int startup_offset = StartupSnapshotOffset(num_contexts);
memcpy(&startup_length, data->data + kStartupLengthOffset, kInt32Size); CHECK_LT(startup_offset, data->raw_size);
DCHECK_LT(startup_length, data->raw_size); int first_context_offset;
memcpy(&first_context_offset, data->data + ContextSnapshotOffsetOffset(0),
kInt32Size);
CHECK_LT(first_context_offset, data->raw_size);
int startup_length = first_context_offset - startup_offset;
const byte* startup_data = const byte* startup_data =
reinterpret_cast<const byte*>(data->data + kStartupDataOffset); reinterpret_cast<const byte*>(data->data + startup_offset);
return Vector<const byte>(startup_data, startup_length); return Vector<const byte>(startup_data, startup_length);
} }
Vector<const byte> Snapshot::ExtractContextData(const v8::StartupData* data,
int index) {
int num_contexts = ExtractNumContexts(data);
CHECK_LT(index, num_contexts);
int context_offset;
memcpy(&context_offset, data->data + ContextSnapshotOffsetOffset(index),
kInt32Size);
int next_context_offset;
if (index == num_contexts - 1) {
next_context_offset = data->raw_size;
} else {
memcpy(&next_context_offset,
data->data + ContextSnapshotOffsetOffset(index + 1), kInt32Size);
CHECK_LT(next_context_offset, data->raw_size);
}
Vector<const byte> Snapshot::ExtractContextData(const v8::StartupData* data) {
DCHECK_LT(kIntSize, data->raw_size);
int startup_length;
memcpy(&startup_length, data->data + kStartupLengthOffset, kIntSize);
int context_offset = ContextOffset(startup_length);
const byte* context_data = const byte* context_data =
reinterpret_cast<const byte*>(data->data + context_offset); reinterpret_cast<const byte*>(data->data + context_offset);
DCHECK_LT(context_offset, data->raw_size); int context_length = next_context_offset - context_offset;
int context_length = data->raw_size - context_offset;
return Vector<const byte>(context_data, context_length); return Vector<const byte>(context_data, context_length);
} }
......
...@@ -16,6 +16,41 @@ class Isolate; ...@@ -16,6 +16,41 @@ class Isolate;
class PartialSerializer; class PartialSerializer;
class StartupSerializer; 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 { class Snapshot : public AllStatic {
public: public:
// Initialize the Isolate from the internal snapshot. Returns false if no // Initialize the Isolate from the internal snapshot. Returns false if no
...@@ -23,7 +58,8 @@ class Snapshot : public AllStatic { ...@@ -23,7 +58,8 @@ class Snapshot : public AllStatic {
static bool Initialize(Isolate* isolate); static bool Initialize(Isolate* isolate);
// Create a new context using the internal partial snapshot. // Create a new context using the internal partial snapshot.
static MaybeHandle<Context> NewContextFromSnapshot( 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); static bool HaveASnapshotToStartFrom(Isolate* isolate);
...@@ -36,32 +72,44 @@ class Snapshot : public AllStatic { ...@@ -36,32 +72,44 @@ class Snapshot : public AllStatic {
static const v8::StartupData* DefaultSnapshotBlob(); static const v8::StartupData* DefaultSnapshotBlob();
static v8::StartupData CreateSnapshotBlob( static v8::StartupData CreateSnapshotBlob(
const StartupSerializer* startup_serializer, const SnapshotData* startup_snapshot,
const PartialSerializer* context_serializer); const List<SnapshotData*>* context_snapshots);
#ifdef DEBUG #ifdef DEBUG
static bool SnapshotIsValid(v8::StartupData* snapshot_blob); static bool SnapshotIsValid(v8::StartupData* snapshot_blob);
#endif // DEBUG #endif // DEBUG
private: private:
static int ExtractNumContexts(const v8::StartupData* data);
static Vector<const byte> ExtractStartupData(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: // Snapshot blob layout:
// [0 - 5] pre-calculated first page sizes for paged spaces // [0 - 5] pre-calculated first page sizes for paged spaces
// [6] serialized start up data length // [6] number of contexts N
// ... serialized start up data // [7] offset to context 0
// ... serialized context data // [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 kNumPagedSpaces = LAST_PAGED_SPACE - FIRST_PAGED_SPACE + 1;
static const int kFirstPageSizesOffset = 0; static const int kFirstPageSizesOffset = 0;
static const int kStartupLengthOffset = static const int kNumberOfContextsOffset =
kFirstPageSizesOffset + kNumPagedSpaces * kInt32Size; 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) { static int ContextSnapshotOffsetOffset(int index) {
return kStartupDataOffset + startup_length; return kFirstContextOffsetOffset + index * kInt32Size;
} }
DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot); DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot);
...@@ -71,41 +119,6 @@ class Snapshot : public AllStatic { ...@@ -71,41 +119,6 @@ class Snapshot : public AllStatic {
void SetSnapshotFromFile(StartupData* snapshot_blob); void SetSnapshotFromFile(StartupData* snapshot_blob);
#endif #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 internal
} // namespace v8 } // namespace v8
......
...@@ -90,6 +90,17 @@ void StartupSerializer::SerializeWeakReferencesAndDeferred() { ...@@ -90,6 +90,17 @@ void StartupSerializer::SerializeWeakReferencesAndDeferred() {
Pad(); 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) { void StartupSerializer::Synchronize(VisitorSynchronization::SyncTag tag) {
// We expect the builtins tag after builtins have been serialized. // We expect the builtins tag after builtins have been serialized.
DCHECK(!serializing_builtins_ || tag == VisitorSynchronization::kBuiltins); DCHECK(!serializing_builtins_ || tag == VisitorSynchronization::kBuiltins);
......
...@@ -27,7 +27,34 @@ class StartupSerializer : public Serializer { ...@@ -27,7 +27,34 @@ class StartupSerializer : public Serializer {
void SerializeStrongReferences(); void SerializeStrongReferences();
void SerializeWeakReferencesAndDeferred(); void SerializeWeakReferencesAndDeferred();
int PartialSnapshotCacheIndex(HeapObject* o);
private: 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 // The StartupSerializer has to serialize the root array, which is slightly
// different. // different.
void VisitPointers(Object** start, Object** end) override; void VisitPointers(Object** start, Object** end) override;
...@@ -45,6 +72,7 @@ class StartupSerializer : public Serializer { ...@@ -45,6 +72,7 @@ class StartupSerializer : public Serializer {
bool serializing_builtins_; bool serializing_builtins_;
bool serializing_immortal_immovables_roots_; bool serializing_immortal_immovables_roots_;
std::bitset<Heap::kStrongRootListLength> root_has_been_serialized_; std::bitset<Heap::kStrongRootListLength> root_has_been_serialized_;
PartialCacheIndexMap partial_cache_index_map_;
DISALLOW_COPY_AND_ASSIGN(StartupSerializer); DISALLOW_COPY_AND_ASSIGN(StartupSerializer);
}; };
......
...@@ -1882,6 +1882,71 @@ TEST(CodeSerializerCell) { ...@@ -1882,6 +1882,71 @@ TEST(CodeSerializerCell) {
} }
#endif // V8_TARGET_ARCH_X64 #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) { TEST(SerializationMemoryStats) {
FLAG_profile_deserialization = true; FLAG_profile_deserialization = true;
FLAG_always_opt = false; FLAG_always_opt = false;
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
}, },
{ {
"name": "ReservedMemoryContext", "name": "ReservedMemoryContext",
"results_regexp": "(\\d+) bytes per context$" "results_regexp": "(\\d+) bytes per context #0$"
}, },
{ {
"name": "SnapshotSizeStartup", "name": "SnapshotSizeStartup",
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
}, },
{ {
"name": "SnapshotSizeContext", "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