startup-serializer.cc 10.7 KB
Newer Older
1 2 3 4 5 6
// 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.

#include "src/snapshot/startup-serializer.h"

7
#include "src/api/api.h"
8
#include "src/deoptimizer/deoptimizer.h"
9
#include "src/execution/v8threads.h"
10
#include "src/handles/global-handles.h"
11
#include "src/heap/heap-inl.h"
12
#include "src/heap/read-only-heap.h"
13
#include "src/objects/contexts.h"
14
#include "src/objects/foreign-inl.h"
15
#include "src/objects/objects-inl.h"
16
#include "src/objects/slots.h"
17
#include "src/snapshot/read-only-serializer.h"
18 19 20 21

namespace v8 {
namespace internal {

22 23 24 25
namespace {

// The isolate roots may not point at context-specific objects during
// serialization.
26
class V8_NODISCARD SanitizeIsolateScope final {
27 28
 public:
  SanitizeIsolateScope(Isolate* isolate, bool allow_active_isolate_for_testing,
29
                       const DisallowGarbageCollection& no_gc)
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
      : isolate_(isolate),
        feedback_vectors_for_profiling_tools_(
            isolate->heap()->feedback_vectors_for_profiling_tools()),
        detached_contexts_(isolate->heap()->detached_contexts()) {
#ifdef DEBUG
    if (!allow_active_isolate_for_testing) {
      // These should already be empty when creating a real snapshot.
      DCHECK_EQ(feedback_vectors_for_profiling_tools_,
                ReadOnlyRoots(isolate).undefined_value());
      DCHECK_EQ(detached_contexts_,
                ReadOnlyRoots(isolate).empty_weak_array_list());
    }
#endif

    isolate->SetFeedbackVectorsForProfilingTools(
        ReadOnlyRoots(isolate).undefined_value());
    isolate->heap()->SetDetachedContexts(
        ReadOnlyRoots(isolate).empty_weak_array_list());
  }

  ~SanitizeIsolateScope() {
    // Restore saved fields.
    isolate_->SetFeedbackVectorsForProfilingTools(
        feedback_vectors_for_profiling_tools_);
    isolate_->heap()->SetDetachedContexts(detached_contexts_);
  }

 private:
  Isolate* isolate_;
  const Object feedback_vectors_for_profiling_tools_;
  const WeakArrayList detached_contexts_;
};

}  // namespace

65
StartupSerializer::StartupSerializer(Isolate* isolate,
66
                                     Snapshot::SerializerFlags flags,
67
                                     ReadOnlySerializer* read_only_serializer)
68
    : RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot),
69 70 71
      read_only_serializer_(read_only_serializer),
      accessor_infos_(isolate->heap()),
      call_handler_infos_(isolate->heap()) {
72 73 74 75
  InitializeCodeAddressMap();
}

StartupSerializer::~StartupSerializer() {
76 77 78 79 80 81
  for (Handle<AccessorInfo> info : accessor_infos_) {
    RestoreExternalReferenceRedirector(isolate(), info);
  }
  for (Handle<CallHandlerInfo> info : call_handler_infos_) {
    RestoreExternalReferenceRedirector(isolate(), info);
  }
82 83 84
  OutputStatistics("StartupSerializer");
}

85
#ifdef DEBUG
86 87
namespace {

88
bool IsUnexpectedCodeObject(Isolate* isolate, HeapObject obj) {
89
  if (!obj.IsCode()) return false;
90

91
  Code code = Code::cast(obj);
92
  if (code.kind() == CodeKind::REGEXP) return false;
93 94
  if (!code.is_builtin()) return true;
  if (code.is_off_heap_trampoline()) return false;
95 96 97 98 99

  // An on-heap builtin. We only expect this for the interpreter entry
  // trampoline copy stored on the root list and transitively called builtins.
  // See Heap::interpreter_entry_trampoline_for_profiling.

100
  switch (code.builtin_index()) {
101 102 103 104 105 106 107 108 109 110
    case Builtins::kAbort:
    case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
    case Builtins::kInterpreterEntryTrampoline:
    case Builtins::kRecordWrite:
      return false;
    default:
      return true;
  }

  UNREACHABLE();
111 112 113
}

}  // namespace
114
#endif  // DEBUG
115

116
void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
117
#ifdef DEBUG
118
  if (obj->IsJSFunction()) {
119 120
    v8::base::OS::PrintError("Reference stack:\n");
    PrintStack(std::cerr);
121
    obj->Print(std::cerr);
122 123 124 125 126
    FATAL(
        "JSFunction should be added through the context snapshot instead of "
        "the isolate snapshot");
  }
#endif  // DEBUG
127
  DCHECK(!IsUnexpectedCodeObject(isolate(), *obj));
128

129
  if (SerializeHotObject(obj)) return;
130
  if (IsRootAndHasBeenSerialized(*obj) && SerializeRoot(obj)) return;
131 132
  if (SerializeUsingReadOnlyObjectCache(&sink_, obj)) return;
  if (SerializeBackReference(obj)) return;
133

134 135 136 137
  bool use_simulator = false;
#ifdef USE_SIMULATOR
  use_simulator = true;
#endif
138

139
  if (use_simulator && obj->IsAccessorInfo()) {
140
    // Wipe external reference redirects in the accessor info.
141
    Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(obj);
142
    Address original_address =
143 144
        Foreign::cast(info->getter()).foreign_address(isolate());
    Foreign::cast(info->js_getter())
145
        .set_foreign_address(isolate(), original_address);
146
    accessor_infos_.Push(*info);
147 148
  } else if (use_simulator && obj->IsCallHandlerInfo()) {
    Handle<CallHandlerInfo> info = Handle<CallHandlerInfo>::cast(obj);
149
    Address original_address =
150 151
        Foreign::cast(info->callback()).foreign_address(isolate());
    Foreign::cast(info->js_callback())
152
        .set_foreign_address(isolate(), original_address);
153
    call_handler_infos_.Push(*info);
154 155
  } else if (obj->IsScript() && Handle<Script>::cast(obj)->IsUserJavaScript()) {
    Handle<Script>::cast(obj)->set_context_data(
156
        ReadOnlyRoots(isolate()).uninitialized_symbol());
157
  } else if (obj->IsSharedFunctionInfo()) {
158
    // Clear inferred name for native functions.
159 160 161
    Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(obj);
    if (!shared->IsSubjectToDebugging() && shared->HasUncompiledData()) {
      shared->uncompiled_data().set_inferred_name(
162
          ReadOnlyRoots(isolate()).empty_string());
163
    }
164 165
  }

166
  CheckRehashability(*obj);
167

168
  // Object has not yet been serialized.  Serialize it here.
169
  DCHECK(!ReadOnlyHeap::Contains(*obj));
170
  ObjectSerializer object_serializer(this, obj, &sink_);
171 172 173 174
  object_serializer.Serialize();
}

void StartupSerializer::SerializeWeakReferencesAndDeferred() {
175 176 177
  // This comes right after serialization of the context snapshot, where we
  // add entries to the startup object cache of the startup snapshot. Add
  // one entry with 'undefined' to terminate the startup object cache.
178
  Object undefined = ReadOnlyRoots(isolate()).undefined_value();
179
  VisitRootPointer(Root::kStartupObjectCache, nullptr,
180
                   FullObjectSlot(&undefined));
181 182 183

  SerializeStringTable(isolate()->string_table());

184 185
  isolate()->heap()->IterateWeakRoots(
      this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable});
186 187 188 189
  SerializeDeferredObjects();
  Pad();
}

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
void StartupSerializer::SerializeStringTable(StringTable* string_table) {
  // A StringTable is serialized as:
  //
  //   N : int
  //   string 1
  //   string 2
  //   ...
  //   string N
  //
  // Notably, the hashmap structure, including empty and deleted elements, is
  // not serialized.

  sink_.PutInt(isolate()->string_table()->NumberOfElements(),
               "String table number of elements");

  // Custom RootVisitor which walks the string table, but only serializes the
  // string entries. This is an inline class to be able to access the non-public
  // SerializeObject method.
  class StartupSerializerStringTableVisitor : public RootVisitor {
   public:
    explicit StartupSerializerStringTableVisitor(StartupSerializer* serializer)
        : serializer_(serializer) {}

    void VisitRootPointers(Root root, const char* description,
                           FullObjectSlot start, FullObjectSlot end) override {
215 216 217 218 219 220
      UNREACHABLE();
    }

    void VisitRootPointers(Root root, const char* description,
                           OffHeapObjectSlot start,
                           OffHeapObjectSlot end) override {
221
      DCHECK_EQ(root, Root::kStringTable);
222 223 224
      Isolate* isolate = serializer_->isolate();
      for (OffHeapObjectSlot current = start; current < end; ++current) {
        Object obj = current.load(isolate);
225 226
        if (obj.IsHeapObject()) {
          DCHECK(obj.IsInternalizedString());
227
          serializer_->SerializeObject(handle(HeapObject::cast(obj), isolate));
228 229 230 231 232 233 234 235 236 237 238 239
        }
      }
    }

   private:
    StartupSerializer* serializer_;
  };

  StartupSerializerStringTableVisitor string_table_visitor(this);
  isolate()->string_table()->IterateElements(&string_table_visitor);
}

240
void StartupSerializer::SerializeStrongReferences(
241
    const DisallowGarbageCollection& no_gc) {
242 243 244
  Isolate* isolate = this->isolate();
  // No active threads.
  CHECK_NULL(isolate->thread_manager()->FirstThreadStateInUse());
245

246 247 248
  SanitizeIsolateScope sanitize_isolate(
      isolate, allow_active_isolate_for_testing(), no_gc);

249 250
  // Visit smi roots and immortal immovables first to make sure they end up in
  // the first page.
251
  isolate->heap()->IterateSmiRoots(this);
252 253 254
  isolate->heap()->IterateRoots(
      this,
      base::EnumSet<SkipRoot>{SkipRoot::kUnserializable, SkipRoot::kWeak});
255 256
}

257 258
SerializedHandleChecker::SerializedHandleChecker(Isolate* isolate,
                                                 std::vector<Context>* contexts)
259 260 261
    : isolate_(isolate) {
  AddToSet(isolate->heap()->serialized_objects());
  for (auto const& context : *contexts) {
262
    AddToSet(context.serialized_objects());
263 264 265
  }
}

266
bool StartupSerializer::SerializeUsingReadOnlyObjectCache(
267
    SnapshotByteSink* sink, Handle<HeapObject> obj) {
268
  return read_only_serializer_->SerializeUsingReadOnlyObjectCache(sink, obj);
269 270
}

271 272
void StartupSerializer::SerializeUsingStartupObjectCache(
    SnapshotByteSink* sink, Handle<HeapObject> obj) {
273
  int cache_index = SerializeInObjectCache(obj);
274 275
  sink->Put(kStartupObjectCache, "StartupObjectCache");
  sink->PutInt(cache_index, "startup_object_cache_index");
276 277
}

278
void StartupSerializer::CheckNoDirtyFinalizationRegistries() {
279
  Isolate* isolate = this->isolate();
280
  CHECK(isolate->heap()->dirty_js_finalization_registries_list().IsUndefined(
281
      isolate));
282 283 284
  CHECK(
      isolate->heap()->dirty_js_finalization_registries_list_tail().IsUndefined(
          isolate));
285 286
}

287
void SerializedHandleChecker::AddToSet(FixedArray serialized) {
288 289
  int length = serialized.length();
  for (int i = 0; i < length; i++) serialized_.insert(serialized.get(i));
290 291
}

292 293
void SerializedHandleChecker::VisitRootPointers(Root root,
                                                const char* description,
294 295 296
                                                FullObjectSlot start,
                                                FullObjectSlot end) {
  for (FullObjectSlot p = start; p < end; ++p) {
297 298 299
    if (serialized_.find(*p) != serialized_.end()) continue;
    PrintF("%s handle not serialized: ",
           root == Root::kGlobalHandles ? "global" : "eternal");
300
    (*p).Print();
301
    PrintF("\n");
302 303 304 305 306 307 308 309 310 311
    ok_ = false;
  }
}

bool SerializedHandleChecker::CheckGlobalAndEternalHandles() {
  isolate_->global_handles()->IterateAllRoots(this);
  isolate_->eternal_handles()->IterateAllRoots(this);
  return ok_;
}

312 313
}  // namespace internal
}  // namespace v8