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
}
+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);
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);
}
+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;
}
}
+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);
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();
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
}
+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;
sink_->Put(kRawData, "RawData");
sink_->PutInt(bytes_to_output, "length");
}
+
+ // 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++) {
- unsigned int data = base[i];
- sink_->PutSection(data, "Byte");
+ sink_->PutSection(object_start[base + i], description);
}
+ if (code_object_) delete[] object_start;
}
if (to_skip != 0 && return_skip == kIgnoringReturn) {
sink_->Put(kSkip, "Skip");
}
+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);