GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 122 160 76.2 %
Date: 2022-04-05 04:14:57 Branches: 36 72 50.0 %

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_v8.h"
16
#include "node_v8_platform-inl.h"
17
18
#if HAVE_INSPECTOR
19
#include "inspector/worker_inspector.h"  // ParentInspectorHandle
20
#endif
21
22
namespace node {
23
24
using v8::Context;
25
using v8::Function;
26
using v8::FunctionCallbackInfo;
27
using v8::HandleScope;
28
using v8::Isolate;
29
using v8::Local;
30
using v8::MaybeLocal;
31
using v8::Object;
32
using v8::ScriptCompiler;
33
using v8::ScriptOrigin;
34
using v8::SnapshotCreator;
35
using v8::StartupData;
36
using v8::String;
37
using v8::TryCatch;
38
using v8::Value;
39
40
template <typename T>
41
24
void WriteVector(std::ostringstream* ss, const T* vec, size_t size) {
42
8091428
  for (size_t i = 0; i < size; i++) {
43
8091404
    *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
44
  }
45
24
}
46
47
6
std::string FormatBlob(SnapshotData* data) {
48
12
  std::ostringstream ss;
49
50
6
  ss << R"(#include <cstddef>
51
#include "env.h"
52
#include "node_main_instance.h"
53
#include "v8.h"
54
55
// This file is generated by tools/snapshot. Do not edit.
56
57
namespace node {
58
59
static const char blob_data[] = {
60
)";
61
6
  WriteVector(&ss, data->blob.data, data->blob.raw_size);
62
6
  ss << R"(};
63
64
static const int blob_size = )"
65
6
     << data->blob.raw_size << R"(;
66
67
SnapshotData snapshot_data {
68
  // -- blob begins --
69
  { blob_data, blob_size },
70
  // -- blob ends --
71
  // -- isolate_data_indices begins --
72
  {
73
)";
74
12
  WriteVector(&ss,
75
6
              data->isolate_data_indices.data(),
76
              data->isolate_data_indices.size());
77
6
  ss << R"(},
78
  // -- isolate_data_indices ends --
79
  // -- env_info begins --
80
6
)" << data->env_info
81
6
  << R"(
82
  // -- env_info ends --
83
};
84
85
const SnapshotData* NodeMainInstance::GetEmbeddedSnapshotData() {
86
  return &snapshot_data;
87
}
88
}  // namespace node
89
)";
90
91
6
  return ss.str();
92
}
93
94
6
void SnapshotBuilder::Generate(SnapshotData* out,
95
                               const std::vector<std::string> args,
96
                               const std::vector<std::string> exec_args) {
97
6
  Isolate* isolate = Isolate::Allocate();
98
6
  isolate->SetCaptureStackTraceForUncaughtExceptions(
99
      true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
100
12
  per_process::v8_platform.Platform()->RegisterIsolate(isolate,
101
6
                                                       uv_default_loop());
102
6
  std::unique_ptr<NodeMainInstance> main_instance;
103
12
  std::string result;
104
105
  {
106
    const std::vector<intptr_t>& external_references =
107
6
        NodeMainInstance::CollectExternalReferences();
108
12
    SnapshotCreator creator(isolate, external_references.data());
109
    Environment* env;
110
    {
111
      main_instance =
112
12
          NodeMainInstance::Create(isolate,
113
                                   uv_default_loop(),
114
6
                                   per_process::v8_platform.Platform(),
115
                                   args,
116
6
                                   exec_args);
117
118
12
      HandleScope scope(isolate);
119
6
      creator.SetDefaultContext(Context::New(isolate));
120
      out->isolate_data_indices =
121
6
          main_instance->isolate_data()->Serialize(&creator);
122
123
      // Run the per-context scripts
124
      Local<Context> context;
125
      {
126
12
        TryCatch bootstrapCatch(isolate);
127
6
        context = NewContext(isolate);
128
6
        if (bootstrapCatch.HasCaught()) {
129
          PrintCaughtException(isolate, context, bootstrapCatch);
130
          abort();
131
        }
132
      }
133
6
      Context::Scope context_scope(context);
134
135
      // Create the environment
136
6
      env = new Environment(main_instance->isolate_data(),
137
                            context,
138
                            args,
139
                            exec_args,
140
                            nullptr,
141
                            node::EnvironmentFlags::kDefaultFlags,
142
6
                            {});
143
144
      // Run scripts in lib/internal/bootstrap/
145
      {
146
12
        TryCatch bootstrapCatch(isolate);
147
6
        MaybeLocal<Value> result = env->RunBootstrapping();
148
6
        if (bootstrapCatch.HasCaught()) {
149
          PrintCaughtException(isolate, context, bootstrapCatch);
150
        }
151
        result.ToLocalChecked();
152
      }
153
154
      // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
155
      // loaded via LoadEnvironment() to execute process.argv[1] as the entry
156
      // point (we currently only support this kind of entry point, but we
157
      // could also explore snapshotting other kinds of execution modes
158
      // in the future).
159
6
      if (per_process::cli_options->build_snapshot) {
160
#if HAVE_INSPECTOR
161
        env->InitializeInspector({});
162
#endif
163
        TryCatch bootstrapCatch(isolate);
164
        // TODO(joyeecheung): we could use the result for something special,
165
        // like setting up initializers that should be invoked at snapshot
166
        // dehydration.
167
        MaybeLocal<Value> result =
168
            LoadEnvironment(env, StartExecutionCallback{});
169
        if (bootstrapCatch.HasCaught()) {
170
          PrintCaughtException(isolate, context, bootstrapCatch);
171
        }
172
        result.ToLocalChecked();
173
        // FIXME(joyeecheung): right now running the loop in the snapshot
174
        // builder seems to introduces inconsistencies in JS land that need to
175
        // be synchronized again after snapshot restoration.
176
        int exit_code = SpinEventLoop(env).FromMaybe(1);
177
        CHECK_EQ(exit_code, 0);
178
        if (bootstrapCatch.HasCaught()) {
179
          PrintCaughtException(isolate, context, bootstrapCatch);
180
          abort();
181
        }
182
      }
183
184
6
      if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
185
        env->PrintAllBaseObjects();
186
        printf("Environment = %p\n", env);
187
      }
188
189
      // Serialize the native states
190
6
      out->env_info = env->Serialize(&creator);
191
      // Serialize the context
192
6
      size_t index = creator.AddContext(
193
6
          context, {SerializeNodeContextInternalFields, env});
194
6
      CHECK_EQ(index, NodeMainInstance::kNodeContextIndex);
195
    }
196
197
    // Must be out of HandleScope
198
    out->blob =
199
6
        creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
200
201
    // We must be able to rehash the blob when we restore it or otherwise
202
    // the hash seed would be fixed by V8, introducing a vulnerability.
203
6
    CHECK(out->blob.CanBeRehashed());
204
205
    // We cannot resurrect the handles from the snapshot, so make sure that
206
    // no handles are left open in the environment after the blob is created
207
    // (which should trigger a GC and close all handles that can be closed).
208
6
    if (!env->req_wrap_queue()->IsEmpty()
209
6
        || !env->handle_wrap_queue()->IsEmpty()
210

12
        || per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
211
      PrintLibuvHandleInformation(env->event_loop(), stderr);
212
    }
213
6
    CHECK(env->req_wrap_queue()->IsEmpty());
214
6
    CHECK(env->handle_wrap_queue()->IsEmpty());
215
216
    // Must be done while the snapshot creator isolate is entered i.e. the
217
    // creator is still alive.
218
6
    FreeEnvironment(env);
219
6
    main_instance->Dispose();
220
  }
221
222
6
  per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
223
6
}
224
225
6
std::string SnapshotBuilder::Generate(
226
    const std::vector<std::string> args,
227
    const std::vector<std::string> exec_args) {
228
12
  SnapshotData data;
229
6
  Generate(&data, args, exec_args);
230
6
  std::string result = FormatBlob(&data);
231
6
  delete[] data.blob.data;
232
6
  return result;
233
}
234
235
22392
SnapshotableObject::SnapshotableObject(Environment* env,
236
                                       Local<Object> wrap,
237
22392
                                       EmbedderObjectType type)
238
22392
    : BaseObject(env, wrap), type_(type) {
239
22392
}
240
241
24
const char* SnapshotableObject::GetTypeNameChars() const {
242

24
  switch (type_) {
243
#define V(PropertyName, NativeTypeName)                                        \
244
  case EmbedderObjectType::k_##PropertyName: {                                 \
245
    return NativeTypeName::type_name.c_str();                                  \
246
  }
247
24
    SERIALIZABLE_OBJECT_TYPES(V)
248
#undef V
249
    default: { UNREACHABLE(); }
250
  }
251
}
252
253
24
bool IsSnapshotableType(FastStringKey key) {
254
#define V(PropertyName, NativeTypeName)                                        \
255
  if (key == NativeTypeName::type_name) {                                      \
256
    return true;                                                               \
257
  }
258


24
  SERIALIZABLE_OBJECT_TYPES(V)
259
#undef V
260
261
  return false;
262
}
263
264
19900
void DeserializeNodeInternalFields(Local<Object> holder,
265
                                   int index,
266
                                   StartupData payload,
267
                                   void* env) {
268
  per_process::Debug(DebugCategory::MKSNAPSHOT,
269
                     "Deserialize internal field %d of %p, size=%d\n",
270
39800
                     static_cast<int>(index),
271
19900
                     (*holder),
272
19900
                     static_cast<int>(payload.raw_size));
273
19900
  if (payload.raw_size == 0) {
274
    holder->SetAlignedPointerInInternalField(index, nullptr);
275
    return;
276
  }
277
278
19900
  Environment* env_ptr = static_cast<Environment*>(env);
279
19900
  const InternalFieldInfo* info =
280
      reinterpret_cast<const InternalFieldInfo*>(payload.data);
281
282

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