GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 129 167 77.2 %
Date: 2022-05-03 04:14:50 Branches: 39 76 51.3 %

Line Branch Exec Source
1
2
#include "node_snapshotable.h"
3
#include <iostream>
4
#include <sstream>
5
#include "base_object-inl.h"
6
#include "debug_utils-inl.h"
7
#include "env-inl.h"
8
#include "node_blob.h"
9
#include "node_errors.h"
10
#include "node_external_reference.h"
11
#include "node_file.h"
12
#include "node_internals.h"
13
#include "node_main_instance.h"
14
#include "node_process.h"
15
#include "node_snapshot_builder.h"
16
#include "node_v8.h"
17
#include "node_v8_platform-inl.h"
18
19
#if HAVE_INSPECTOR
20
#include "inspector/worker_inspector.h"  // ParentInspectorHandle
21
#endif
22
23
namespace node {
24
25
using v8::Context;
26
using v8::Function;
27
using v8::FunctionCallbackInfo;
28
using v8::HandleScope;
29
using v8::Isolate;
30
using v8::Local;
31
using v8::MaybeLocal;
32
using v8::Object;
33
using v8::ScriptCompiler;
34
using v8::ScriptOrigin;
35
using v8::SnapshotCreator;
36
using v8::StartupData;
37
using v8::String;
38
using v8::TryCatch;
39
using v8::Value;
40
41
template <typename T>
42
24
void WriteVector(std::ostringstream* ss, const T* vec, size_t size) {
43
8157108
  for (size_t i = 0; i < size; i++) {
44
8157084
    *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
45
  }
46
24
}
47
48
6
std::string FormatBlob(SnapshotData* data) {
49
12
  std::ostringstream ss;
50
51
6
  ss << R"(#include <cstddef>
52
#include "env.h"
53
#include "node_snapshot_builder.h"
54
#include "v8.h"
55
56
// This file is generated by tools/snapshot. Do not edit.
57
58
namespace node {
59
60
static const char blob_data[] = {
61
)";
62
6
  WriteVector(&ss, data->blob.data, data->blob.raw_size);
63
6
  ss << R"(};
64
65
static const int blob_size = )"
66
6
     << data->blob.raw_size << R"(;
67
68
SnapshotData snapshot_data {
69
  // -- blob begins --
70
  { blob_data, blob_size },
71
  // -- blob ends --
72
  // -- isolate_data_indices begins --
73
  {
74
)";
75
12
  WriteVector(&ss,
76
6
              data->isolate_data_indices.data(),
77
              data->isolate_data_indices.size());
78
6
  ss << R"(},
79
  // -- isolate_data_indices ends --
80
  // -- env_info begins --
81
6
)" << data->env_info
82
6
     << R"(
83
  // -- env_info ends --
84
};
85
86
const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() {
87
  Mutex::ScopedLock lock(snapshot_data_mutex_);
88
  return &snapshot_data;
89
}
90
}  // namespace node
91
)";
92
93
6
  return ss.str();
94
}
95
96
Mutex SnapshotBuilder::snapshot_data_mutex_;
97
98
5536
const std::vector<intptr_t>& SnapshotBuilder::CollectExternalReferences() {
99

5536
  static auto registry = std::make_unique<ExternalReferenceRegistry>();
100
5536
  return registry->external_references();
101
}
102
103
5530
void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
104
                                              Isolate::CreateParams* params) {
105
5530
  params->external_references = CollectExternalReferences().data();
106
5530
  params->snapshot_blob = const_cast<v8::StartupData*>(&(data->blob));
107
5530
}
108
109
6
void SnapshotBuilder::Generate(SnapshotData* out,
110
                               const std::vector<std::string> args,
111
                               const std::vector<std::string> exec_args) {
112
6
  Isolate* isolate = Isolate::Allocate();
113
6
  isolate->SetCaptureStackTraceForUncaughtExceptions(
114
      true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
115
12
  per_process::v8_platform.Platform()->RegisterIsolate(isolate,
116
6
                                                       uv_default_loop());
117
6
  std::unique_ptr<NodeMainInstance> main_instance;
118
12
  std::string result;
119
120
  {
121
    const std::vector<intptr_t>& external_references =
122
6
        CollectExternalReferences();
123
12
    SnapshotCreator creator(isolate, external_references.data());
124
    Environment* env;
125
    {
126
      main_instance =
127
12
          NodeMainInstance::Create(isolate,
128
                                   uv_default_loop(),
129
6
                                   per_process::v8_platform.Platform(),
130
                                   args,
131
6
                                   exec_args);
132
133
12
      HandleScope scope(isolate);
134
6
      creator.SetDefaultContext(Context::New(isolate));
135
      out->isolate_data_indices =
136
6
          main_instance->isolate_data()->Serialize(&creator);
137
138
      // Run the per-context scripts
139
      Local<Context> context;
140
      {
141
12
        TryCatch bootstrapCatch(isolate);
142
6
        context = NewContext(isolate);
143
6
        if (bootstrapCatch.HasCaught()) {
144
          PrintCaughtException(isolate, context, bootstrapCatch);
145
          abort();
146
        }
147
      }
148
6
      Context::Scope context_scope(context);
149
150
      // Create the environment
151
6
      env = new Environment(main_instance->isolate_data(),
152
                            context,
153
                            args,
154
                            exec_args,
155
                            nullptr,
156
                            node::EnvironmentFlags::kDefaultFlags,
157
6
                            {});
158
159
      // Run scripts in lib/internal/bootstrap/
160
      {
161
12
        TryCatch bootstrapCatch(isolate);
162
6
        MaybeLocal<Value> result = env->RunBootstrapping();
163
6
        if (bootstrapCatch.HasCaught()) {
164
          PrintCaughtException(isolate, context, bootstrapCatch);
165
        }
166
        result.ToLocalChecked();
167
      }
168
169
      // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
170
      // loaded via LoadEnvironment() to execute process.argv[1] as the entry
171
      // point (we currently only support this kind of entry point, but we
172
      // could also explore snapshotting other kinds of execution modes
173
      // in the future).
174
6
      if (per_process::cli_options->build_snapshot) {
175
#if HAVE_INSPECTOR
176
        env->InitializeInspector({});
177
#endif
178
        TryCatch bootstrapCatch(isolate);
179
        // TODO(joyeecheung): we could use the result for something special,
180
        // like setting up initializers that should be invoked at snapshot
181
        // dehydration.
182
        MaybeLocal<Value> result =
183
            LoadEnvironment(env, StartExecutionCallback{});
184
        if (bootstrapCatch.HasCaught()) {
185
          PrintCaughtException(isolate, context, bootstrapCatch);
186
        }
187
        result.ToLocalChecked();
188
        // FIXME(joyeecheung): right now running the loop in the snapshot
189
        // builder seems to introduces inconsistencies in JS land that need to
190
        // be synchronized again after snapshot restoration.
191
        int exit_code = SpinEventLoop(env).FromMaybe(1);
192
        CHECK_EQ(exit_code, 0);
193
        if (bootstrapCatch.HasCaught()) {
194
          PrintCaughtException(isolate, context, bootstrapCatch);
195
          abort();
196
        }
197
      }
198
199
6
      if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
200
        env->PrintAllBaseObjects();
201
        printf("Environment = %p\n", env);
202
      }
203
204
      // Serialize the native states
205
6
      out->env_info = env->Serialize(&creator);
206
      // Serialize the context
207
6
      size_t index = creator.AddContext(
208
6
          context, {SerializeNodeContextInternalFields, env});
209
6
      CHECK_EQ(index, NodeMainInstance::kNodeContextIndex);
210
    }
211
212
    // Must be out of HandleScope
213
    out->blob =
214
6
        creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
215
216
    // We must be able to rehash the blob when we restore it or otherwise
217
    // the hash seed would be fixed by V8, introducing a vulnerability.
218
6
    CHECK(out->blob.CanBeRehashed());
219
220
    // We cannot resurrect the handles from the snapshot, so make sure that
221
    // no handles are left open in the environment after the blob is created
222
    // (which should trigger a GC and close all handles that can be closed).
223
6
    if (!env->req_wrap_queue()->IsEmpty()
224
6
        || !env->handle_wrap_queue()->IsEmpty()
225

12
        || per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
226
      PrintLibuvHandleInformation(env->event_loop(), stderr);
227
    }
228
6
    CHECK(env->req_wrap_queue()->IsEmpty());
229
6
    CHECK(env->handle_wrap_queue()->IsEmpty());
230
231
    // Must be done while the snapshot creator isolate is entered i.e. the
232
    // creator is still alive.
233
6
    FreeEnvironment(env);
234
6
    main_instance->Dispose();
235
  }
236
237
6
  per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
238
6
}
239
240
6
std::string SnapshotBuilder::Generate(
241
    const std::vector<std::string> args,
242
    const std::vector<std::string> exec_args) {
243
12
  SnapshotData data;
244
6
  Generate(&data, args, exec_args);
245
6
  std::string result = FormatBlob(&data);
246
6
  delete[] data.blob.data;
247
6
  return result;
248
}
249
250
22288
SnapshotableObject::SnapshotableObject(Environment* env,
251
                                       Local<Object> wrap,
252
22288
                                       EmbedderObjectType type)
253
22288
    : BaseObject(env, wrap), type_(type) {
254
22288
}
255
256
24
const char* SnapshotableObject::GetTypeNameChars() const {
257

24
  switch (type_) {
258
#define V(PropertyName, NativeTypeName)                                        \
259
  case EmbedderObjectType::k_##PropertyName: {                                 \
260
    return NativeTypeName::type_name.c_str();                                  \
261
  }
262
24
    SERIALIZABLE_OBJECT_TYPES(V)
263
#undef V
264
    default: { UNREACHABLE(); }
265
  }
266
}
267
268
24
bool IsSnapshotableType(FastStringKey key) {
269
#define V(PropertyName, NativeTypeName)                                        \
270
  if (key == NativeTypeName::type_name) {                                      \
271
    return true;                                                               \
272
  }
273


24
  SERIALIZABLE_OBJECT_TYPES(V)
274
#undef V
275
276
  return false;
277
}
278
279
19776
void DeserializeNodeInternalFields(Local<Object> holder,
280
                                   int index,
281
                                   StartupData payload,
282
                                   void* env) {
283
  per_process::Debug(DebugCategory::MKSNAPSHOT,
284
                     "Deserialize internal field %d of %p, size=%d\n",
285
39552
                     static_cast<int>(index),
286
19776
                     (*holder),
287
19776
                     static_cast<int>(payload.raw_size));
288
19776
  if (payload.raw_size == 0) {
289
    holder->SetAlignedPointerInInternalField(index, nullptr);
290
    return;
291
  }
292
293
19776
  Environment* env_ptr = static_cast<Environment*>(env);
294
19776
  const InternalFieldInfo* info =
295
      reinterpret_cast<const InternalFieldInfo*>(payload.data);
296
297

19776
  switch (info->type) {
298
#define V(PropertyName, NativeTypeName)                                        \
299
  case EmbedderObjectType::k_##PropertyName: {                                 \
300
    per_process::Debug(DebugCategory::MKSNAPSHOT,                              \
301
                       "Object %p is %s\n",                                    \
302
                       (*holder),                                              \
303
                       NativeTypeName::type_name.c_str());                     \
304
    env_ptr->EnqueueDeserializeRequest(                                        \
305
        NativeTypeName::Deserialize, holder, index, info->Copy());             \
306
    break;                                                                     \
307
  }
308
39552
    SERIALIZABLE_OBJECT_TYPES(V)
309
#undef V
310
    default: { UNREACHABLE(); }
311
  }
312
}
313
314
780
StartupData SerializeNodeContextInternalFields(Local<Object> holder,
315
                                               int index,
316
                                               void* env) {
317
  per_process::Debug(DebugCategory::MKSNAPSHOT,
318
                     "Serialize internal field, index=%d, holder=%p\n",
319
1560
                     static_cast<int>(index),
320
1560
                     *holder);
321
780
  void* ptr = holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
322
780
  if (ptr == nullptr) {
323
756
    return StartupData{nullptr, 0};
324
  }
325
326
  DCHECK(static_cast<BaseObject*>(ptr)->is_snapshotable());
327
24
  SnapshotableObject* obj = static_cast<SnapshotableObject*>(ptr);
328
  per_process::Debug(DebugCategory::MKSNAPSHOT,
329
                     "Object %p is %s, ",
330
24
                     *holder,
331
24
                     obj->GetTypeNameChars());
332
24
  InternalFieldInfo* info = obj->Serialize(index);
333
  per_process::Debug(DebugCategory::MKSNAPSHOT,
334
                     "payload size=%d\n",
335
24
                     static_cast<int>(info->length));
336
  return StartupData{reinterpret_cast<const char*>(info),
337
24
                     static_cast<int>(info->length)};
338
}
339
340
6
void SerializeBindingData(Environment* env,
341
                          SnapshotCreator* creator,
342
                          EnvSerializeInfo* info) {
343
6
  size_t i = 0;
344
6
  env->ForEachBindingData([&](FastStringKey key,
345
24
                              BaseObjectPtr<BaseObject> binding) {
346
    per_process::Debug(DebugCategory::MKSNAPSHOT,
347
                       "Serialize binding %i, %p, type=%s\n",
348
96
                       static_cast<int>(i),
349
48
                       *(binding->object()),
350
24
                       key.c_str());
351
352
24
    if (IsSnapshotableType(key)) {
353
24
      size_t index = creator->AddData(env->context(), binding->object());
354
      per_process::Debug(DebugCategory::MKSNAPSHOT,
355
                         "Serialized with index=%d\n",
356
24
                         static_cast<int>(index));
357
24
      info->bindings.push_back({key.c_str(), i, index});
358
24
      SnapshotableObject* ptr = static_cast<SnapshotableObject*>(binding.get());
359
24
      ptr->PrepareForSerialization(env->context(), creator);
360
    } else {
361
      UNREACHABLE();
362
    }
363
364
24
    i++;
365
24
  });
366
6
}
367
368
namespace mksnapshot {
369
370
static void CompileSnapshotMain(const FunctionCallbackInfo<Value>& args) {
371
  CHECK(args[0]->IsString());
372
  Local<String> filename = args[0].As<String>();
373
  Local<String> source = args[1].As<String>();
374
  Isolate* isolate = args.GetIsolate();
375
  Local<Context> context = isolate->GetCurrentContext();
376
  ScriptOrigin origin(isolate, filename, 0, 0, true);
377
  // TODO(joyeecheung): do we need all of these? Maybe we would want a less
378
  // internal version of them.
379
  std::vector<Local<String>> parameters = {
380
      FIXED_ONE_BYTE_STRING(isolate, "require"),
381
      FIXED_ONE_BYTE_STRING(isolate, "__filename"),
382
      FIXED_ONE_BYTE_STRING(isolate, "__dirname"),
383
  };
384
  ScriptCompiler::Source script_source(source, origin);
385
  Local<Function> fn;
386
  if (ScriptCompiler::CompileFunctionInContext(context,
387
                                               &script_source,
388
                                               parameters.size(),
389
                                               parameters.data(),
390
                                               0,
391
                                               nullptr,
392
                                               ScriptCompiler::kEagerCompile)
393
          .ToLocal(&fn)) {
394
    args.GetReturnValue().Set(fn);
395
  }
396
}
397
398
2655
static void Initialize(Local<Object> target,
399
                       Local<Value> unused,
400
                       Local<Context> context,
401
                       void* priv) {
402
2655
  Environment* env = Environment::GetCurrent(context);
403
2655
  Isolate* isolate = context->GetIsolate();
404
2655
  env->SetMethod(target, "compileSnapshotMain", CompileSnapshotMain);
405
  target
406
2655
      ->Set(context,
407
            FIXED_ONE_BYTE_STRING(isolate, "cleanups"),
408
7965
            v8::Array::New(isolate))
409
      .Check();
410
2655
}
411
412
4950
static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
413
4950
  registry->Register(CompileSnapshotMain);
414
4950
  registry->Register(MarkBootstrapComplete);
415
4950
}
416
}  // namespace mksnapshot
417
}  // namespace node
418
419
5008
NODE_MODULE_CONTEXT_AWARE_INTERNAL(mksnapshot, node::mksnapshot::Initialize)
420
4950
NODE_MODULE_EXTERNAL_REFERENCE(mksnapshot,
421
                               node::mksnapshot::RegisterExternalReferences)