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);
};
......
......@@ -18,8 +18,7 @@ namespace internal {
#ifdef DEBUG
bool Snapshot::SnapshotIsValid(v8::StartupData* snapshot_blob) {
return !Snapshot::ExtractStartupData(snapshot_blob).is_empty() &&
!Snapshot::ExtractContextData(snapshot_blob).is_empty();
return Snapshot::ExtractNumContexts(snapshot_blob) > 0;
}
#endif // DEBUG
......@@ -61,15 +60,16 @@ bool Snapshot::Initialize(Isolate* isolate) {
return success;
}
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>();
base::ElapsedTimer timer;
if (FLAG_profile_deserialization) timer.Start();
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);
Deserializer deserializer(&snapshot_data);
......@@ -81,133 +81,163 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
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);
}
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,
const SnapshotData* context_snapshot,
const List<SnapshotData*>* context_snapshots,
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) {
int startup_total = 0;
int context_total = 0;
for (const auto& reservation : startup_reservations) {
PrintF("Deserialization will reserve:\n");
for (const auto& reservation : startup_snapshot->Reservations()) {
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();
}
PrintF(
"Deserialization will reserve:\n"
"%10d bytes per isolate\n"
"%10d bytes per context\n",
startup_total, context_total);
PrintF("%10d bytes per context #%d\n", context_total, i);
}
}
uint32_t startup_requirements[i::Serializer::kNumberOfSpaces];
uint32_t context_requirements[i::Serializer::kNumberOfSpaces];
for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
bool single_chunk = true;
while (!startup_reservations[startup_index].is_last()) {
single_chunk = false;
startup_index++;
startup_requirements[space] = 0;
context_requirements[space] = 0;
}
while (!context_reservations[context_index].is_last()) {
single_chunk = false;
context_index++;
UpdateMaxRequirementPerPage(startup_requirements,
startup_snapshot->Reservations());
for (const auto& context_snapshot : *context_snapshots) {
UpdateMaxRequirementPerPage(context_requirements,
context_snapshot->Reservations());
}
uint32_t required = kMaxUInt32;
if (single_chunk) {
// If both the startup snapshot data and the context snapshot data on
// this space fit in a single page, then we consider limiting the size
// of the first page. For this, we add the chunk sizes and some extra
// allowance. This way we achieve a smaller startup memory footprint.
required = (startup_reservations[startup_index].chunk_size() +
2 * context_reservations[context_index].chunk_size()) +
for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
// If the space requirement for a page is less than a page size, we consider
// limiting the size of the first page in order to save memory on startup.
uint32_t required = startup_requirements[space] +
2 * context_requirements[space] +
Page::kObjectStartOffset;
// Add a small allowance to the code space for small scripts.
if (space == CODE_SPACE) required += 32 * KB;
}
if (space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE) {
uint32_t max_size =
MemoryAllocator::PageAreaSize(static_cast<AllocationSpace>(space));
sizes_out[space - FIRST_PAGED_SPACE] = Min(required, max_size);
} else {
DCHECK(single_chunk);
sizes_out[space - FIRST_PAGED_SPACE] = std::min(required, max_size);
}
startup_index++;
context_index++;
}
DCHECK_EQ(startup_reservations.length(), startup_index);
DCHECK_EQ(context_reservations.length(), context_index);
}
v8::StartupData Snapshot::CreateSnapshotBlob(
const StartupSerializer* startup_serializer,
const PartialSerializer* context_serializer) {
SnapshotData startup_snapshot(startup_serializer);
SnapshotData context_snapshot(context_serializer);
Vector<const byte> startup_data = startup_snapshot.RawData();
Vector<const byte> context_data = context_snapshot.RawData();
const SnapshotData* startup_snapshot,
const List<SnapshotData*>* context_snapshots) {
int num_contexts = context_snapshots->length();
int startup_snapshot_offset = StartupSnapshotOffset(num_contexts);
int total_length = startup_snapshot_offset;
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];
CalculateFirstPageSizes(&startup_snapshot, &context_snapshot,
CalculateFirstPageSizes(startup_snapshot, context_snapshots,
first_page_sizes);
int startup_length = startup_data.length();
int context_length = context_data.length();
int context_offset = ContextOffset(startup_length);
int length = context_offset + context_length;
char* data = new char[length];
char* data = new char[total_length];
memcpy(data + kFirstPageSizesOffset, first_page_sizes,
kNumPagedSpaces * kInt32Size);
memcpy(data + kStartupLengthOffset, &startup_length, kInt32Size);
memcpy(data + kStartupDataOffset, startup_data.begin(), startup_length);
memcpy(data + context_offset, context_data.begin(), context_length);
v8::StartupData result = {data, length};
memcpy(data + kNumberOfContextsOffset, &num_contexts, kInt32Size);
int payload_offset = StartupSnapshotOffset(num_contexts);
int payload_length = startup_snapshot->RawData().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) {
PrintF(
"Snapshot blob consists of:\n"
"%10d bytes for startup\n"
"%10d bytes for context\n",
startup_length, context_length);
PrintF("%10d bytes for context #%d\n", payload_length, i);
}
payload_offset += payload_length;
}
v8::StartupData result = {data, total_length};
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) {
DCHECK_LT(kIntSize, data->raw_size);
int startup_length;
memcpy(&startup_length, data->data + kStartupLengthOffset, kInt32Size);
DCHECK_LT(startup_length, data->raw_size);
int num_contexts = ExtractNumContexts(data);
int startup_offset = StartupSnapshotOffset(num_contexts);
CHECK_LT(startup_offset, 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 =
reinterpret_cast<const byte*>(data->data + kStartupDataOffset);
reinterpret_cast<const byte*>(data->data + startup_offset);
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 =
reinterpret_cast<const byte*>(data->data + context_offset);
DCHECK_LT(context_offset, data->raw_size);
int context_length = data->raw_size - context_offset;
int context_length = next_context_offset - context_offset;
return Vector<const byte>(context_data, context_length);
}
......
......@@ -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