GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 108 120 90.0 %
Date: 2022-02-27 04:15:00 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
6779648
  for (size_t i = 0; i < size; i++) {
33
6779624
    *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
static v8::StartupData blob = { blob_data, blob_size };
57
)";
58
59
6
  ss << R"(v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() {
60
  return &blob;
61
}
62
63
static const std::vector<size_t> isolate_data_indices {
64
)";
65
12
  WriteVector(&ss,
66
6
              data->isolate_data_indices.data(),
67
              data->isolate_data_indices.size());
68
6
  ss << R"(};
69
70
const std::vector<size_t>* NodeMainInstance::GetIsolateDataIndices() {
71
  return &isolate_data_indices;
72
}
73
74
static const EnvSerializeInfo env_info )"
75
6
     << data->env_info << R"(;
76
77
const EnvSerializeInfo* NodeMainInstance::GetEnvSerializeInfo() {
78
  return &env_info;
79
}
80
81
}  // namespace node
82
)";
83
84
6
  return ss.str();
85
}
86
87
6
void SnapshotBuilder::Generate(SnapshotData* out,
88
                               const std::vector<std::string> args,
89
                               const std::vector<std::string> exec_args) {
90
6
  Isolate* isolate = Isolate::Allocate();
91
6
  isolate->SetCaptureStackTraceForUncaughtExceptions(
92
      true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
93
12
  per_process::v8_platform.Platform()->RegisterIsolate(isolate,
94
6
                                                       uv_default_loop());
95
6
  std::unique_ptr<NodeMainInstance> main_instance;
96
12
  std::string result;
97
98
  {
99
    const std::vector<intptr_t>& external_references =
100
6
        NodeMainInstance::CollectExternalReferences();
101
12
    SnapshotCreator creator(isolate, external_references.data());
102
    Environment* env;
103
    {
104
      main_instance =
105
12
          NodeMainInstance::Create(isolate,
106
                                   uv_default_loop(),
107
6
                                   per_process::v8_platform.Platform(),
108
                                   args,
109
6
                                   exec_args);
110
111
12
      HandleScope scope(isolate);
112
6
      creator.SetDefaultContext(Context::New(isolate));
113
      out->isolate_data_indices =
114
6
          main_instance->isolate_data()->Serialize(&creator);
115
116
      // Run the per-context scripts
117
      Local<Context> context;
118
      {
119
12
        TryCatch bootstrapCatch(isolate);
120
6
        context = NewContext(isolate);
121
6
        if (bootstrapCatch.HasCaught()) {
122
          PrintCaughtException(isolate, context, bootstrapCatch);
123
          abort();
124
        }
125
      }
126
6
      Context::Scope context_scope(context);
127
128
      // Create the environment
129
6
      env = new Environment(main_instance->isolate_data(),
130
                            context,
131
                            args,
132
                            exec_args,
133
                            nullptr,
134
                            node::EnvironmentFlags::kDefaultFlags,
135
6
                            {});
136
      // Run scripts in lib/internal/bootstrap/
137
      {
138
12
        TryCatch bootstrapCatch(isolate);
139
6
        v8::MaybeLocal<Value> result = env->RunBootstrapping();
140
6
        if (bootstrapCatch.HasCaught()) {
141
          PrintCaughtException(isolate, context, bootstrapCatch);
142
        }
143
        result.ToLocalChecked();
144
      }
145
146
6
      if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
147
        env->PrintAllBaseObjects();
148
        printf("Environment = %p\n", env);
149
      }
150
151
      // Serialize the native states
152
6
      out->env_info = env->Serialize(&creator);
153
      // Serialize the context
154
6
      size_t index = creator.AddContext(
155
6
          context, {SerializeNodeContextInternalFields, env});
156
6
      CHECK_EQ(index, NodeMainInstance::kNodeContextIndex);
157
    }
158
159
    // Must be out of HandleScope
160
    out->blob =
161
6
        creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
162
163
    // We must be able to rehash the blob when we restore it or otherwise
164
    // the hash seed would be fixed by V8, introducing a vulnerability.
165
6
    CHECK(out->blob.CanBeRehashed());
166
167
    // We cannot resurrect the handles from the snapshot, so make sure that
168
    // no handles are left open in the environment after the blob is created
169
    // (which should trigger a GC and close all handles that can be closed).
170
6
    if (!env->req_wrap_queue()->IsEmpty()
171
6
        || !env->handle_wrap_queue()->IsEmpty()
172

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

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


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

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