GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 108 120 90.0 %
Date: 2021-10-07 04:12:46 Branches: 31 48 64.6 %

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

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

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

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

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