// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SERIALIZER_H_
#define V8_SNAPSHOT_SERIALIZER_H_
#include "src/isolate.h"
#include "src/log.h"
#include "src/objects.h"
#include "src/snapshot/serializer-common.h"
#include "src/snapshot/snapshot-source-sink.h"
namespace v8 {
namespace internal {
class CodeAddressMap : public CodeEventLogger {
public:
explicit CodeAddressMap(Isolate* isolate) : isolate_(isolate) {
isolate->logger()->addCodeEventListener(this);
}
~CodeAddressMap() override {
isolate_->logger()->removeCodeEventListener(this);
}
void CodeMoveEvent(AbstractCode* from, Address to) override {
address_to_name_map_.Move(from->address(), to);
}
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override {}
const char* Lookup(Address address) {
return address_to_name_map_.Lookup(address);
}
private:
class NameMap {
public:
NameMap() : impl_() {}
~NameMap() {
for (base::HashMap::Entry* p = impl_.Start(); p != NULL;
p = impl_.Next(p)) {
DeleteArray(static_cast<const char*>(p->value));
}
}
void Insert(Address code_address, const char* name, int name_size) {
base::HashMap::Entry* entry = FindOrCreateEntry(code_address);
if (entry->value == NULL) {
entry->value = CopyName(name, name_size);
}
}
const char* Lookup(Address code_address) {
base::HashMap::Entry* entry = FindEntry(code_address);
return (entry != NULL) ? static_cast<const char*>(entry->value) : NULL;
}
void Remove(Address code_address) {
base::HashMap::Entry* entry = FindEntry(code_address);
if (entry != NULL) {
DeleteArray(static_cast<char*>(entry->value));
RemoveEntry(entry);
}
}
void Move(Address from, Address to) {
if (from == to) return;
base::HashMap::Entry* from_entry = FindEntry(from);
DCHECK(from_entry != NULL);
void* value = from_entry->value;
RemoveEntry(from_entry);
base::HashMap::Entry* to_entry = FindOrCreateEntry(to);
DCHECK(to_entry->value == NULL);
to_entry->value = value;
}
private:
static char* CopyName(const char* name, int name_size) {
char* result = NewArray<char>(name_size + 1);
for (int i = 0; i < name_size; ++i) {
char c = name[i];
if (c == '\0') c = ' ';
result[i] = c;
}
result[name_size] = '\0';
return result;
}
base::HashMap::Entry* FindOrCreateEntry(Address code_address) {
return impl_.LookupOrInsert(code_address,
ComputePointerHash(code_address));
}
base::HashMap::Entry* FindEntry(Address code_address) {
return impl_.Lookup(code_address, ComputePointerHash(code_address));
}
void RemoveEntry(base::HashMap::Entry* entry) {
impl_.Remove(entry->key, entry->hash);
}
base::HashMap impl_;
DISALLOW_COPY_AND_ASSIGN(NameMap);
};
void LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo*,
const char* name, int length) override {
address_to_name_map_.Insert(code->address(), name, length);
}
NameMap address_to_name_map_;
Isolate* isolate_;
};
// There can be only one serializer per V8 process.
class Serializer : public SerializerDeserializer {
public:
explicit Serializer(Isolate* isolate);
~Serializer() override;
void EncodeReservations(List<SerializedData::Reservation>* out) const;
void SerializeDeferredObjects();
Isolate* isolate() const { return isolate_; }
SerializerReferenceMap* reference_map() { return &reference_map_; }
RootIndexMap* root_index_map() { return &root_index_map_; }
#ifdef OBJECT_PRINT
void CountInstanceType(Map* map, int size);
#endif // OBJECT_PRINT
protected:
class ObjectSerializer;
class RecursionScope {
public:
explicit RecursionScope(Serializer* serializer) : serializer_(serializer) {
serializer_->recursion_depth_++;
}
~RecursionScope() { serializer_->recursion_depth_--; }
bool ExceedsMaximum() {
return serializer_->recursion_depth_ >= kMaxRecursionDepth;
}
private:
static const int kMaxRecursionDepth = 32;
Serializer* serializer_;
};
virtual void SerializeObject(HeapObject* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) = 0;
void VisitPointers(Object** start, Object** end) override;
void PutRoot(int index, HeapObject* object, HowToCode how, WhereToPoint where,
int skip);
void PutSmi(Smi* smi);
void PutBackReference(HeapObject* object, SerializerReference reference);
void PutAttachedReference(SerializerReference reference,
HowToCode how_to_code, WhereToPoint where_to_point);
// Emit alignment prefix if necessary, return required padding space in bytes.
int PutAlignmentPrefix(HeapObject* object);
// Returns true if the object was successfully serialized as hot object.
bool SerializeHotObject(HeapObject* obj, HowToCode how_to_code,
WhereToPoint where_to_point, int skip);
// Returns true if the object was successfully serialized as back reference.
bool SerializeBackReference(HeapObject* obj, HowToCode how_to_code,
WhereToPoint where_to_point, int skip);
inline void FlushSkip(int skip) {
if (skip != 0) {
sink_.Put(kSkip, "SkipFromSerializeObject");
sink_.PutInt(skip, "SkipDistanceFromSerializeObject");
}
}
bool BackReferenceIsAlreadyAllocated(SerializerReference back_reference);
// This will return the space for an object.
SerializerReference AllocateLargeObject(int size);
SerializerReference AllocateMap();
SerializerReference Allocate(AllocationSpace space, int size);
int EncodeExternalReference(Address addr) {
return external_reference_encoder_.Encode(addr);
}
bool HasNotExceededFirstPageOfEachSpace();
// GetInt reads 4 bytes at once, requiring padding at the end.
void Pad();
// We may not need the code address map for logging for every instance
// of the serializer. Initialize it on demand.
void InitializeCodeAddressMap();
Code* CopyCode(Code* code);
inline uint32_t max_chunk_size(int space) const {
DCHECK_LE(0, space);
DCHECK_LT(space, kNumberOfSpaces);
return max_chunk_size_[space];
}
const SnapshotByteSink* sink() const { return &sink_; }
void QueueDeferredObject(HeapObject* obj) {
DCHECK(reference_map_.Lookup(obj).is_back_reference());
deferred_objects_.Add(obj);
}
void OutputStatistics(const char* name);
Isolate* isolate_;
SnapshotByteSink sink_;
ExternalReferenceEncoder external_reference_encoder_;
SerializerReferenceMap reference_map_;
RootIndexMap root_index_map_;
int recursion_depth_;
friend class Deserializer;
friend class ObjectSerializer;
friend class RecursionScope;
friend class SnapshotData;
private:
CodeAddressMap* code_address_map_;
// Objects from the same space are put into chunks for bulk-allocation
// when deserializing. We have to make sure that each chunk fits into a
// page. So we track the chunk size in pending_chunk_ of a space, but
// when it exceeds a page, we complete the current chunk and start a new one.
uint32_t pending_chunk_[kNumberOfPreallocatedSpaces];
List<uint32_t> completed_chunks_[kNumberOfPreallocatedSpaces];
uint32_t max_chunk_size_[kNumberOfPreallocatedSpaces];
// Number of maps that we need to allocate.
uint32_t num_maps_;
// We map serialized large objects to indexes for back-referencing.
uint32_t large_objects_total_size_;
uint32_t seen_large_objects_index_;
List<byte> code_buffer_;
// To handle stack overflow.
List<HeapObject*> deferred_objects_;
#ifdef OBJECT_PRINT
static const int kInstanceTypes = 256;
int* instance_type_count_;
size_t* instance_type_size_;
#endif // OBJECT_PRINT
DISALLOW_COPY_AND_ASSIGN(Serializer);
};
class Serializer::ObjectSerializer : public ObjectVisitor {
public:
ObjectSerializer(Serializer* serializer, HeapObject* obj,
SnapshotByteSink* sink, HowToCode how_to_code,
WhereToPoint where_to_point)
: serializer_(serializer),
object_(obj),
sink_(sink),
reference_representation_(how_to_code + where_to_point),
bytes_processed_so_far_(0),
code_has_been_output_(false) {}
~ObjectSerializer() override {}
void Serialize();
void SerializeDeferred();
void VisitPointers(Object** start, Object** end) override;
void VisitEmbeddedPointer(RelocInfo* target) override;
void VisitExternalReference(Address* p) override;
void VisitExternalReference(RelocInfo* rinfo) override;
void VisitInternalReference(RelocInfo* rinfo) override;
void VisitCodeTarget(RelocInfo* target) override;
void VisitCodeEntry(Address entry_address) override;
void VisitCell(RelocInfo* rinfo) override;
void VisitRuntimeEntry(RelocInfo* reloc) override;
// Used for seralizing the external strings that hold the natives source.
void VisitExternalOneByteString(
v8::String::ExternalOneByteStringResource** resource) override;
// We can't serialize a heap with external two byte strings.
void VisitExternalTwoByteString(
v8::String::ExternalStringResource** resource) override {
UNREACHABLE();
}
private:
void SerializePrologue(AllocationSpace space, int size, Map* map);
bool SerializeExternalNativeSourceString(
int builtin_count,
v8::String::ExternalOneByteStringResource** resource_pointer,
FixedArray* source_cache, int resource_index);
enum ReturnSkip { kCanReturnSkipInsteadOfSkipping, kIgnoringReturn };
// This function outputs or skips the raw data between the last pointer and
// up to the current position. It optionally can just return the number of
// bytes to skip instead of performing a skip instruction, in case the skip
// can be merged into the next instruction.
int OutputRawData(Address up_to, ReturnSkip return_skip = kIgnoringReturn);
// External strings are serialized in a way to resemble sequential strings.
void SerializeExternalString();
Address PrepareCode();
Serializer* serializer_;
HeapObject* object_;
SnapshotByteSink* sink_;
int reference_representation_;
int bytes_processed_so_far_;
bool code_has_been_output_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SERIALIZER_H_