• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/snapshot/code-serializer.h"
6 
7 #include "src/base/platform/platform.h"
8 #include "src/codegen/macro-assembler.h"
9 #include "src/common/globals.h"
10 #include "src/debug/debug.h"
11 #include "src/heap/heap-inl.h"
12 #include "src/heap/local-factory-inl.h"
13 #include "src/logging/counters.h"
14 #include "src/logging/log.h"
15 #include "src/objects/objects-inl.h"
16 #include "src/objects/slots.h"
17 #include "src/objects/visitors.h"
18 #include "src/snapshot/object-deserializer.h"
19 #include "src/snapshot/snapshot-utils.h"
20 #include "src/snapshot/snapshot.h"
21 #include "src/utils/version.h"
22 
23 namespace v8 {
24 namespace internal {
25 
ScriptData(const byte * data,int length)26 ScriptData::ScriptData(const byte* data, int length)
27     : owns_data_(false), rejected_(false), data_(data), length_(length) {
28   if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) {
29     byte* copy = NewArray<byte>(length);
30     DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment));
31     CopyBytes(copy, data, length);
32     data_ = copy;
33     AcquireDataOwnership();
34   }
35 }
36 
CodeSerializer(Isolate * isolate,uint32_t source_hash)37 CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash)
38     : Serializer(isolate, Snapshot::kDefaultSerializerFlags),
39       source_hash_(source_hash) {}
40 
41 // static
Serialize(Handle<SharedFunctionInfo> info)42 ScriptCompiler::CachedData* CodeSerializer::Serialize(
43     Handle<SharedFunctionInfo> info) {
44   Isolate* isolate = info->GetIsolate();
45   TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.Execute");
46   HistogramTimerScope histogram_timer(isolate->counters()->compile_serialize());
47   RuntimeCallTimerScope runtimeTimer(isolate,
48                                      RuntimeCallCounterId::kCompileSerialize);
49   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileSerialize");
50 
51   base::ElapsedTimer timer;
52   if (FLAG_profile_deserialization) timer.Start();
53   Handle<Script> script(Script::cast(info->script()), isolate);
54   if (FLAG_trace_serializer) {
55     PrintF("[Serializing from");
56     script->name().ShortPrint();
57     PrintF("]\n");
58   }
59   // TODO(7110): Enable serialization of Asm modules once the AsmWasmData is
60   // context independent.
61   if (script->ContainsAsmModule()) return nullptr;
62 
63   // Serialize code object.
64   Handle<String> source(String::cast(script->source()), isolate);
65   HandleScope scope(isolate);
66   CodeSerializer cs(isolate, SerializedCodeData::SourceHash(
67                                  source, script->origin_options()));
68   DisallowGarbageCollection no_gc;
69   cs.reference_map()->AddAttachedReference(*source);
70   ScriptData* script_data = cs.SerializeSharedFunctionInfo(info);
71 
72   if (FLAG_profile_deserialization) {
73     double ms = timer.Elapsed().InMillisecondsF();
74     int length = script_data->length();
75     PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
76   }
77 
78   ScriptCompiler::CachedData* result =
79       new ScriptCompiler::CachedData(script_data->data(), script_data->length(),
80                                      ScriptCompiler::CachedData::BufferOwned);
81   script_data->ReleaseDataOwnership();
82   delete script_data;
83 
84   return result;
85 }
86 
SerializeSharedFunctionInfo(Handle<SharedFunctionInfo> info)87 ScriptData* CodeSerializer::SerializeSharedFunctionInfo(
88     Handle<SharedFunctionInfo> info) {
89   DisallowGarbageCollection no_gc;
90 
91   VisitRootPointer(Root::kHandleScope, nullptr,
92                    FullObjectSlot(info.location()));
93   SerializeDeferredObjects();
94   Pad();
95 
96   SerializedCodeData data(sink_.data(), this);
97 
98   return data.GetScriptData();
99 }
100 
SerializeReadOnlyObject(Handle<HeapObject> obj)101 bool CodeSerializer::SerializeReadOnlyObject(Handle<HeapObject> obj) {
102   if (!ReadOnlyHeap::Contains(*obj)) return false;
103 
104   // For objects on the read-only heap, never serialize the object, but instead
105   // create a back reference that encodes the page number as the chunk_index and
106   // the offset within the page as the chunk_offset.
107   Address address = obj->address();
108   BasicMemoryChunk* chunk = BasicMemoryChunk::FromAddress(address);
109   uint32_t chunk_index = 0;
110   ReadOnlySpace* const read_only_space = isolate()->heap()->read_only_space();
111   for (ReadOnlyPage* page : read_only_space->pages()) {
112     if (chunk == page) break;
113     ++chunk_index;
114   }
115   uint32_t chunk_offset = static_cast<uint32_t>(chunk->Offset(address));
116   sink_.Put(kReadOnlyHeapRef, "ReadOnlyHeapRef");
117   sink_.PutInt(chunk_index, "ReadOnlyHeapRefChunkIndex");
118   sink_.PutInt(chunk_offset, "ReadOnlyHeapRefChunkOffset");
119   return true;
120 }
121 
SerializeObjectImpl(Handle<HeapObject> obj)122 void CodeSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
123   if (SerializeHotObject(obj)) return;
124 
125   if (SerializeRoot(obj)) return;
126 
127   if (SerializeBackReference(obj)) return;
128 
129   if (SerializeReadOnlyObject(obj)) return;
130 
131   CHECK(!obj->IsCode());
132 
133   ReadOnlyRoots roots(isolate());
134   if (ElideObject(*obj)) {
135     return SerializeObject(roots.undefined_value_handle());
136   }
137 
138   if (obj->IsScript()) {
139     Handle<Script> script_obj = Handle<Script>::cast(obj);
140     DCHECK_NE(script_obj->compilation_type(), Script::COMPILATION_TYPE_EVAL);
141     // We want to differentiate between undefined and uninitialized_symbol for
142     // context_data for now. It is hack to allow debugging for scripts that are
143     // included as a part of custom snapshot. (see debug::Script::IsEmbedded())
144     Object context_data = script_obj->context_data();
145     if (context_data != roots.undefined_value() &&
146         context_data != roots.uninitialized_symbol()) {
147       script_obj->set_context_data(roots.undefined_value());
148     }
149     // We don't want to serialize host options to avoid serializing unnecessary
150     // object graph.
151     FixedArray host_options = script_obj->host_defined_options();
152     script_obj->set_host_defined_options(roots.empty_fixed_array());
153     SerializeGeneric(obj);
154     script_obj->set_host_defined_options(host_options);
155     script_obj->set_context_data(context_data);
156     return;
157   }
158 
159   if (obj->IsSharedFunctionInfo()) {
160     Handle<SharedFunctionInfo> sfi = Handle<SharedFunctionInfo>::cast(obj);
161     // TODO(7110): Enable serializing of Asm modules once the AsmWasmData
162     // is context independent.
163     DCHECK(!sfi->IsApiFunction() && !sfi->HasAsmWasmData());
164 
165     DebugInfo debug_info;
166     BytecodeArray debug_bytecode_array;
167     if (sfi->HasDebugInfo()) {
168       // Clear debug info.
169       debug_info = sfi->GetDebugInfo();
170       if (debug_info.HasInstrumentedBytecodeArray()) {
171         debug_bytecode_array = debug_info.DebugBytecodeArray();
172         sfi->SetDebugBytecodeArray(debug_info.OriginalBytecodeArray());
173       }
174       sfi->set_script_or_debug_info(debug_info.script(), kReleaseStore);
175     }
176     DCHECK(!sfi->HasDebugInfo());
177 
178     SerializeGeneric(obj);
179 
180     // Restore debug info
181     if (!debug_info.is_null()) {
182       sfi->set_script_or_debug_info(debug_info, kReleaseStore);
183       if (!debug_bytecode_array.is_null()) {
184         sfi->SetDebugBytecodeArray(debug_bytecode_array);
185       }
186     }
187     return;
188   }
189 
190   // NOTE(mmarchini): If we try to serialize an InterpreterData our process
191   // will crash since it stores a code object. Instead, we serialize the
192   // bytecode array stored within the InterpreterData, which is the important
193   // information. On deserialization we'll create our code objects again, if
194   // --interpreted-frames-native-stack is on. See v8:9122 for more context
195 #ifndef V8_TARGET_ARCH_ARM
196   if (V8_UNLIKELY(FLAG_interpreted_frames_native_stack) &&
197       obj->IsInterpreterData()) {
198     obj = handle(InterpreterData::cast(*obj).bytecode_array(), isolate());
199   }
200 #endif  // V8_TARGET_ARCH_ARM
201 
202   // Past this point we should not see any (context-specific) maps anymore.
203   CHECK(!obj->IsMap());
204   // There should be no references to the global object embedded.
205   CHECK(!obj->IsJSGlobalProxy() && !obj->IsJSGlobalObject());
206   // Embedded FixedArrays that need rehashing must support rehashing.
207   CHECK_IMPLIES(obj->NeedsRehashing(), obj->CanBeRehashed());
208   // We expect no instantiated function objects or contexts.
209   CHECK(!obj->IsJSFunction() && !obj->IsContext());
210 
211   SerializeGeneric(obj);
212 }
213 
SerializeGeneric(Handle<HeapObject> heap_object)214 void CodeSerializer::SerializeGeneric(Handle<HeapObject> heap_object) {
215   // Object has not yet been serialized.  Serialize it here.
216   ObjectSerializer serializer(this, heap_object, &sink_);
217   serializer.Serialize();
218 }
219 
220 #ifndef V8_TARGET_ARCH_ARM
221 // NOTE(mmarchini): when FLAG_interpreted_frames_native_stack is on, we want to
222 // create duplicates of InterpreterEntryTrampoline for the deserialized
223 // functions, otherwise we'll call the builtin IET for those functions (which
224 // is not what a user of this flag wants).
CreateInterpreterDataForDeserializedCode(Isolate * isolate,Handle<SharedFunctionInfo> sfi,bool log_code_creation)225 void CreateInterpreterDataForDeserializedCode(Isolate* isolate,
226                                               Handle<SharedFunctionInfo> sfi,
227                                               bool log_code_creation) {
228   Handle<Script> script(Script::cast(sfi->script()), isolate);
229   String name = ReadOnlyRoots(isolate).empty_string();
230   if (script->name().IsString()) name = String::cast(script->name());
231   Handle<String> name_handle(name, isolate);
232 
233   SharedFunctionInfo::ScriptIterator iter(isolate, *script);
234   for (SharedFunctionInfo shared_info = iter.Next(); !shared_info.is_null();
235        shared_info = iter.Next()) {
236     if (!shared_info.HasBytecodeArray()) continue;
237     Handle<SharedFunctionInfo> info = handle(shared_info, isolate);
238     Handle<Code> code = isolate->factory()->CopyCode(Handle<Code>::cast(
239         isolate->factory()->interpreter_entry_trampoline_for_profiling()));
240 
241     Handle<InterpreterData> interpreter_data =
242         Handle<InterpreterData>::cast(isolate->factory()->NewStruct(
243             INTERPRETER_DATA_TYPE, AllocationType::kOld));
244 
245     interpreter_data->set_bytecode_array(info->GetBytecodeArray());
246     interpreter_data->set_interpreter_trampoline(*code);
247 
248     info->set_interpreter_data(*interpreter_data);
249 
250     if (!log_code_creation) continue;
251     Handle<AbstractCode> abstract_code = Handle<AbstractCode>::cast(code);
252     int line_num = script->GetLineNumber(info->StartPosition()) + 1;
253     int column_num = script->GetColumnNumber(info->StartPosition()) + 1;
254     PROFILE(isolate,
255             CodeCreateEvent(CodeEventListener::INTERPRETED_FUNCTION_TAG,
256                             abstract_code, info, name_handle, line_num,
257                             column_num));
258   }
259 }
260 #endif  // V8_TARGET_ARCH_ARM
261 
262 namespace {
263 class StressOffThreadDeserializeThread final : public base::Thread {
264  public:
StressOffThreadDeserializeThread(Isolate * isolate,const SerializedCodeData * scd)265   explicit StressOffThreadDeserializeThread(Isolate* isolate,
266                                             const SerializedCodeData* scd)
267       : Thread(
268             base::Thread::Options("StressOffThreadDeserializeThread", 2 * MB)),
269         isolate_(isolate),
270         scd_(scd) {}
271 
maybe_result() const272   MaybeHandle<SharedFunctionInfo> maybe_result() const { return maybe_result_; }
273 
Run()274   void Run() final {
275     LocalIsolate local_isolate(isolate_, ThreadKind::kBackground);
276     MaybeHandle<SharedFunctionInfo> local_maybe_result =
277         ObjectDeserializer::DeserializeSharedFunctionInfoOffThread(
278             &local_isolate, scd_, local_isolate.factory()->empty_string());
279 
280     maybe_result_ =
281         local_isolate.heap()->NewPersistentMaybeHandle(local_maybe_result);
282   }
283 
284  private:
285   Isolate* isolate_;
286   const SerializedCodeData* scd_;
287   MaybeHandle<SharedFunctionInfo> maybe_result_;
288 };
289 }  // namespace
290 
Deserialize(Isolate * isolate,ScriptData * cached_data,Handle<String> source,ScriptOriginOptions origin_options)291 MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
292     Isolate* isolate, ScriptData* cached_data, Handle<String> source,
293     ScriptOriginOptions origin_options) {
294   base::ElapsedTimer timer;
295   if (FLAG_profile_deserialization || FLAG_log_function_events) timer.Start();
296 
297   HandleScope scope(isolate);
298 
299   SerializedCodeData::SanityCheckResult sanity_check_result =
300       SerializedCodeData::CHECK_SUCCESS;
301   const SerializedCodeData scd = SerializedCodeData::FromCachedData(
302       cached_data, SerializedCodeData::SourceHash(source, origin_options),
303       &sanity_check_result);
304   if (sanity_check_result != SerializedCodeData::CHECK_SUCCESS) {
305     if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n");
306     DCHECK(cached_data->rejected());
307     isolate->counters()->code_cache_reject_reason()->AddSample(
308         sanity_check_result);
309     return MaybeHandle<SharedFunctionInfo>();
310   }
311 
312   // Deserialize.
313   MaybeHandle<SharedFunctionInfo> maybe_result;
314   // TODO(leszeks): Add LocalHeap support to deserializer
315   if (false && FLAG_stress_background_compile) {
316     StressOffThreadDeserializeThread thread(isolate, &scd);
317     CHECK(thread.Start());
318     thread.Join();
319 
320     maybe_result = thread.maybe_result();
321 
322     // Fix-up result script source.
323     Handle<SharedFunctionInfo> result;
324     if (maybe_result.ToHandle(&result)) {
325       Script::cast(result->script()).set_source(*source);
326     }
327   } else {
328     maybe_result = ObjectDeserializer::DeserializeSharedFunctionInfo(
329         isolate, &scd, source);
330   }
331 
332   Handle<SharedFunctionInfo> result;
333   if (!maybe_result.ToHandle(&result)) {
334     // Deserializing may fail if the reservations cannot be fulfilled.
335     if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n");
336     return MaybeHandle<SharedFunctionInfo>();
337   }
338 
339   if (FLAG_profile_deserialization) {
340     double ms = timer.Elapsed().InMillisecondsF();
341     int length = cached_data->length();
342     PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
343   }
344 
345   const bool log_code_creation =
346       isolate->logger()->is_listening_to_code_events() ||
347       isolate->is_profiling() ||
348       isolate->code_event_dispatcher()->IsListeningToCodeEvents();
349 
350 #ifndef V8_TARGET_ARCH_ARM
351   if (V8_UNLIKELY(FLAG_interpreted_frames_native_stack))
352     CreateInterpreterDataForDeserializedCode(isolate, result,
353                                              log_code_creation);
354 #endif  // V8_TARGET_ARCH_ARM
355 
356   bool needs_source_positions = isolate->NeedsSourcePositionsForProfiling();
357 
358   if (log_code_creation || FLAG_log_function_events) {
359     Handle<Script> script(Script::cast(result->script()), isolate);
360     Handle<String> name(script->name().IsString()
361                             ? String::cast(script->name())
362                             : ReadOnlyRoots(isolate).empty_string(),
363                         isolate);
364 
365     if (FLAG_log_function_events) {
366       LOG(isolate,
367           FunctionEvent("deserialize", script->id(),
368                         timer.Elapsed().InMillisecondsF(),
369                         result->StartPosition(), result->EndPosition(), *name));
370     }
371     if (log_code_creation) {
372       Script::InitLineEnds(isolate, script);
373 
374       SharedFunctionInfo::ScriptIterator iter(isolate, *script);
375       for (SharedFunctionInfo info = iter.Next(); !info.is_null();
376            info = iter.Next()) {
377         if (info.is_compiled()) {
378           Handle<SharedFunctionInfo> shared_info(info, isolate);
379           if (needs_source_positions) {
380             SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate,
381                                                                shared_info);
382           }
383           DisallowGarbageCollection no_gc;
384           int line_num =
385               script->GetLineNumber(shared_info->StartPosition()) + 1;
386           int column_num =
387               script->GetColumnNumber(shared_info->StartPosition()) + 1;
388           PROFILE(isolate,
389                   CodeCreateEvent(CodeEventListener::SCRIPT_TAG,
390                                   handle(shared_info->abstract_code(), isolate),
391                                   shared_info, name, line_num, column_num));
392         }
393       }
394     }
395   }
396 
397   if (needs_source_positions) {
398     Handle<Script> script(Script::cast(result->script()), isolate);
399     Script::InitLineEnds(isolate, script);
400   }
401   return scope.CloseAndEscape(result);
402 }
403 
SerializedCodeData(const std::vector<byte> * payload,const CodeSerializer * cs)404 SerializedCodeData::SerializedCodeData(const std::vector<byte>* payload,
405                                        const CodeSerializer* cs) {
406   DisallowGarbageCollection no_gc;
407 
408   // Calculate sizes.
409   uint32_t size = kHeaderSize + static_cast<uint32_t>(payload->size());
410   DCHECK(IsAligned(size, kPointerAlignment));
411 
412   // Allocate backing store and create result data.
413   AllocateData(size);
414 
415   // Zero out pre-payload data. Part of that is only used for padding.
416   memset(data_, 0, kHeaderSize);
417 
418   // Set header values.
419   SetMagicNumber();
420   SetHeaderValue(kVersionHashOffset, Version::Hash());
421   SetHeaderValue(kSourceHashOffset, cs->source_hash());
422   SetHeaderValue(kFlagHashOffset, FlagList::Hash());
423   SetHeaderValue(kPayloadLengthOffset, static_cast<uint32_t>(payload->size()));
424 
425   // Zero out any padding in the header.
426   memset(data_ + kUnalignedHeaderSize, 0, kHeaderSize - kUnalignedHeaderSize);
427 
428   // Copy serialized data.
429   CopyBytes(data_ + kHeaderSize, payload->data(),
430             static_cast<size_t>(payload->size()));
431 
432   SetHeaderValue(kChecksumOffset, Checksum(ChecksummedContent()));
433 }
434 
SanityCheck(uint32_t expected_source_hash) const435 SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
436     uint32_t expected_source_hash) const {
437   if (this->size_ < kHeaderSize) return INVALID_HEADER;
438   uint32_t magic_number = GetMagicNumber();
439   if (magic_number != kMagicNumber) return MAGIC_NUMBER_MISMATCH;
440   uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
441   uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
442   uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
443   uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
444   uint32_t c = GetHeaderValue(kChecksumOffset);
445   if (version_hash != Version::Hash()) return VERSION_MISMATCH;
446   if (source_hash != expected_source_hash) return SOURCE_MISMATCH;
447   if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH;
448   uint32_t max_payload_length = this->size_ - kHeaderSize;
449   if (payload_length > max_payload_length) return LENGTH_MISMATCH;
450   if (Checksum(ChecksummedContent()) != c) return CHECKSUM_MISMATCH;
451   return CHECK_SUCCESS;
452 }
453 
SourceHash(Handle<String> source,ScriptOriginOptions origin_options)454 uint32_t SerializedCodeData::SourceHash(Handle<String> source,
455                                         ScriptOriginOptions origin_options) {
456   const uint32_t source_length = source->length();
457 
458   static constexpr uint32_t kModuleFlagMask = (1 << 31);
459   const uint32_t is_module = origin_options.IsModule() ? kModuleFlagMask : 0;
460   DCHECK_EQ(0, source_length & kModuleFlagMask);
461 
462   return source_length | is_module;
463 }
464 
465 // Return ScriptData object and relinquish ownership over it to the caller.
GetScriptData()466 ScriptData* SerializedCodeData::GetScriptData() {
467   DCHECK(owns_data_);
468   ScriptData* result = new ScriptData(data_, size_);
469   result->AcquireDataOwnership();
470   owns_data_ = false;
471   data_ = nullptr;
472   return result;
473 }
474 
Payload() const475 Vector<const byte> SerializedCodeData::Payload() const {
476   const byte* payload = data_ + kHeaderSize;
477   DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment));
478   int length = GetHeaderValue(kPayloadLengthOffset);
479   DCHECK_EQ(data_ + size_, payload + length);
480   return Vector<const byte>(payload, length);
481 }
482 
SerializedCodeData(ScriptData * data)483 SerializedCodeData::SerializedCodeData(ScriptData* data)
484     : SerializedData(const_cast<byte*>(data->data()), data->length()) {}
485 
FromCachedData(ScriptData * cached_data,uint32_t expected_source_hash,SanityCheckResult * rejection_result)486 SerializedCodeData SerializedCodeData::FromCachedData(
487     ScriptData* cached_data, uint32_t expected_source_hash,
488     SanityCheckResult* rejection_result) {
489   DisallowGarbageCollection no_gc;
490   SerializedCodeData scd(cached_data);
491   *rejection_result = scd.SanityCheck(expected_source_hash);
492   if (*rejection_result != CHECK_SUCCESS) {
493     cached_data->Reject();
494     return SerializedCodeData(nullptr, 0);
495   }
496   return scd;
497 }
498 
499 }  // namespace internal
500 }  // namespace v8
501