GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 108 120 90.0 %
Date: 2022-03-30 04:15:10 Branches: 35 52 67.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_v8.h"
16
#include "node_v8_platform-inl.h"
17
18
namespace node {
19
20
using v8::Context;
21
using v8::HandleScope;
22
using v8::Isolate;
23
using v8::Local;
24
using v8::Object;
25
using v8::SnapshotCreator;
26
using v8::StartupData;
27
using v8::TryCatch;
28
using v8::Value;
29
30
template <typename T>
31
24
void WriteVector(std::ostringstream* ss, const T* vec, size_t size) {
32
7533714
  for (size_t i = 0; i < size; i++) {
33
7533690
    *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
34
  }
35
24
}
36
37
6
std::string FormatBlob(SnapshotData* data) {
38
12
  std::ostringstream ss;
39
40
6
  ss << R"(#include <cstddef>
41
#include "env.h"
42
#include "node_main_instance.h"
43
#include "v8.h"
44
45
// This file is generated by tools/snapshot. Do not edit.
46
47
namespace node {
48
49
static const char blob_data[] = {
50
)";
51
6
  WriteVector(&ss, data->blob.data, data->blob.raw_size);
52
6
  ss << R"(};
53
54
static const int blob_size = )"
55
6
     << data->blob.raw_size << R"(;
56
57
SnapshotData snapshot_data {
58
  // -- blob begins --
59
  { blob_data, blob_size },
60
  // -- blob ends --
61
  // -- isolate_data_indices begins --
62
  {
63
)";
64
12
  WriteVector(&ss,
65
6
              data->isolate_data_indices.data(),
66
              data->isolate_data_indices.size());
67
6
  ss << R"(},
68
  // -- isolate_data_indices ends --
69
  // -- env_info begins --
70
6
)" << data->env_info
71
6
  << R"(
72
  // -- env_info ends --
73
};
74
75
const SnapshotData* NodeMainInstance::GetEmbeddedSnapshotData() {
76
  return &snapshot_data;
77
}
78
}  // namespace node
79
)";
80
81
6
  return ss.str();
82
}
83
84
6
void SnapshotBuilder::Generate(SnapshotData* out,
85
                               const std::vector<std::string> args,
86
                               const std::vector<std::string> exec_args) {
87
6
  Isolate* isolate = Isolate::Allocate();
88
6
  isolate->SetCaptureStackTraceForUncaughtExceptions(
89
      true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
90
12
  per_process::v8_platform.Platform()->RegisterIsolate(isolate,
91
6
                                                       uv_default_loop());
92
6
  std::unique_ptr<NodeMainInstance> main_instance;
93
12
  std::string result;
94
95
  {
96
    const std::vector<intptr_t>& external_references =
97
6
        NodeMainInstance::CollectExternalReferences();
98
12
    SnapshotCreator creator(isolate, external_references.data());
99
    Environment* env;
100
    {
101
      main_instance =
102
12
          NodeMainInstance::Create(isolate,
103
                                   uv_default_loop(),
104
6
                                   per_process::v8_platform.Platform(),
105
                                   args,
106
6
                                   exec_args);
107
108
12
      HandleScope scope(isolate);
109
6
      creator.SetDefaultContext(Context::New(isolate));
110
      out->isolate_data_indices =
111
6
          main_instance->isolate_data()->Serialize(&creator);
112
113
      // Run the per-context scripts
114
      Local<Context> context;
115
      {
116
12
        TryCatch bootstrapCatch(isolate);
117
6
        context = NewContext(isolate);
118
6
        if (bootstrapCatch.HasCaught()) {
119
          PrintCaughtException(isolate, context, bootstrapCatch);
120
          abort();
121
        }
122
      }
123
6
      Context::Scope context_scope(context);
124
125
      // Create the environment
126
6
      env = new Environment(main_instance->isolate_data(),
127
                            context,
128
                            args,
129
                            exec_args,
130
                            nullptr,
131
                            node::EnvironmentFlags::kDefaultFlags,
132
6
                            {});
133
      // Run scripts in lib/internal/bootstrap/
134
      {
135
12
        TryCatch bootstrapCatch(isolate);
136
6
        v8::MaybeLocal<Value> result = env->RunBootstrapping();
137
6
        if (bootstrapCatch.HasCaught()) {
138
          PrintCaughtException(isolate, context, bootstrapCatch);
139
        }
140
        result.ToLocalChecked();
141
      }
142
143
6
      if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
144
        env->PrintAllBaseObjects();
145
        printf("Environment = %p\n", env);
146
      }
147
148
      // Serialize the native states
149
6
      out->env_info = env->Serialize(&creator);
150
      // Serialize the context
151
6
      size_t index = creator.AddContext(
152
6
          context, {SerializeNodeContextInternalFields, env});
153
6
      CHECK_EQ(index, NodeMainInstance::kNodeContextIndex);
154
    }
155
156
    // Must be out of HandleScope
157
    out->blob =
158
6
        creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
159
160
    // We must be able to rehash the blob when we restore it or otherwise
161
    // the hash seed would be fixed by V8, introducing a vulnerability.
162
6
    CHECK(out->blob.CanBeRehashed());
163
164
    // We cannot resurrect the handles from the snapshot, so make sure that
165
    // no handles are left open in the environment after the blob is created
166
    // (which should trigger a GC and close all handles that can be closed).
167
6
    if (!env->req_wrap_queue()->IsEmpty()
168
6
        || !env->handle_wrap_queue()->IsEmpty()
169

12
        || per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
170
      PrintLibuvHandleInformation(env->event_loop(), stderr);
171
    }
172
6
    CHECK(env->req_wrap_queue()->IsEmpty());
173
6
    CHECK(env->handle_wrap_queue()->IsEmpty());
174
175
    // Must be done while the snapshot creator isolate is entered i.e. the
176
    // creator is still alive.
177
6
    FreeEnvironment(env);
178
6
    main_instance->Dispose();
179
  }
180
181
6
  per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
182
6
}
183
184
6
std::string SnapshotBuilder::Generate(
185
    const std::vector<std::string> args,
186
    const std::vector<std::string> exec_args) {
187
12
  SnapshotData data;
188
6
  Generate(&data, args, exec_args);
189
6
  std::string result = FormatBlob(&data);
190
6
  delete[] data.blob.data;
191
6
  return result;
192
}
193
194
22364
SnapshotableObject::SnapshotableObject(Environment* env,
195
                                       Local<Object> wrap,
196
22364
                                       EmbedderObjectType type)
197
22364
    : BaseObject(env, wrap), type_(type) {
198
22364
}
199
200
24
const char* SnapshotableObject::GetTypeNameChars() const {
201

24
  switch (type_) {
202
#define V(PropertyName, NativeTypeName)                                        \
203
  case EmbedderObjectType::k_##PropertyName: {                                 \
204
    return NativeTypeName::type_name.c_str();                                  \
205
  }
206
24
    SERIALIZABLE_OBJECT_TYPES(V)
207
#undef V
208
    default: { UNREACHABLE(); }
209
  }
210
}
211
212
24
bool IsSnapshotableType(FastStringKey key) {
213
#define V(PropertyName, NativeTypeName)                                        \
214
  if (key == NativeTypeName::type_name) {                                      \
215
    return true;                                                               \
216
  }
217


24
  SERIALIZABLE_OBJECT_TYPES(V)
218
#undef V
219
220
  return false;
221
}
222
223
19868
void DeserializeNodeInternalFields(Local<Object> holder,
224
                                   int index,
225
                                   StartupData payload,
226
                                   void* env) {
227
  per_process::Debug(DebugCategory::MKSNAPSHOT,
228
                     "Deserialize internal field %d of %p, size=%d\n",
229
39736
                     static_cast<int>(index),
230
19868
                     (*holder),
231
19868
                     static_cast<int>(payload.raw_size));
232
19868
  if (payload.raw_size == 0) {
233
    holder->SetAlignedPointerInInternalField(index, nullptr);
234
    return;
235
  }
236
237
19868
  Environment* env_ptr = static_cast<Environment*>(env);
238
19868
  const InternalFieldInfo* info =
239
      reinterpret_cast<const InternalFieldInfo*>(payload.data);
240
241

19868
  switch (info->type) {
242
#define V(PropertyName, NativeTypeName)                                        \
243
  case EmbedderObjectType::k_##PropertyName: {                                 \
244
    per_process::Debug(DebugCategory::MKSNAPSHOT,                              \
245
                       "Object %p is %s\n",                                    \
246
                       (*holder),                                              \
247
                       NativeTypeName::type_name.c_str());                     \
248
    env_ptr->EnqueueDeserializeRequest(                                        \
249
        NativeTypeName::Deserialize, holder, index, info->Copy());             \
250
    break;                                                                     \
251
  }
252
39736
    SERIALIZABLE_OBJECT_TYPES(V)
253
#undef V
254
    default: { UNREACHABLE(); }
255
  }
256
}
257
258
780
StartupData SerializeNodeContextInternalFields(Local<Object> holder,
259
                                               int index,
260
                                               void* env) {
261
  per_process::Debug(DebugCategory::MKSNAPSHOT,
262
                     "Serialize internal field, index=%d, holder=%p\n",
263
1560
                     static_cast<int>(index),
264
1560
                     *holder);
265
780
  void* ptr = holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
266
780
  if (ptr == nullptr) {
267
756
    return StartupData{nullptr, 0};
268
  }
269
270
  DCHECK(static_cast<BaseObject*>(ptr)->is_snapshotable());
271
24
  SnapshotableObject* obj = static_cast<SnapshotableObject*>(ptr);
272
  per_process::Debug(DebugCategory::MKSNAPSHOT,
273
                     "Object %p is %s, ",
274
24
                     *holder,
275
24
                     obj->GetTypeNameChars());
276
24
  InternalFieldInfo* info = obj->Serialize(index);
277
  per_process::Debug(DebugCategory::MKSNAPSHOT,
278
                     "payload size=%d\n",
279
24
                     static_cast<int>(info->length));
280
  return StartupData{reinterpret_cast<const char*>(info),
281
24
                     static_cast<int>(info->length)};
282
}
283
284
6
void SerializeBindingData(Environment* env,
285
                          SnapshotCreator* creator,
286
                          EnvSerializeInfo* info) {
287
6
  size_t i = 0;
288
6
  env->ForEachBindingData([&](FastStringKey key,
289
24
                              BaseObjectPtr<BaseObject> binding) {
290
    per_process::Debug(DebugCategory::MKSNAPSHOT,
291
                       "Serialize binding %i, %p, type=%s\n",
292
96
                       static_cast<int>(i),
293
48
                       *(binding->object()),
294
24
                       key.c_str());
295
296
24
    if (IsSnapshotableType(key)) {
297
24
      size_t index = creator->AddData(env->context(), binding->object());
298
      per_process::Debug(DebugCategory::MKSNAPSHOT,
299
                         "Serialized with index=%d\n",
300
24
                         static_cast<int>(index));
301
24
      info->bindings.push_back({key.c_str(), i, index});
302
24
      SnapshotableObject* ptr = static_cast<SnapshotableObject*>(binding.get());
303
24
      ptr->PrepareForSerialization(env->context(), creator);
304
    } else {
305
      UNREACHABLE();
306
    }
307
308
24
    i++;
309
24
  });
310
6
}
311
312
}  // namespace node