Skip to content

Commit

Permalink
[snapshot] Rehash strings after deserialization.
Browse files Browse the repository at this point in the history
See https://goo.gl/6aN8xA

Bug: v8:6593
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: Ic8b0b57195d01d41591397d5d45de3f0f3ebc3d9
Reviewed-on: https://chromium-review.googlesource.com/574527
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46732}
  • Loading branch information
hashseed authored and Commit Bot committed Jul 18, 2017
1 parent 3e19d72 commit a2ab135
Show file tree
Hide file tree
Showing 20 changed files with 277 additions and 30 deletions.
13 changes: 11 additions & 2 deletions src/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,9 @@ StartupData SnapshotCreator::CreateBlob(
isolate->heap()->SetSerializedGlobalProxySizes(*global_proxy_sizes);
}

// We might rehash strings and re-sort descriptors. Clear the lookup cache.
isolate->descriptor_lookup_cache()->Clear();

// 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.
isolate->heap()->CollectAllAvailableGarbage(
Expand Down Expand Up @@ -707,22 +710,28 @@ StartupData SnapshotCreator::CreateBlob(
// Serialize each context with a new partial serializer.
i::List<i::SnapshotData*> context_snapshots(num_additional_contexts + 1);

// TODO(6593): generalize rehashing, and remove this flag.
bool can_be_rehashed = true;

{
// The default snapshot does not support embedder fields.
i::PartialSerializer partial_serializer(
isolate, &startup_serializer, v8::SerializeInternalFieldsCallback());
partial_serializer.Serialize(&default_context, false);
can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed();
context_snapshots.Add(new i::SnapshotData(&partial_serializer));
}

for (int i = 0; i < num_additional_contexts; i++) {
i::PartialSerializer partial_serializer(
isolate, &startup_serializer, data->embedder_fields_serializers_[i]);
partial_serializer.Serialize(&contexts[i], true);
can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed();
context_snapshots.Add(new i::SnapshotData(&partial_serializer));
}

startup_serializer.SerializeWeakReferencesAndDeferred();
can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed();

#ifdef DEBUG
if (i::FLAG_external_reference_stats) {
Expand All @@ -731,8 +740,8 @@ StartupData SnapshotCreator::CreateBlob(
#endif // DEBUG

i::SnapshotData startup_snapshot(&startup_serializer);
StartupData result =
i::Snapshot::CreateSnapshotBlob(&startup_snapshot, &context_snapshots);
StartupData result = i::Snapshot::CreateSnapshotBlob(
&startup_snapshot, &context_snapshots, can_be_rehashed);

// Delete heap-allocated context snapshot instances.
for (const auto& context_snapshot : context_snapshots) {
Expand Down
2 changes: 2 additions & 0 deletions src/bootstrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,8 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorIntrinsic() {
DCHECK(false);
}

JSObject::MigrateSlowToFast(function, 0, "Bootstrapping");

restricted_properties_thrower_ = function;
return function;
}
Expand Down
2 changes: 2 additions & 0 deletions src/flag-definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,8 @@ DEFINE_BOOL(abort_on_stack_overflow, false,
DEFINE_BOOL(randomize_hashes, true,
"randomize hashes to avoid predictable hash collisions "
"(with snapshots this option cannot override the baked-in seed)")
DEFINE_BOOL(rehash_snapshot, true,
"rehash strings from the snapshot to override the baked-in seed")
DEFINE_INT(hash_seed, 0,
"Fixed seed to use to hash property keys (0 means random)"
"(with snapshots this option cannot override the baked-in seed)")
Expand Down
17 changes: 9 additions & 8 deletions src/heap/heap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5680,14 +5680,7 @@ bool Heap::SetUp() {

// Set up the seed that is used to randomize the string hash function.
DCHECK(hash_seed() == 0);
if (FLAG_randomize_hashes) {
if (FLAG_hash_seed == 0) {
int rnd = isolate()->random_number_generator()->NextInt();
set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask));
} else {
set_hash_seed(Smi::FromInt(FLAG_hash_seed));
}
}
if (FLAG_randomize_hashes) InitializeHashSeed();

for (int i = 0; i < static_cast<int>(v8::Isolate::kUseCounterFeatureCount);
i++) {
Expand Down Expand Up @@ -5736,6 +5729,14 @@ bool Heap::SetUp() {
return true;
}

void Heap::InitializeHashSeed() {
if (FLAG_hash_seed == 0) {
int rnd = isolate()->random_number_generator()->NextInt();
set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask));
} else {
set_hash_seed(Smi::FromInt(FLAG_hash_seed));
}
}

bool Heap::CreateHeapObjects() {
// Create initial maps.
Expand Down
3 changes: 3 additions & 0 deletions src/heap/heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,9 @@ class Heap {
// without actually creating any objects.
bool SetUp();

// (Re-)Initialize hash seed from flag or RNG.
void InitializeHashSeed();

// Bootstraps the object heap with the core set of objects required to run.
// Returns whether it succeeded.
bool CreateHeapObjects();
Expand Down
2 changes: 2 additions & 0 deletions src/js/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,8 @@ var unscopables = {
keys: true,
};

%ToFastProperties(unscopables);

%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
DONT_ENUM | READ_ONLY);

Expand Down
2 changes: 2 additions & 0 deletions src/objects.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16612,6 +16612,8 @@ BaseNameDictionary<GlobalDictionary, GlobalDictionaryShape>::Add(
Handle<GlobalDictionary>, Handle<Name>, Handle<Object>, PropertyDetails,
int*);

template void HashTable<GlobalDictionary, GlobalDictionaryShape>::Rehash();

template Handle<SeededNumberDictionary>
Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape>::Add(
Handle<SeededNumberDictionary>, uint32_t, Handle<Object>, PropertyDetails,
Expand Down
39 changes: 39 additions & 0 deletions src/snapshot/deserializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ void Deserializer::Deserialize(Isolate* isolate) {
// Needs to be called after the builtins are marked as initialized, in order
// to display the builtin names.
PrintDisassembledCodeObjects();

if (FLAG_rehash_snapshot && can_rehash_) Rehash();
}

MaybeHandle<Object> Deserializer::DeserializePartial(
Expand Down Expand Up @@ -157,6 +159,9 @@ MaybeHandle<Object> Deserializer::DeserializePartial(
// changed and logging should be added to notify the profiler et al of the
// new code, which also has to be flushed from instruction cache.
CHECK_EQ(start_address, code_space->top());

if (FLAG_rehash_snapshot && can_rehash_) RehashContext(Context::cast(root));

return Handle<Object>(root, isolate);
}

Expand All @@ -183,6 +188,30 @@ MaybeHandle<HeapObject> Deserializer::DeserializeObject(Isolate* isolate) {
}
}

void Deserializer::Rehash() {
DCHECK(can_rehash_);
isolate_->heap()->InitializeHashSeed();
isolate_->heap()->string_table()->Rehash();
isolate_->heap()->weak_object_to_code_table()->Rehash();
SortMapDescriptors();
}

void Deserializer::RehashContext(Context* context) {
DCHECK(can_rehash_);
for (const auto& array : transition_arrays_) array->Sort();
context->global_object()->global_dictionary()->Rehash();
SortMapDescriptors();
}

void Deserializer::SortMapDescriptors() {
for (const auto& address : allocated_maps_) {
Map* map = Map::cast(HeapObject::FromAddress(address));
if (map->instance_descriptors()->number_of_descriptors() > 1) {
map->instance_descriptors()->Sort();
}
}
}

Deserializer::~Deserializer() {
#ifdef DEBUG
// Do not perform checks if we aborted deserialization.
Expand Down Expand Up @@ -371,6 +400,16 @@ HeapObject* Deserializer::PostProcessNewObject(HeapObject* obj, int space) {
string->resource()));
isolate_->heap()->RegisterExternalString(string);
}
if (FLAG_rehash_snapshot && can_rehash_ && !deserializing_user_code()) {
if (obj->IsString()) {
// Uninitialize hash field as we are going to reinitialize the hash seed.
String* string = String::cast(obj);
string->set_hash_field(String::kEmptyHashField);
} else if (obj->IsTransitionArray() &&
TransitionArray::cast(obj)->number_of_entries() > 1) {
transition_arrays_.Add(TransitionArray::cast(obj));
}
}
// Check alignment.
DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment()));
return obj;
Expand Down
18 changes: 17 additions & 1 deletion src/snapshot/deserializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class Deserializer : public SerializerDeserializer {
external_reference_table_(NULL),
deserialized_large_objects_(0),
deserializing_user_code_(deserializing_user_code),
next_alignment_(kWordAligned) {
next_alignment_(kWordAligned),
can_rehash_(false) {
DecodeReservation(data->Reservations());
}

Expand All @@ -62,6 +63,8 @@ class Deserializer : public SerializerDeserializer {
attached_objects_.Add(attached_object);
}

void SetRehashability(bool v) { can_rehash_ = v; }

private:
void VisitRootPointers(Root root, Object** start, Object** end) override;

Expand Down Expand Up @@ -115,6 +118,15 @@ class Deserializer : public SerializerDeserializer {
// snapshot by chunk index and offset.
HeapObject* GetBackReferencedObject(int space);

// Rehash after deserializing an isolate.
void Rehash();

// Rehash after deserializing a context.
void RehashContext(Context* context);

// Sort descriptors of deserialized maps using new string hashes.
void SortMapDescriptors();

// Cached current isolate.
Isolate* isolate_;

Expand Down Expand Up @@ -142,11 +154,15 @@ class Deserializer : public SerializerDeserializer {
List<AccessorInfo*> accessor_infos_;
List<Handle<String> > new_internalized_strings_;
List<Handle<Script> > new_scripts_;
List<TransitionArray*> transition_arrays_;

bool deserializing_user_code_;

AllocationAlignment next_alignment_;

// TODO(6593): generalize rehashing, and remove this flag.
bool can_rehash_;

DISALLOW_COPY_AND_ASSIGN(Deserializer);
};

Expand Down
37 changes: 27 additions & 10 deletions src/snapshot/partial-serializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ PartialSerializer::PartialSerializer(
v8::SerializeEmbedderFieldsCallback callback)
: Serializer(isolate),
startup_serializer_(startup_serializer),
serialize_embedder_fields_(callback) {
serialize_embedder_fields_(callback),
rehashable_global_dictionary_(nullptr),
can_be_rehashed_(true) {
InitializeCodeAddressMap();
}

Expand All @@ -24,22 +26,26 @@ PartialSerializer::~PartialSerializer() {
}

void PartialSerializer::Serialize(Object** o, bool include_global_proxy) {
if ((*o)->IsContext()) {
if ((*o)->IsNativeContext()) {
Context* context = Context::cast(*o);
reference_map()->AddAttachedReference(context->global_proxy());
// The bootstrap snapshot has a code-stub context. When serializing the
// partial snapshot, it is chained into the weak context list on the isolate
// and it's next context pointer may point to the code-stub context. Clear
// it before serializing, it will get re-added to the context list
// explicitly when it's loaded.
if (context->IsNativeContext()) {
context->set(Context::NEXT_CONTEXT_LINK,
isolate_->heap()->undefined_value());
DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
// Reset math random cache to get fresh random numbers.
context->set_math_random_index(Smi::kZero);
context->set_math_random_cache(isolate_->heap()->undefined_value());
}
context->set(Context::NEXT_CONTEXT_LINK,
isolate_->heap()->undefined_value());
DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
// Reset math random cache to get fresh random numbers.
context->set_math_random_index(Smi::kZero);
context->set_math_random_cache(isolate_->heap()->undefined_value());
DCHECK_NULL(rehashable_global_dictionary_);
rehashable_global_dictionary_ =
context->global_object()->global_dictionary();
} else {
// We only do rehashing for native contexts.
can_be_rehashed_ = false;
}
VisitRootPointer(Root::kPartialSnapshotCache, o);
SerializeDeferredObjects();
Expand Down Expand Up @@ -104,6 +110,8 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
}
}

if (obj->IsHashTable()) CheckRehashability(obj);

// Object has not yet been serialized. Serialize it here.
ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point);
serializer.Serialize();
Expand Down Expand Up @@ -152,5 +160,14 @@ void PartialSerializer::SerializeEmbedderFields() {
sink_.Put(kSynchronize, "Finished with embedder fields data");
}

void PartialSerializer::CheckRehashability(HeapObject* table) {
DCHECK(table->IsHashTable());
if (!can_be_rehashed_) return;
// We can only correctly rehash if the global dictionary is the only hash
// table that we deserialize.
if (table == rehashable_global_dictionary_) return;
can_be_rehashed_ = false;
}

} // namespace internal
} // namespace v8
8 changes: 8 additions & 0 deletions src/snapshot/partial-serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class PartialSerializer : public Serializer {
// Serialize the objects reachable from a single object pointer.
void Serialize(Object** o, bool include_global_proxy);

bool can_be_rehashed() const { return can_be_rehashed_; }

private:
void SerializeObject(HeapObject* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) override;
Expand All @@ -31,9 +33,15 @@ class PartialSerializer : public Serializer {

void SerializeEmbedderFields();

void CheckRehashability(HeapObject* table);

StartupSerializer* startup_serializer_;
List<JSObject*> embedder_field_holders_;
v8::SerializeEmbedderFieldsCallback serialize_embedder_fields_;
GlobalDictionary* rehashable_global_dictionary_;
// Indicates whether we only serialized hash tables that we can rehash.
// TODO(yangguo): generalize rehashing, and remove this flag.
bool can_be_rehashed_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
};

Expand Down
13 changes: 12 additions & 1 deletion src/snapshot/snapshot-common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ bool Snapshot::Initialize(Isolate* isolate) {
Vector<const byte> startup_data = ExtractStartupData(blob);
SnapshotData snapshot_data(startup_data);
Deserializer deserializer(&snapshot_data);
deserializer.SetRehashability(ExtractRehashability(blob));
bool success = isolate->Init(&deserializer);
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
Expand All @@ -62,6 +63,7 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
ExtractContextData(blob, static_cast<int>(context_index));
SnapshotData snapshot_data(context_data);
Deserializer deserializer(&snapshot_data);
deserializer.SetRehashability(ExtractRehashability(blob));

MaybeHandle<Object> maybe_context = deserializer.DeserializePartial(
isolate, global_proxy, embedder_fields_deserializer);
Expand Down Expand Up @@ -98,7 +100,7 @@ void ProfileDeserialization(const SnapshotData* startup_snapshot,

v8::StartupData Snapshot::CreateSnapshotBlob(
const SnapshotData* startup_snapshot,
const List<SnapshotData*>* context_snapshots) {
const List<SnapshotData*>* context_snapshots, bool can_be_rehashed) {
int num_contexts = context_snapshots->length();
int startup_snapshot_offset = StartupSnapshotOffset(num_contexts);
int total_length = startup_snapshot_offset;
Expand All @@ -111,6 +113,8 @@ v8::StartupData Snapshot::CreateSnapshotBlob(

char* data = new char[total_length];
memcpy(data + kNumberOfContextsOffset, &num_contexts, kInt32Size);
int rehashability = can_be_rehashed ? 1 : 0;
memcpy(data + kRehashabilityOffset, &rehashability, kInt32Size);
int payload_offset = StartupSnapshotOffset(num_contexts);
int payload_length = startup_snapshot->RawData().length();
memcpy(data + payload_offset, startup_snapshot->RawData().start(),
Expand Down Expand Up @@ -143,6 +147,13 @@ int Snapshot::ExtractNumContexts(const v8::StartupData* data) {
return num_contexts;
}

bool Snapshot::ExtractRehashability(const v8::StartupData* data) {
CHECK_LT(kRehashabilityOffset, data->raw_size);
int rehashability;
memcpy(&rehashability, data->data + kRehashabilityOffset, kInt32Size);
return rehashability != 0;
}

Vector<const byte> Snapshot::ExtractStartupData(const v8::StartupData* data) {
int num_contexts = ExtractNumContexts(data);
int startup_offset = StartupSnapshotOffset(num_contexts);
Expand Down
Loading

0 comments on commit a2ab135

Please sign in to comment.