Make snapshots reproducible.
To keep the structure of the serializer more or less untouched, we use some ingenious Corry-approved(TM) 3-step technology (a.k.a. "hack"): * Create copies of code objects. * Wipe out all absolute addresses in these copies. * Write out the cleaned copies instead of the originals. In conjunction with --random-seed, our snapshots are reproducible now. BUG=v8:2885 R=bmeurer@chromium.org, erik.corry@gmail.com Review URL: https://codereview.chromium.org/54823002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17473 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
82b8e23bc1
commit
dc8c314084
@ -258,6 +258,15 @@ Object** RelocInfo::call_object_address() {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
ASSERT(IsEmbeddedObject(rmode_) ||
|
||||
IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) ||
|
||||
IsExternalReference(rmode_));
|
||||
Assembler::set_target_pointer_at(pc_, NULL);
|
||||
}
|
||||
|
||||
|
||||
bool RelocInfo::IsPatchedReturnSequence() {
|
||||
Instr current_instr = Assembler::instr_at(pc_);
|
||||
Instr next_instr = Assembler::instr_at(pc_ + Assembler::kInstrSize);
|
||||
|
@ -435,6 +435,10 @@ class RelocInfo BASE_EMBEDDED {
|
||||
INLINE(void set_call_object(Object* target));
|
||||
INLINE(Object** call_object_address());
|
||||
|
||||
// Wipe out a relocation to a fixed value, used for making snapshots
|
||||
// reproducible.
|
||||
INLINE(void WipeOut());
|
||||
|
||||
template<typename StaticVisitor> inline void Visit(Heap* heap);
|
||||
inline void Visit(Isolate* isolate, ObjectVisitor* v);
|
||||
|
||||
|
@ -243,6 +243,18 @@ Object** RelocInfo::call_object_address() {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_)) {
|
||||
Memory::Address_at(pc_) = NULL;
|
||||
} else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
|
||||
// Effectively write zero into the relocation.
|
||||
Assembler::set_target_address_at(pc_, pc_ + sizeof(int32_t));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RelocInfo::IsPatchedReturnSequence() {
|
||||
return *pc_ == kCallOpcode;
|
||||
}
|
||||
|
@ -315,6 +315,15 @@ void RelocInfo::set_call_object(Object* target) {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
ASSERT(IsEmbeddedObject(rmode_) ||
|
||||
IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) ||
|
||||
IsExternalReference(rmode_));
|
||||
Assembler::set_target_address_at(pc_, NULL);
|
||||
}
|
||||
|
||||
|
||||
bool RelocInfo::IsPatchedReturnSequence() {
|
||||
Instr instr0 = Assembler::instr_at(pc_);
|
||||
Instr instr1 = Assembler::instr_at(pc_ + 1 * Assembler::kInstrSize);
|
||||
|
@ -5323,6 +5323,17 @@ ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset)
|
||||
ACCESSORS(Code, raw_type_feedback_info, Object, kTypeFeedbackInfoOffset)
|
||||
|
||||
|
||||
void Code::WipeOutHeader() {
|
||||
WRITE_FIELD(this, kRelocationInfoOffset, NULL);
|
||||
WRITE_FIELD(this, kHandlerTableOffset, NULL);
|
||||
WRITE_FIELD(this, kDeoptimizationDataOffset, NULL);
|
||||
// Do not wipe out e.g. a minor key.
|
||||
if (!READ_FIELD(this, kTypeFeedbackInfoOffset)->IsSmi()) {
|
||||
WRITE_FIELD(this, kTypeFeedbackInfoOffset, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Object* Code::type_feedback_info() {
|
||||
ASSERT(kind() == FUNCTION);
|
||||
return raw_type_feedback_info();
|
||||
|
@ -5198,6 +5198,15 @@ class Code: public HeapObject {
|
||||
|
||||
void ReplaceNthCell(int n, Cell* replace_with);
|
||||
|
||||
// The entire code object including its header is copied verbatim to the
|
||||
// snapshot so that it can be written in one, fast, memcpy during
|
||||
// deserialization. The deserializer will overwrite some pointers, rather
|
||||
// like a runtime linker, but the random allocation addresses used in the
|
||||
// mksnapshot process would still be present in the unlinked snapshot data,
|
||||
// which would make snapshot production non-reproducible. This method wipes
|
||||
// out the to-be-overwritten header data for reproducible snapshots.
|
||||
inline void WipeOutHeader();
|
||||
|
||||
class ExtraICStateStrictMode: public BitField<StrictModeFlag, 0, 1> {};
|
||||
class ExtraICStateKeyedAccessStoreMode:
|
||||
public BitField<KeyedAccessStoreMode, 1, 4> {}; // NOLINT
|
||||
|
@ -1776,10 +1776,29 @@ void Serializer::ObjectSerializer::VisitExternalAsciiString(
|
||||
}
|
||||
|
||||
|
||||
static Code* CloneCodeObject(HeapObject* code) {
|
||||
Address copy = new byte[code->Size()];
|
||||
OS::MemCopy(copy, code->address(), code->Size());
|
||||
return Code::cast(HeapObject::FromAddress(copy));
|
||||
}
|
||||
|
||||
|
||||
static void WipeOutRelocations(Code* code) {
|
||||
int mode_mask =
|
||||
RelocInfo::kCodeTargetMask |
|
||||
RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
|
||||
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
|
||||
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
|
||||
for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
|
||||
it.rinfo()->WipeOut();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Serializer::ObjectSerializer::OutputRawData(
|
||||
Address up_to, Serializer::ObjectSerializer::ReturnSkip return_skip) {
|
||||
Address object_start = object_->address();
|
||||
Address base = object_start + bytes_processed_so_far_;
|
||||
int base = bytes_processed_so_far_;
|
||||
int up_to_offset = static_cast<int>(up_to - object_start);
|
||||
int to_skip = up_to_offset - bytes_processed_so_far_;
|
||||
int bytes_to_output = to_skip;
|
||||
@ -1809,10 +1828,22 @@ int Serializer::ObjectSerializer::OutputRawData(
|
||||
sink_->Put(kRawData, "RawData");
|
||||
sink_->PutInt(bytes_to_output, "length");
|
||||
}
|
||||
for (int i = 0; i < bytes_to_output; i++) {
|
||||
unsigned int data = base[i];
|
||||
sink_->PutSection(data, "Byte");
|
||||
|
||||
// To make snapshots reproducible, we need to wipe out all pointers in code.
|
||||
if (code_object_) {
|
||||
Code* code = CloneCodeObject(object_);
|
||||
WipeOutRelocations(code);
|
||||
// We need to wipe out the header fields *after* wiping out the
|
||||
// relocations, because some of these fields are needed for the latter.
|
||||
code->WipeOutHeader();
|
||||
object_start = code->address();
|
||||
}
|
||||
|
||||
const char* description = code_object_ ? "Code" : "Byte";
|
||||
for (int i = 0; i < bytes_to_output; i++) {
|
||||
sink_->PutSection(object_start[base + i], description);
|
||||
}
|
||||
if (code_object_) delete[] object_start;
|
||||
}
|
||||
if (to_skip != 0 && return_skip == kIgnoringReturn) {
|
||||
sink_->Put(kSkip, "Skip");
|
||||
|
@ -364,6 +364,18 @@ void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_)) {
|
||||
Memory::Address_at(pc_) = NULL;
|
||||
} else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
|
||||
// Effectively write zero into the relocation.
|
||||
Assembler::set_target_address_at(pc_, pc_ + sizeof(int32_t));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RelocInfo::IsPatchedReturnSequence() {
|
||||
// The recognized call sequence is:
|
||||
// movq(kScratchRegister, address); call(kScratchRegister);
|
||||
|
Loading…
Reference in New Issue
Block a user