GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 589 725 81.2 %
Date: 2022-12-31 04:22:30 Branches: 140 244 57.4 %

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_builtins.h"
10
#include "node_contextify.h"
11
#include "node_errors.h"
12
#include "node_external_reference.h"
13
#include "node_file.h"
14
#include "node_internals.h"
15
#include "node_main_instance.h"
16
#include "node_metadata.h"
17
#include "node_process.h"
18
#include "node_snapshot_builder.h"
19
#include "node_util.h"
20
#include "node_v8.h"
21
#include "node_v8_platform-inl.h"
22
23
#if HAVE_INSPECTOR
24
#include "inspector/worker_inspector.h"  // ParentInspectorHandle
25
#endif
26
27
namespace node {
28
29
using v8::Context;
30
using v8::Function;
31
using v8::FunctionCallbackInfo;
32
using v8::HandleScope;
33
using v8::Isolate;
34
using v8::Local;
35
using v8::Object;
36
using v8::ObjectTemplate;
37
using v8::ScriptCompiler;
38
using v8::ScriptOrigin;
39
using v8::SnapshotCreator;
40
using v8::StartupData;
41
using v8::String;
42
using v8::TryCatch;
43
using v8::Value;
44
45
const uint32_t SnapshotData::kMagic;
46
47
std::ostream& operator<<(std::ostream& output,
48
                         const builtins::CodeCacheInfo& info) {
49
  output << "<builtins::CodeCacheInfo id=" << info.id
50
         << ", size=" << info.data.size() << ">\n";
51
  return output;
52
}
53
54
std::ostream& operator<<(std::ostream& output,
55
                         const std::vector<builtins::CodeCacheInfo>& vec) {
56
  output << "{\n";
57
  for (const auto& info : vec) {
58
    output << info;
59
  }
60
  output << "}\n";
61
  return output;
62
}
63
64
std::ostream& operator<<(std::ostream& output,
65
                         const std::vector<uint8_t>& vec) {
66
  output << "{\n";
67
  for (const auto& i : vec) {
68
    output << i << ",";
69
  }
70
  output << "}";
71
  return output;
72
}
73
74
18
std::ostream& operator<<(std::ostream& output,
75
                         const std::vector<PropInfo>& vec) {
76
18
  output << "{\n";
77
336
  for (const auto& info : vec) {
78
318
    output << "  " << info << ",\n";
79
  }
80
18
  output << "}";
81
18
  return output;
82
}
83
84
318
std::ostream& operator<<(std::ostream& output, const PropInfo& info) {
85
318
  output << "{ \"" << info.name << "\", " << std::to_string(info.id) << ", "
86
636
         << std::to_string(info.index) << " }";
87
318
  return output;
88
}
89
90
6
std::ostream& operator<<(std::ostream& output,
91
                         const std::vector<std::string>& vec) {
92
6
  output << "{\n";
93
354
  for (const auto& info : vec) {
94
348
    output << "  \"" << info << "\",\n";
95
  }
96
6
  output << "}";
97
6
  return output;
98
}
99
100
6
std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i) {
101
  output << "{\n"
102
6
         << "// -- builtins begins --\n"
103
6
         << i.builtins << ",\n"
104
         << "// -- builtins ends --\n"
105
6
         << "// -- persistent_values begins --\n"
106
6
         << i.persistent_values << ",\n"
107
         << "// -- persistent_values ends --\n"
108
6
         << "// -- native_objects begins --\n"
109
6
         << i.native_objects << ",\n"
110
6
         << "// -- native_objects ends --\n"
111
6
         << i.context << ",  // context\n"
112
6
         << "}";
113
6
  return output;
114
}
115
116
6
std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
117
  output << "{\n"
118
6
         << "// -- async_hooks begins --\n"
119
6
         << i.async_hooks << ",\n"
120
6
         << "// -- async_hooks ends --\n"
121
6
         << i.tick_info << ",  // tick_info\n"
122
6
         << i.immediate_info << ",  // immediate_info\n"
123
6
         << "// -- performance_state begins --\n"
124
6
         << i.performance_state << ",\n"
125
6
         << "// -- performance_state ends --\n"
126
6
         << i.exit_info << ",  // exit_info\n"
127
6
         << i.stream_base_state << ",  // stream_base_state\n"
128
6
         << i.should_abort_on_uncaught_toggle
129
         << ",  // should_abort_on_uncaught_toggle\n"
130
6
         << "// -- principal_realm begins --\n"
131
6
         << i.principal_realm << ",\n"
132
         << "// -- principal_realm ends --\n"
133
6
         << "}";
134
6
  return output;
135
}
136
137
class FileIO {
138
 public:
139
5
  explicit FileIO(FILE* file)
140
5
      : f(file),
141
5
        is_debug(per_process::enabled_debug_list.enabled(
142
5
            DebugCategory::MKSNAPSHOT)) {}
143
144
  template <typename... Args>
145
5692
  void Debug(const char* format, Args&&... args) const {
146
4180
    per_process::Debug(
147
        DebugCategory::MKSNAPSHOT, format, std::forward<Args>(args)...);
148
  }
149
150
  template <typename T>
151
  std::string ToStr(const T& arg) const {
152
    std::stringstream ss;
153
    ss << arg;
154
    return ss.str();
155
  }
156
157
  template <typename T>
158
  std::string GetName() const {
159
#define TYPE_LIST(V)                                                           \
160
  V(builtins::CodeCacheInfo)                                                   \
161
  V(PropInfo)                                                                  \
162
  V(std::string)
163
164
#define V(TypeName)                                                            \
165
  if (std::is_same_v<T, TypeName>) {                                           \
166
    return #TypeName;                                                          \
167
  }
168
    TYPE_LIST(V)
169
#undef V
170
171
    std::string name;
172
    if (std::is_arithmetic_v<T>) {
173
      if (!std::is_signed_v<T>) {
174
        name += "u";
175
      }
176
      name += std::is_integral_v<T> ? "int" : "float";
177
      name += std::to_string(sizeof(T) * 8);
178
      name += "_t";
179
    }
180
    return name;
181
  }
182
183
  FILE* f = nullptr;
184
  bool is_debug = false;
185
};
186
187
class FileReader : public FileIO {
188
 public:
189
2
  explicit FileReader(FILE* file) : FileIO(file) {}
190
2
  ~FileReader() {}
191
192
  // Helper for reading numeric types.
193
  template <typename T>
194
3472
  T Read() {
195
    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
196
    T result;
197
3472
    Read(&result, 1);
198
3472
    return result;
199
  }
200
201
  // Layout of vectors:
202
  // [ 4/8 bytes ] count
203
  // [   ...     ] contents (count * size of individual elements)
204
  template <typename T>
205
1276
  std::vector<T> ReadVector() {
206
1276
    if (is_debug) {
207
      std::string name = GetName<T>();
208
      Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T));
209
    }
210
1276
    size_t count = static_cast<size_t>(Read<size_t>());
211
1276
    if (count == 0) {
212
4
      return std::vector<T>();
213
    }
214
1272
    if (is_debug) {
215
      Debug("Reading %d vector elements...\n", count);
216
    }
217
2544
    std::vector<T> result = ReadVector<T>(count, std::is_arithmetic<T>{});
218
1272
    if (is_debug) {
219
      std::string str = std::is_arithmetic_v<T> ? "" : ToStr(result);
220
      std::string name = GetName<T>();
221
      Debug("ReadVector<%s>() read %s\n", name.c_str(), str.c_str());
222
    }
223
1272
    return result;
224
  }
225
226
852
  std::string ReadString() {
227
852
    size_t length = Read<size_t>();
228
229
852
    if (is_debug) {
230
      Debug("ReadString(), length=%d: ", length);
231
    }
232
233
852
    CHECK_GT(length, 0);  // There should be no empty strings.
234
1704
    MallocedBuffer<char> buf(length + 1);
235
852
    size_t r = fread(buf.data, 1, length + 1, f);
236
852
    CHECK_EQ(r, length + 1);
237
852
    std::string result(buf.data, length);  // This creates a copy of buf.data.
238
239
852
    if (is_debug) {
240
      Debug("\"%s\", read %d bytes\n", result.c_str(), r);
241
    }
242
243
852
    read_total += r;
244
852
    return result;
245
  }
246
247
  size_t read_total = 0;
248
249
 private:
250
  // Helper for reading an array of numeric types.
251
  template <typename T>
252
4728
  void Read(T* out, size_t count) {
253
    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
254
    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
255
4728
    if (is_debug) {
256
      std::string name = GetName<T>();
257
      Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count);
258
    }
259
260
4728
    size_t r = fread(out, sizeof(T), count, f);
261
4728
    CHECK_EQ(r, count);
262
263
4728
    if (is_debug) {
264
      std::string str =
265
          "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
266
      Debug("%s, read %d bytes\n", str.c_str(), r);
267
    }
268
4728
    read_total += r;
269
4728
  }
270
271
  // Helper for reading numeric vectors.
272
  template <typename Number>
273
1252
  std::vector<Number> ReadVector(size_t count, std::true_type) {
274
    static_assert(std::is_arithmetic_v<Number>, "Not an arithmetic type");
275
    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
276
1252
    std::vector<Number> result(count);
277
1252
    Read(result.data(), count);
278
1252
    return result;
279
  }
280
281
  // Helper for reading non-numeric vectors.
282
  template <typename T>
283
20
  std::vector<T> ReadVector(size_t count, std::false_type) {
284
    static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
285
    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
286
20
    std::vector<T> result;
287
20
    result.reserve(count);
288
20
    bool original_is_debug = is_debug;
289
20
    is_debug = original_is_debug && !std::is_same_v<T, std::string>;
290
1712
    for (size_t i = 0; i < count; ++i) {
291
1692
      if (is_debug) {
292
        Debug("\n[%d] ", i);
293
      }
294
1692
      result.push_back(Read<T>());
295
    }
296
20
    is_debug = original_is_debug;
297
298
20
    return result;
299
  }
300
};
301
302
class FileWriter : public FileIO {
303
 public:
304
3
  explicit FileWriter(FILE* file) : FileIO(file) {}
305
3
  ~FileWriter() {}
306
307
  // Helper for writing numeric types.
308
  template <typename T>
309
5240
  size_t Write(const T& data) {
310
    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
311
5240
    return Write(&data, 1);
312
  }
313
314
  // Layout of vectors:
315
  // [ 4/8 bytes ] count
316
  // [   ...     ] contents (count * size of individual elements)
317
  template <typename T>
318
1914
  size_t WriteVector(const std::vector<T>& data) {
319
1914
    if (is_debug) {
320
      std::string str = std::is_arithmetic_v<T> ? "" : ToStr(data);
321
      std::string name = GetName<T>();
322
      Debug("\nWriteVector<%s>() (%d-byte), count=%d: %s\n",
323
            name.c_str(),
324
            sizeof(T),
325
            data.size(),
326
            str.c_str());
327
    }
328
329
1914
    size_t written_total = Write<size_t>(data.size());
330
1914
    if (data.size() == 0) {
331
6
      return written_total;
332
    }
333
1908
    written_total += WriteVector<T>(data, std::is_arithmetic<T>{});
334
335
1908
    if (is_debug) {
336
      std::string name = GetName<T>();
337
      Debug("WriteVector<%s>() wrote %d bytes\n", name.c_str(), written_total);
338
    }
339
340
1908
    return written_total;
341
  }
342
343
  // The layout of a written string:
344
  // [  4/8 bytes     ] length
345
  // [ |length| bytes ] contents
346
1284
  size_t WriteString(const std::string& data) {
347
1284
    CHECK_GT(data.size(), 0);  // No empty strings should be written.
348
1284
    size_t written_total = Write<size_t>(data.size());
349
1284
    if (is_debug) {
350
      std::string str = ToStr(data);
351
      Debug("WriteString(), length=%d: \"%s\"\n", data.size(), data.c_str());
352
    }
353
354
1284
    size_t r = fwrite(data.c_str(), 1, data.size() + 1, f);
355
1284
    CHECK_EQ(r, data.size() + 1);
356
1284
    written_total += r;
357
358
1284
    if (is_debug) {
359
      Debug("WriteString() wrote %d bytes\n", written_total);
360
    }
361
362
1284
    return written_total;
363
  }
364
365
 private:
366
  // Helper for writing an array of numeric types.
367
  template <typename T>
368
7124
  size_t Write(const T* data, size_t count) {
369
    DCHECK_GT(count, 0);  // Should not write contents for vectors of size 0.
370
7124
    if (is_debug) {
371
      std::string str =
372
          "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
373
      std::string name = GetName<T>();
374
      Debug("Write<%s>() (%d-byte), count=%d: %s",
375
            name.c_str(),
376
            sizeof(T),
377
            count,
378
            str.c_str());
379
    }
380
381
7124
    size_t r = fwrite(data, sizeof(T), count, f);
382
7124
    CHECK_EQ(r, count);
383
384
7124
    if (is_debug) {
385
      Debug(", wrote %d bytes\n", r);
386
    }
387
7124
    return r;
388
  }
389
390
  // Helper for writing numeric vectors.
391
  template <typename Number>
392
1878
  size_t WriteVector(const std::vector<Number>& data, std::true_type) {
393
1878
    return Write(data.data(), data.size());
394
  }
395
396
  // Helper for writing non-numeric vectors.
397
  template <typename T>
398
30
  size_t WriteVector(const std::vector<T>& data, std::false_type) {
399
    DCHECK_GT(data.size(),
400
              0);  // Should not write contents for vectors of size 0.
401
30
    size_t written_total = 0;
402
30
    bool original_is_debug = is_debug;
403
30
    is_debug = original_is_debug && !std::is_same_v<T, std::string>;
404
2580
    for (size_t i = 0; i < data.size(); ++i) {
405
2550
      if (is_debug) {
406
        Debug("\n[%d] ", i);
407
      }
408
2550
      written_total += Write<T>(data[i]);
409
    }
410
30
    is_debug = original_is_debug;
411
412
30
    return written_total;
413
  }
414
};
415
416
// Layout of serialized std::string:
417
// [  4/8 bytes     ] length
418
// [ |length| bytes ] contents
419
template <>
420
116
std::string FileReader::Read() {
421
116
  return ReadString();
422
}
423
template <>
424
175
size_t FileWriter::Write(const std::string& data) {
425
175
  return WriteString(data);
426
}
427
428
// Layout of v8::StartupData
429
// [  4/8 bytes       ] raw_size
430
// [ |raw_size| bytes ] contents
431
template <>
432
2
v8::StartupData FileReader::Read() {
433
2
  Debug("Read<v8::StartupData>()\n");
434
435
2
  int raw_size = Read<int>();
436
2
  Debug("size=%d\n", raw_size);
437
438
2
  CHECK_GT(raw_size, 0);  // There should be no startup data of size 0.
439
  // The data pointer of v8::StartupData would be deleted so it must be new'ed.
440
2
  std::unique_ptr<char> buf = std::unique_ptr<char>(new char[raw_size]);
441
2
  Read<char>(buf.get(), raw_size);
442
443
2
  return v8::StartupData{buf.release(), raw_size};
444
}
445
446
template <>
447
3
size_t FileWriter::Write(const v8::StartupData& data) {
448
3
  Debug("\nWrite<v8::StartupData>() size=%d\n", data.raw_size);
449
450
3
  CHECK_GT(data.raw_size, 0);  // There should be no startup data of size 0.
451
3
  size_t written_total = Write<int>(data.raw_size);
452
3
  written_total += Write<char>(data.data, static_cast<size_t>(data.raw_size));
453
454
3
  Debug("Write<v8::StartupData>() wrote %d bytes\n\n", written_total);
455
3
  return written_total;
456
}
457
458
// Layout of builtins::CodeCacheInfo
459
// [  4/8 bytes ]  length of the module id string
460
// [    ...     ]  |length| bytes of module id
461
// [  4/8 bytes ]  length of module code cache
462
// [    ...     ]  |length| bytes of module code cache
463
template <>
464
624
builtins::CodeCacheInfo FileReader::Read() {
465
624
  Debug("Read<builtins::CodeCacheInfo>()\n");
466
467
624
  builtins::CodeCacheInfo result{ReadString(), ReadVector<uint8_t>()};
468
469
624
  if (is_debug) {
470
    std::string str = ToStr(result);
471
    Debug("Read<builtins::CodeCacheInfo>() %s\n", str.c_str());
472
  }
473
624
  return result;
474
}
475
476
template <>
477
936
size_t FileWriter::Write(const builtins::CodeCacheInfo& data) {
478
1872
  Debug("\nWrite<builtins::CodeCacheInfo>() id = %s"
479
        ", size=%d\n",
480
936
        data.id.c_str(),
481
936
        data.data.size());
482
483
936
  size_t written_total = WriteString(data.id);
484
936
  written_total += WriteVector<uint8_t>(data.data);
485
486
936
  Debug("Write<builtins::CodeCacheInfo>() wrote %d bytes\n", written_total);
487
936
  return written_total;
488
}
489
490
// Layout of PropInfo
491
// [ 4/8 bytes ]  length of the data name string
492
// [    ...    ]  |length| bytes of data name
493
// [  4 bytes  ]  index in the PropInfo vector
494
// [ 4/8 bytes ]  index in the snapshot blob, can be used with
495
//                GetDataFromSnapshotOnce().
496
template <>
497
106
PropInfo FileReader::Read() {
498
106
  Debug("Read<PropInfo>()\n");
499
500
106
  PropInfo result;
501
106
  result.name = ReadString();
502
106
  result.id = Read<uint32_t>();
503
106
  result.index = Read<SnapshotIndex>();
504
505
106
  if (is_debug) {
506
    std::string str = ToStr(result);
507
    Debug("Read<PropInfo>() %s\n", str.c_str());
508
  }
509
510
106
  return result;
511
}
512
513
template <>
514
164
size_t FileWriter::Write(const PropInfo& data) {
515
164
  if (is_debug) {
516
    std::string str = ToStr(data);
517
    Debug("Write<PropInfo>() %s\n", str.c_str());
518
  }
519
520
164
  size_t written_total = WriteString(data.name);
521
164
  written_total += Write<uint32_t>(data.id);
522
164
  written_total += Write<SnapshotIndex>(data.index);
523
524
164
  Debug("Write<PropInfo>() wrote %d bytes\n", written_total);
525
164
  return written_total;
526
}
527
528
// Layout of AsyncHooks::SerializeInfo
529
// [ 4/8 bytes ]  snapshot index of async_ids_stack
530
// [ 4/8 bytes ]  snapshot index of fields
531
// [ 4/8 bytes ]  snapshot index of async_id_fields
532
// [ 4/8 bytes ]  snapshot index of js_execution_async_resources
533
// [ 4/8 bytes ]  length of native_execution_async_resources
534
// [   ...     ]  snapshot indices of each element in
535
//                native_execution_async_resources
536
template <>
537
2
AsyncHooks::SerializeInfo FileReader::Read() {
538
2
  Debug("Read<AsyncHooks::SerializeInfo>()\n");
539
540
2
  AsyncHooks::SerializeInfo result;
541
2
  result.async_ids_stack = Read<AliasedBufferIndex>();
542
2
  result.fields = Read<AliasedBufferIndex>();
543
2
  result.async_id_fields = Read<AliasedBufferIndex>();
544
2
  result.js_execution_async_resources = Read<SnapshotIndex>();
545
2
  result.native_execution_async_resources = ReadVector<SnapshotIndex>();
546
547
2
  if (is_debug) {
548
    std::string str = ToStr(result);
549
    Debug("Read<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
550
  }
551
552
2
  return result;
553
}
554
template <>
555
3
size_t FileWriter::Write(const AsyncHooks::SerializeInfo& data) {
556
3
  if (is_debug) {
557
    std::string str = ToStr(data);
558
    Debug("Write<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
559
  }
560
561
3
  size_t written_total = Write<AliasedBufferIndex>(data.async_ids_stack);
562
3
  written_total += Write<AliasedBufferIndex>(data.fields);
563
3
  written_total += Write<AliasedBufferIndex>(data.async_id_fields);
564
3
  written_total += Write<SnapshotIndex>(data.js_execution_async_resources);
565
3
  written_total +=
566
3
      WriteVector<SnapshotIndex>(data.native_execution_async_resources);
567
568
3
  Debug("Write<AsyncHooks::SerializeInfo>() wrote %d bytes\n", written_total);
569
3
  return written_total;
570
}
571
572
// Layout of TickInfo::SerializeInfo
573
// [ 4/8 bytes ]  snapshot index of fields
574
template <>
575
2
TickInfo::SerializeInfo FileReader::Read() {
576
2
  Debug("Read<TickInfo::SerializeInfo>()\n");
577
578
  TickInfo::SerializeInfo result;
579
2
  result.fields = Read<AliasedBufferIndex>();
580
581
2
  if (is_debug) {
582
    std::string str = ToStr(result);
583
    Debug("Read<TickInfo::SerializeInfo>() %s\n", str.c_str());
584
  }
585
586
2
  return result;
587
}
588
589
template <>
590
3
size_t FileWriter::Write(const TickInfo::SerializeInfo& data) {
591
3
  if (is_debug) {
592
    std::string str = ToStr(data);
593
    Debug("Write<TickInfo::SerializeInfo>() %s\n", str.c_str());
594
  }
595
596
3
  size_t written_total = Write<AliasedBufferIndex>(data.fields);
597
598
3
  Debug("Write<TickInfo::SerializeInfo>() wrote %d bytes\n", written_total);
599
3
  return written_total;
600
}
601
602
// Layout of TickInfo::SerializeInfo
603
// [ 4/8 bytes ]  snapshot index of fields
604
template <>
605
2
ImmediateInfo::SerializeInfo FileReader::Read() {
606
  per_process::Debug(DebugCategory::MKSNAPSHOT,
607
                     "Read<ImmediateInfo::SerializeInfo>()\n");
608
609
  ImmediateInfo::SerializeInfo result;
610
2
  result.fields = Read<AliasedBufferIndex>();
611
2
  if (is_debug) {
612
    std::string str = ToStr(result);
613
    Debug("Read<ImmediateInfo::SerializeInfo>() %s\n", str.c_str());
614
  }
615
2
  return result;
616
}
617
618
template <>
619
3
size_t FileWriter::Write(const ImmediateInfo::SerializeInfo& data) {
620
3
  if (is_debug) {
621
    std::string str = ToStr(data);
622
    Debug("Write<ImmeidateInfo::SerializeInfo>() %s\n", str.c_str());
623
  }
624
625
3
  size_t written_total = Write<AliasedBufferIndex>(data.fields);
626
627
3
  Debug("Write<ImmeidateInfo::SerializeInfo>() wrote %d bytes\n",
628
        written_total);
629
3
  return written_total;
630
}
631
632
// Layout of PerformanceState::SerializeInfo
633
// [ 4/8 bytes ]  snapshot index of root
634
// [ 4/8 bytes ]  snapshot index of milestones
635
// [ 4/8 bytes ]  snapshot index of observers
636
template <>
637
2
performance::PerformanceState::SerializeInfo FileReader::Read() {
638
  per_process::Debug(DebugCategory::MKSNAPSHOT,
639
                     "Read<PerformanceState::SerializeInfo>()\n");
640
641
  performance::PerformanceState::SerializeInfo result;
642
2
  result.root = Read<AliasedBufferIndex>();
643
2
  result.milestones = Read<AliasedBufferIndex>();
644
2
  result.observers = Read<AliasedBufferIndex>();
645
2
  if (is_debug) {
646
    std::string str = ToStr(result);
647
    Debug("Read<PerformanceState::SerializeInfo>() %s\n", str.c_str());
648
  }
649
2
  return result;
650
}
651
652
template <>
653
3
size_t FileWriter::Write(
654
    const performance::PerformanceState::SerializeInfo& data) {
655
3
  if (is_debug) {
656
    std::string str = ToStr(data);
657
    Debug("Write<PerformanceState::SerializeInfo>() %s\n", str.c_str());
658
  }
659
660
3
  size_t written_total = Write<AliasedBufferIndex>(data.root);
661
3
  written_total += Write<AliasedBufferIndex>(data.milestones);
662
3
  written_total += Write<AliasedBufferIndex>(data.observers);
663
664
3
  Debug("Write<PerformanceState::SerializeInfo>() wrote %d bytes\n",
665
        written_total);
666
3
  return written_total;
667
}
668
669
// Layout of IsolateDataSerializeInfo
670
// [ 4/8 bytes ]  length of primitive_values vector
671
// [    ...    ]  |length| of primitive_values indices
672
// [ 4/8 bytes ]  length of template_values vector
673
// [    ...    ]  |length| of PropInfo data
674
template <>
675
2
IsolateDataSerializeInfo FileReader::Read() {
676
  per_process::Debug(DebugCategory::MKSNAPSHOT,
677
                     "Read<IsolateDataSerializeInfo>()\n");
678
679
2
  IsolateDataSerializeInfo result;
680
2
  result.primitive_values = ReadVector<SnapshotIndex>();
681
2
  result.template_values = ReadVector<PropInfo>();
682
2
  if (is_debug) {
683
    std::string str = ToStr(result);
684
    Debug("Read<IsolateDataSerializeInfo>() %s\n", str.c_str());
685
  }
686
2
  return result;
687
}
688
689
template <>
690
3
size_t FileWriter::Write(const IsolateDataSerializeInfo& data) {
691
3
  if (is_debug) {
692
    std::string str = ToStr(data);
693
    Debug("Write<IsolateDataSerializeInfo>() %s\n", str.c_str());
694
  }
695
696
3
  size_t written_total = WriteVector<SnapshotIndex>(data.primitive_values);
697
3
  written_total += WriteVector<PropInfo>(data.template_values);
698
699
3
  Debug("Write<IsolateDataSerializeInfo>() wrote %d bytes\n", written_total);
700
3
  return written_total;
701
}
702
703
template <>
704
2
RealmSerializeInfo FileReader::Read() {
705
  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<RealmSerializeInfo>()\n");
706
2
  RealmSerializeInfo result;
707
2
  result.builtins = ReadVector<std::string>();
708
2
  result.persistent_values = ReadVector<PropInfo>();
709
2
  result.native_objects = ReadVector<PropInfo>();
710
2
  result.context = Read<SnapshotIndex>();
711
2
  return result;
712
}
713
714
template <>
715
3
size_t FileWriter::Write(const RealmSerializeInfo& data) {
716
3
  if (is_debug) {
717
    std::string str = ToStr(data);
718
    Debug("\nWrite<RealmSerializeInfo>() %s\n", str.c_str());
719
  }
720
721
  // Use += here to ensure order of evaluation.
722
3
  size_t written_total = WriteVector<std::string>(data.builtins);
723
3
  written_total += WriteVector<PropInfo>(data.persistent_values);
724
3
  written_total += WriteVector<PropInfo>(data.native_objects);
725
3
  written_total += Write<SnapshotIndex>(data.context);
726
727
3
  Debug("Write<RealmSerializeInfo>() wrote %d bytes\n", written_total);
728
3
  return written_total;
729
}
730
731
template <>
732
2
EnvSerializeInfo FileReader::Read() {
733
  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n");
734
2
  EnvSerializeInfo result;
735
2
  result.async_hooks = Read<AsyncHooks::SerializeInfo>();
736
2
  result.tick_info = Read<TickInfo::SerializeInfo>();
737
2
  result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
738
  result.performance_state =
739
2
      Read<performance::PerformanceState::SerializeInfo>();
740
2
  result.exit_info = Read<AliasedBufferIndex>();
741
2
  result.stream_base_state = Read<AliasedBufferIndex>();
742
2
  result.should_abort_on_uncaught_toggle = Read<AliasedBufferIndex>();
743
2
  result.principal_realm = Read<RealmSerializeInfo>();
744
2
  return result;
745
}
746
747
template <>
748
3
size_t FileWriter::Write(const EnvSerializeInfo& data) {
749
3
  if (is_debug) {
750
    std::string str = ToStr(data);
751
    Debug("\nWrite<EnvSerializeInfo>() %s\n", str.c_str());
752
  }
753
754
  // Use += here to ensure order of evaluation.
755
3
  size_t written_total = Write<AsyncHooks::SerializeInfo>(data.async_hooks);
756
3
  written_total += Write<TickInfo::SerializeInfo>(data.tick_info);
757
3
  written_total += Write<ImmediateInfo::SerializeInfo>(data.immediate_info);
758
6
  written_total += Write<performance::PerformanceState::SerializeInfo>(
759
3
      data.performance_state);
760
3
  written_total += Write<AliasedBufferIndex>(data.exit_info);
761
3
  written_total += Write<AliasedBufferIndex>(data.stream_base_state);
762
3
  written_total +=
763
3
      Write<AliasedBufferIndex>(data.should_abort_on_uncaught_toggle);
764
3
  written_total += Write<RealmSerializeInfo>(data.principal_realm);
765
766
3
  Debug("Write<EnvSerializeInfo>() wrote %d bytes\n", written_total);
767
3
  return written_total;
768
}
769
770
// Layout of SnapshotMetadata
771
// [  1 byte   ]  type of the snapshot
772
// [ 4/8 bytes ]  length of the node version string
773
// [    ...    ]  |length| bytes of node version
774
// [ 4/8 bytes ]  length of the node arch string
775
// [    ...    ]  |length| bytes of node arch
776
// [ 4/8 bytes ]  length of the node platform string
777
// [    ...    ]  |length| bytes of node platform
778
// [  4 bytes  ]  v8 cache version tag
779
template <>
780
2
SnapshotMetadata FileReader::Read() {
781
  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n");
782
783
2
  SnapshotMetadata result;
784
2
  result.type = static_cast<SnapshotMetadata::Type>(Read<uint8_t>());
785
2
  result.node_version = ReadString();
786
2
  result.node_arch = ReadString();
787
2
  result.node_platform = ReadString();
788
2
  result.v8_cache_version_tag = Read<uint32_t>();
789
790
2
  if (is_debug) {
791
    std::string str = ToStr(result);
792
    Debug("Read<SnapshotMetadata>() %s\n", str.c_str());
793
  }
794
2
  return result;
795
}
796
797
template <>
798
3
size_t FileWriter::Write(const SnapshotMetadata& data) {
799
3
  if (is_debug) {
800
    std::string str = ToStr(data);
801
    Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str());
802
  }
803
3
  size_t written_total = 0;
804
  // We need the Node.js version, platform and arch to match because
805
  // Node.js may perform synchronizations that are platform-specific and they
806
  // can be changed in semver-patches.
807
3
  Debug("Write snapshot type %" PRIu8 "\n", static_cast<uint8_t>(data.type));
808
3
  written_total += Write<uint8_t>(static_cast<uint8_t>(data.type));
809
3
  Debug("Write Node.js version %s\n", data.node_version.c_str());
810
3
  written_total += WriteString(data.node_version);
811
3
  Debug("Write Node.js arch %s\n", data.node_arch);
812
3
  written_total += WriteString(data.node_arch);
813
3
  Debug("Write Node.js platform %s\n", data.node_platform);
814
3
  written_total += WriteString(data.node_platform);
815
6
  Debug("Write V8 cached data version tag %" PRIx32 "\n",
816
3
        data.v8_cache_version_tag);
817
3
  written_total += Write<uint32_t>(data.v8_cache_version_tag);
818
3
  return written_total;
819
}
820
821
// Layout of the snapshot blob
822
// [   4 bytes    ]  kMagic
823
// [   4/8 bytes  ]  length of Node.js version string
824
// [    ...       ]  contents of Node.js version string
825
// [   4/8 bytes  ]  length of Node.js arch string
826
// [    ...       ]  contents of Node.js arch string
827
// [    ...       ]  v8_snapshot_blob_data from SnapshotCreator::CreateBlob()
828
// [    ...       ]  isolate_data_info
829
// [    ...       ]  env_info
830
// [    ...       ]  code_cache
831
832
3
void SnapshotData::ToBlob(FILE* out) const {
833
6
  FileWriter w(out);
834
3
  w.Debug("SnapshotData::ToBlob()\n");
835
836
3
  size_t written_total = 0;
837
838
  // Metadata
839
3
  w.Debug("Write magic %" PRIx32 "\n", kMagic);
840
3
  written_total += w.Write<uint32_t>(kMagic);
841
3
  w.Debug("Write metadata\n");
842
3
  written_total += w.Write<SnapshotMetadata>(metadata);
843
844
3
  written_total += w.Write<v8::StartupData>(v8_snapshot_blob_data);
845
3
  w.Debug("Write isolate_data_indices\n");
846
3
  written_total += w.Write<IsolateDataSerializeInfo>(isolate_data_info);
847
3
  written_total += w.Write<EnvSerializeInfo>(env_info);
848
3
  w.Debug("Write code_cache\n");
849
3
  written_total += w.WriteVector<builtins::CodeCacheInfo>(code_cache);
850
3
  w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
851
3
}
852
853
2
bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
854
4
  FileReader r(in);
855
2
  r.Debug("SnapshotData::FromBlob()\n");
856
857
  DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
858
859
  // Metadata
860
2
  uint32_t magic = r.Read<uint32_t>();
861
2
  r.Debug("Read magic %" PRIx32 "\n", magic);
862
2
  CHECK_EQ(magic, kMagic);
863
2
  out->metadata = r.Read<SnapshotMetadata>();
864
2
  r.Debug("Read metadata\n");
865
2
  if (!out->Check()) {
866
    return false;
867
  }
868
869
2
  out->v8_snapshot_blob_data = r.Read<v8::StartupData>();
870
2
  r.Debug("Read isolate_data_info\n");
871
2
  out->isolate_data_info = r.Read<IsolateDataSerializeInfo>();
872
2
  out->env_info = r.Read<EnvSerializeInfo>();
873
2
  r.Debug("Read code_cache\n");
874
2
  out->code_cache = r.ReadVector<builtins::CodeCacheInfo>();
875
876
2
  r.Debug("SnapshotData::FromBlob() read %d bytes\n", r.read_total);
877
2
  return true;
878
}
879
880
5710
bool SnapshotData::Check() const {
881
5710
  if (metadata.node_version != per_process::metadata.versions.node) {
882
    fprintf(stderr,
883
            "Failed to load the startup snapshot because it was built with"
884
            "Node.js version %s and the current Node.js version is %s.\n",
885
            metadata.node_version.c_str(),
886
            NODE_VERSION);
887
    return false;
888
  }
889
890
5710
  if (metadata.node_arch != per_process::metadata.arch) {
891
    fprintf(stderr,
892
            "Failed to load the startup snapshot because it was built with"
893
            "architecture %s and the architecture is %s.\n",
894
            metadata.node_arch.c_str(),
895
            NODE_ARCH);
896
    return false;
897
  }
898
899
5710
  if (metadata.node_platform != per_process::metadata.platform) {
900
    fprintf(stderr,
901
            "Failed to load the startup snapshot because it was built with"
902
            "platform %s and the current platform is %s.\n",
903
            metadata.node_platform.c_str(),
904
            NODE_PLATFORM);
905
    return false;
906
  }
907
908
5710
  uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag();
909
5710
  if (metadata.v8_cache_version_tag != current_cache_version &&
910
151
      metadata.type == SnapshotMetadata::Type::kFullyCustomized) {
911
    // For now we only do this check for the customized snapshots - we know
912
    // that the flags we use in the default snapshot are limited and safe
913
    // enough so we can relax the constraints for it.
914
    fprintf(stderr,
915
            "Failed to load the startup snapshot because it was built with "
916
            "a different version of V8 or with different V8 configurations.\n"
917
            "Expected tag %" PRIx32 ", read %" PRIx32 "\n",
918
            current_cache_version,
919
            metadata.v8_cache_version_tag);
920
    return false;
921
  }
922
923
  // TODO(joyeecheung): check incompatible Node.js flags.
924
5710
  return true;
925
}
926
927
5785
SnapshotData::~SnapshotData() {
928
5785
  if (data_ownership == DataOwnership::kOwned &&
929
10
      v8_snapshot_blob_data.data != nullptr) {
930
9
    delete[] v8_snapshot_blob_data.data;
931
  }
932
5785
}
933
934
template <typename T>
935
3756
void WriteVector(std::ostream* ss, const T* vec, size_t size) {
936
46111404
  for (size_t i = 0; i < size; i++) {
937
46107648
    *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
938
  }
939
3756
}
940
941
3744
static std::string GetCodeCacheDefName(const std::string& id) {
942
3744
  char buf[64] = {0};
943
3744
  size_t size = id.size();
944
3744
  CHECK_LT(size, sizeof(buf));
945
84096
  for (size_t i = 0; i < size; ++i) {
946
80352
    char ch = id[i];
947

80352
    buf[i] = (ch == '-' || ch == '/') ? '_' : ch;
948
  }
949
3744
  return std::string(buf) + std::string("_cache_data");
950
}
951
952
2184
static std::string FormatSize(size_t size) {
953
2184
  char buf[64] = {0};
954
2184
  if (size < 1024) {
955
196
    snprintf(buf, sizeof(buf), "%.2fB", static_cast<double>(size));
956
1988
  } else if (size < 1024 * 1024) {
957
1988
    snprintf(buf, sizeof(buf), "%.2fKB", static_cast<double>(size / 1024));
958
  } else {
959
    snprintf(
960
        buf, sizeof(buf), "%.2fMB", static_cast<double>(size / 1024 / 1024));
961
  }
962
2184
  return buf;
963
}
964
965
1872
static void WriteStaticCodeCacheData(std::ostream* ss,
966
                                     const builtins::CodeCacheInfo& info) {
967
1872
  *ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n";
968
1872
  WriteVector(ss, info.data.data(), info.data.size());
969
1872
  *ss << "};";
970
1872
}
971
972
1872
static void WriteCodeCacheInitializer(std::ostream* ss, const std::string& id) {
973
3744
  std::string def_name = GetCodeCacheDefName(id);
974
1872
  *ss << "    { \"" << id << "\",\n";
975
1872
  *ss << "      {" << def_name << ",\n";
976
1872
  *ss << "       " << def_name << " + arraysize(" << def_name << "),\n";
977
1872
  *ss << "      }\n";
978
1872
  *ss << "    },\n";
979
1872
}
980
981
6
void FormatBlob(std::ostream& ss, const SnapshotData* data) {
982
6
  ss << R"(#include <cstddef>
983
#include "env.h"
984
#include "node_snapshot_builder.h"
985
#include "v8.h"
986
987
// This file is generated by tools/snapshot. Do not edit.
988
989
namespace node {
990
991
static const char v8_snapshot_blob_data[] = {
992
)";
993
12
  WriteVector(&ss,
994
6
              data->v8_snapshot_blob_data.data,
995
6
              data->v8_snapshot_blob_data.raw_size);
996
6
  ss << R"(};
997
998
static const int v8_snapshot_blob_size = )"
999
6
     << data->v8_snapshot_blob_data.raw_size << ";";
1000
1001
  // Windows can't deal with too many large vector initializers.
1002
  // Store the data into static arrays first.
1003
1878
  for (const auto& item : data->code_cache) {
1004
1872
    WriteStaticCodeCacheData(&ss, item);
1005
  }
1006
1007
6
  ss << R"(const SnapshotData snapshot_data {
1008
  // -- data_ownership begins --
1009
  SnapshotData::DataOwnership::kNotOwned,
1010
  // -- data_ownership ends --
1011
  // -- metadata begins --
1012
6
)" << data->metadata
1013
6
     << R"(,
1014
  // -- metadata ends --
1015
  // -- v8_snapshot_blob_data begins --
1016
  { v8_snapshot_blob_data, v8_snapshot_blob_size },
1017
  // -- v8_snapshot_blob_data ends --
1018
  // -- isolate_data_indices begins --
1019
6
)" << data->isolate_data_info
1020
6
     << R"(
1021
  // -- isolate_data_indices ends --
1022
  ,
1023
  // -- env_info begins --
1024
6
)" << data->env_info
1025
6
     << R"(
1026
  // -- env_info ends --
1027
  ,
1028
  // -- code_cache begins --
1029
  {)";
1030
1878
  for (const auto& item : data->code_cache) {
1031
1872
    WriteCodeCacheInitializer(&ss, item.id);
1032
  }
1033
6
  ss << R"(
1034
  }
1035
  // -- code_cache ends --
1036
};
1037
1038
const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() {
1039
  return &snapshot_data;
1040
}
1041
}  // namespace node
1042
)";
1043
6
}
1044
1045
// Reset context settings that need to be initialized again after
1046
// deserialization.
1047
15
static void ResetContextSettingsBeforeSnapshot(Local<Context> context) {
1048
  // Reset the AllowCodeGenerationFromStrings flag to true (default value) so
1049
  // that it can be re-initialized with v8 flag
1050
  // --disallow-code-generation-from-strings and recognized in
1051
  // node::InitializeContextRuntime.
1052
15
  context->AllowCodeGenerationFromStrings(true);
1053
15
}
1054
1055
5718
const std::vector<intptr_t>& SnapshotBuilder::CollectExternalReferences() {
1056

5718
  static auto registry = std::make_unique<ExternalReferenceRegistry>();
1057
5718
  return registry->external_references();
1058
}
1059
1060
5710
void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
1061
                                              Isolate::CreateParams* params) {
1062
5710
  params->external_references = CollectExternalReferences().data();
1063
5710
  params->snapshot_blob =
1064
5710
      const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
1065
5710
}
1066
1067
8
ExitCode SnapshotBuilder::Generate(SnapshotData* out,
1068
                                   const std::vector<std::string> args,
1069
                                   const std::vector<std::string> exec_args) {
1070
  const std::vector<intptr_t>& external_references =
1071
8
      CollectExternalReferences();
1072
8
  Isolate* isolate = Isolate::Allocate();
1073
  // Must be done before the SnapshotCreator creation so  that the
1074
  // memory reducer can be initialized.
1075
16
  per_process::v8_platform.Platform()->RegisterIsolate(isolate,
1076
8
                                                       uv_default_loop());
1077
1078
16
  SnapshotCreator creator(isolate, external_references.data());
1079
1080
8
  isolate->SetCaptureStackTraceForUncaughtExceptions(
1081
      true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
1082
1083
8
  Environment* env = nullptr;
1084
  std::unique_ptr<NodeMainInstance> main_instance =
1085
      NodeMainInstance::Create(isolate,
1086
                               uv_default_loop(),
1087
8
                               per_process::v8_platform.Platform(),
1088
                               args,
1089
16
                               exec_args);
1090
1091
  // The cleanups should be done in case of an early exit due to errors.
1092
8
  auto cleanup = OnScopeLeave([&]() {
1093
    // Must be done while the snapshot creator isolate is entered i.e. the
1094
    // creator is still alive. The snapshot creator destructor will destroy
1095
    // the isolate.
1096
8
    if (env != nullptr) {
1097
8
      FreeEnvironment(env);
1098
    }
1099
8
    main_instance->Dispose();
1100
8
    per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
1101
16
  });
1102
1103
  // It's only possible to be kDefault in node_mksnapshot.
1104
  SnapshotMetadata::Type snapshot_type =
1105
8
      per_process::cli_options->build_snapshot
1106
8
          ? SnapshotMetadata::Type::kFullyCustomized
1107
8
          : SnapshotMetadata::Type::kDefault;
1108
1109
  {
1110
8
    HandleScope scope(isolate);
1111
8
    TryCatch bootstrapCatch(isolate);
1112
1113
8
    auto print_Exception = OnScopeLeave([&]() {
1114
8
      if (bootstrapCatch.HasCaught()) {
1115
2
        PrintCaughtException(
1116
1
            isolate, isolate->GetCurrentContext(), bootstrapCatch);
1117
      }
1118
8
    });
1119
1120
    // The default context with only things created by V8.
1121
8
    Local<Context> default_context = Context::New(isolate);
1122
1123
    // The context used by the vm module.
1124
    Local<Context> vm_context;
1125
    {
1126
      Local<ObjectTemplate> global_template =
1127
8
          main_instance->isolate_data()->contextify_global_template();
1128
8
      CHECK(!global_template.IsEmpty());
1129
8
      if (!contextify::ContextifyContext::CreateV8Context(
1130
8
               isolate, global_template, nullptr, nullptr)
1131
8
               .ToLocal(&vm_context)) {
1132
        return ExitCode::kStartupSnapshotFailure;
1133
      }
1134
    }
1135
1136
    // The Node.js-specific context with primodials, can be used by workers
1137
    // TODO(joyeecheung): investigate if this can be used by vm contexts
1138
    // without breaking compatibility.
1139
8
    Local<Context> base_context = NewContext(isolate);
1140
8
    if (base_context.IsEmpty()) {
1141
      return ExitCode::kBootstrapFailure;
1142
    }
1143
8
    ResetContextSettingsBeforeSnapshot(base_context);
1144
1145
8
    Local<Context> main_context = NewContext(isolate);
1146
8
    if (main_context.IsEmpty()) {
1147
      return ExitCode::kBootstrapFailure;
1148
    }
1149
    // Initialize the main instance context.
1150
    {
1151
8
      Context::Scope context_scope(main_context);
1152
1153
      // Create the environment.
1154
      // It's not guaranteed that a context that goes through
1155
      // v8_inspector::V8Inspector::contextCreated() is runtime-independent,
1156
      // so do not start the inspector on the main context when building
1157
      // the default snapshot.
1158
8
      uint64_t env_flags = EnvironmentFlags::kDefaultFlags |
1159
                           EnvironmentFlags::kNoCreateInspector;
1160
1161
8
      env = CreateEnvironment(main_instance->isolate_data(),
1162
                              main_context,
1163
                              args,
1164
                              exec_args,
1165
                              static_cast<EnvironmentFlags::Flags>(env_flags));
1166
1167
      // This already ran scripts in lib/internal/bootstrap/, if it fails return
1168
8
      if (env == nullptr) {
1169
        return ExitCode::kBootstrapFailure;
1170
      }
1171
      // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
1172
      // loaded via LoadEnvironment() to execute process.argv[1] as the entry
1173
      // point (we currently only support this kind of entry point, but we
1174
      // could also explore snapshotting other kinds of execution modes
1175
      // in the future).
1176
8
      if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) {
1177
#if HAVE_INSPECTOR
1178
2
        env->InitializeInspector({});
1179
#endif
1180
4
        if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) {
1181
1
          return ExitCode::kGenericUserError;
1182
        }
1183
        // FIXME(joyeecheung): right now running the loop in the snapshot
1184
        // builder seems to introduces inconsistencies in JS land that need to
1185
        // be synchronized again after snapshot restoration.
1186
        ExitCode exit_code =
1187
1
            SpinEventLoopInternal(env).FromMaybe(ExitCode::kGenericUserError);
1188
1
        if (exit_code != ExitCode::kNoFailure) {
1189
          return exit_code;
1190
        }
1191
      }
1192
1193
7
      if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1194
        env->ForEachRealm([](Realm* realm) { realm->PrintInfoForSnapshot(); });
1195
        printf("Environment = %p\n", env);
1196
      }
1197
1198
      // Serialize the native states
1199
      out->isolate_data_info =
1200
7
          main_instance->isolate_data()->Serialize(&creator);
1201
7
      out->env_info = env->Serialize(&creator);
1202
1203
#ifdef NODE_USE_NODE_CODE_CACHE
1204
      // Regenerate all the code cache.
1205
7
      if (!builtins::BuiltinLoader::CompileAllBuiltins(main_context)) {
1206
        return ExitCode::kGenericUserError;
1207
      }
1208
7
      builtins::BuiltinLoader::CopyCodeCache(&(out->code_cache));
1209
2191
      for (const auto& item : out->code_cache) {
1210
2184
        std::string size_str = FormatSize(item.data.size());
1211
        per_process::Debug(DebugCategory::MKSNAPSHOT,
1212
                           "Generated code cache for %d: %s\n",
1213
2184
                           item.id.c_str(),
1214
4368
                           size_str.c_str());
1215
      }
1216
#endif
1217
1218
7
      ResetContextSettingsBeforeSnapshot(main_context);
1219
    }
1220
1221
    // Global handles to the contexts can't be disposed before the
1222
    // blob is created. So initialize all the contexts before adding them.
1223
    // TODO(joyeecheung): figure out how to remove this restriction.
1224
7
    creator.SetDefaultContext(default_context);
1225
7
    size_t index = creator.AddContext(vm_context);
1226
7
    CHECK_EQ(index, SnapshotData::kNodeVMContextIndex);
1227
7
    index = creator.AddContext(base_context);
1228
7
    CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
1229
7
    index = creator.AddContext(main_context,
1230
                               {SerializeNodeContextInternalFields, env});
1231
7
    CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
1232
  }
1233
1234
  // Must be out of HandleScope
1235
  out->v8_snapshot_blob_data =
1236
7
      creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep);
1237
1238
  // We must be able to rehash the blob when we restore it or otherwise
1239
  // the hash seed would be fixed by V8, introducing a vulnerability.
1240
7
  if (!out->v8_snapshot_blob_data.CanBeRehashed()) {
1241
    return ExitCode::kStartupSnapshotFailure;
1242
  }
1243
1244
14
  out->metadata = SnapshotMetadata{snapshot_type,
1245
                                   per_process::metadata.versions.node,
1246
                                   per_process::metadata.arch,
1247
                                   per_process::metadata.platform,
1248
7
                                   v8::ScriptCompiler::CachedDataVersionTag()};
1249
1250
  // We cannot resurrect the handles from the snapshot, so make sure that
1251
  // no handles are left open in the environment after the blob is created
1252
  // (which should trigger a GC and close all handles that can be closed).
1253
  bool queues_are_empty =
1254

7
      env->req_wrap_queue()->IsEmpty() && env->handle_wrap_queue()->IsEmpty();
1255

14
  if (!queues_are_empty ||
1256
7
      per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1257
    PrintLibuvHandleInformation(env->event_loop(), stderr);
1258
  }
1259
7
  if (!queues_are_empty) {
1260
    return ExitCode::kStartupSnapshotFailure;
1261
  }
1262
7
  return ExitCode::kNoFailure;
1263
}
1264
1265
6
ExitCode SnapshotBuilder::Generate(std::ostream& out,
1266
                                   const std::vector<std::string> args,
1267
                                   const std::vector<std::string> exec_args) {
1268
12
  SnapshotData data;
1269
6
  ExitCode exit_code = Generate(&data, args, exec_args);
1270
6
  if (exit_code != ExitCode::kNoFailure) {
1271
    return exit_code;
1272
  }
1273
6
  FormatBlob(out, &data);
1274
6
  return exit_code;
1275
}
1276
1277
53225
SnapshotableObject::SnapshotableObject(Environment* env,
1278
                                       Local<Object> wrap,
1279
53225
                                       EmbedderObjectType type)
1280
53225
    : BaseObject(env, wrap), type_(type) {
1281
53225
}
1282
1283
42
std::string_view SnapshotableObject::GetTypeName() const {
1284

42
  switch (type_) {
1285
#define V(PropertyName, NativeTypeName)                                        \
1286
  case EmbedderObjectType::k_##PropertyName: {                                 \
1287
    return NativeTypeName::type_name.as_string_view();                         \
1288
  }
1289
42
    SERIALIZABLE_OBJECT_TYPES(V)
1290
#undef V
1291
    default: { UNREACHABLE(); }
1292
  }
1293
}
1294
1295
17130
void DeserializeNodeInternalFields(Local<Object> holder,
1296
                                   int index,
1297
                                   StartupData payload,
1298
                                   void* env) {
1299
17130
  if (payload.raw_size == 0) {
1300
    holder->SetAlignedPointerInInternalField(index, nullptr);
1301
    return;
1302
  }
1303
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1304
                     "Deserialize internal field %d of %p, size=%d\n",
1305
34260
                     static_cast<int>(index),
1306
17130
                     (*holder),
1307
17130
                     static_cast<int>(payload.raw_size));
1308
1309
17130
  if (payload.raw_size == 0) {
1310
    holder->SetAlignedPointerInInternalField(index, nullptr);
1311
    return;
1312
  }
1313
1314
  DCHECK_EQ(index, BaseObject::kEmbedderType);
1315
1316
17130
  Environment* env_ptr = static_cast<Environment*>(env);
1317
17130
  const InternalFieldInfoBase* info =
1318
      reinterpret_cast<const InternalFieldInfoBase*>(payload.data);
1319
  // TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
1320
  // beginning of every InternalFieldInfoBase to ensure that we don't
1321
  // step on payloads that were not serialized by Node.js.
1322

17130
  switch (info->type) {
1323
#define V(PropertyName, NativeTypeName)                                        \
1324
  case EmbedderObjectType::k_##PropertyName: {                                 \
1325
    per_process::Debug(DebugCategory::MKSNAPSHOT,                              \
1326
                       "Object %p is %s\n",                                    \
1327
                       (*holder),                                              \
1328
                       NativeTypeName::type_name.as_string_view());            \
1329
    env_ptr->EnqueueDeserializeRequest(                                        \
1330
        NativeTypeName::Deserialize,                                           \
1331
        holder,                                                                \
1332
        index,                                                                 \
1333
        info->Copy<NativeTypeName::InternalFieldInfo>());                      \
1334
    break;                                                                     \
1335
  }
1336
51390
    SERIALIZABLE_OBJECT_TYPES(V)
1337
#undef V
1338
    default: {
1339
      // This should only be reachable during development when trying to
1340
      // deserialize a snapshot blob built by a version of Node.js that
1341
      // has more recognizable EmbedderObjectTypes than the deserializing
1342
      // Node.js binary.
1343
      fprintf(stderr,
1344
              "Unknown embedder object type %" PRIu8 ", possibly caused by "
1345
              "mismatched Node.js versions\n",
1346
              static_cast<uint8_t>(info->type));
1347
      ABORT();
1348
    }
1349
  }
1350
}
1351
1352
784
StartupData SerializeNodeContextInternalFields(Local<Object> holder,
1353
                                               int index,
1354
                                               void* env) {
1355
  // We only do one serialization for the kEmbedderType slot, the result
1356
  // contains everything necessary for deserializing the entire object,
1357
  // including the fields whose index is bigger than kEmbedderType
1358
  // (most importantly, BaseObject::kSlot).
1359
  // For Node.js this design is enough for all the native binding that are
1360
  // serializable.
1361
784
  if (index != BaseObject::kEmbedderType) {
1362
392
    return StartupData{nullptr, 0};
1363
  }
1364
1365
392
  void* type_ptr = holder->GetAlignedPointerFromInternalField(index);
1366
392
  if (type_ptr == nullptr) {
1367
371
    return StartupData{nullptr, 0};
1368
  }
1369
1370
21
  uint16_t type = *(static_cast<uint16_t*>(type_ptr));
1371
  per_process::Debug(DebugCategory::MKSNAPSHOT, "type = 0x%x\n", type);
1372
21
  if (type != kNodeEmbedderId) {
1373
    return StartupData{nullptr, 0};
1374
  }
1375
1376
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1377
                     "Serialize internal field, index=%d, holder=%p\n",
1378
42
                     static_cast<int>(index),
1379
42
                     *holder);
1380
1381
  void* native_ptr =
1382
42
      holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
1383
  per_process::Debug(DebugCategory::MKSNAPSHOT, "native = %p\n", native_ptr);
1384
  DCHECK(static_cast<BaseObject*>(native_ptr)->is_snapshotable());
1385
21
  SnapshotableObject* obj = static_cast<SnapshotableObject*>(native_ptr);
1386
1387
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1388
                     "Object %p is %s, ",
1389
21
                     *holder,
1390
21
                     obj->GetTypeName());
1391
21
  InternalFieldInfoBase* info = obj->Serialize(index);
1392
1393
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1394
                     "payload size=%d\n",
1395
21
                     static_cast<int>(info->length));
1396
  return StartupData{reinterpret_cast<const char*>(info),
1397
21
                     static_cast<int>(info->length)};
1398
}
1399
1400
7
void SerializeSnapshotableObjects(Realm* realm,
1401
                                  SnapshotCreator* creator,
1402
                                  RealmSerializeInfo* info) {
1403
7
  HandleScope scope(realm->isolate());
1404
7
  Local<Context> context = realm->context();
1405
7
  uint32_t i = 0;
1406
7
  realm->ForEachBaseObject([&](BaseObject* obj) {
1407
    // If there are any BaseObjects that are not snapshotable left
1408
    // during context serialization, V8 would crash due to unregistered
1409
    // global handles and print detailed information about them.
1410
21
    if (!obj->is_snapshotable()) {
1411
      return;
1412
    }
1413
21
    SnapshotableObject* ptr = static_cast<SnapshotableObject*>(obj);
1414
1415
21
    std::string type_name{ptr->GetTypeName()};
1416
    per_process::Debug(DebugCategory::MKSNAPSHOT,
1417
                       "Serialize snapshotable object %i (%p), "
1418
                       "object=%p, type=%s\n",
1419
84
                       static_cast<int>(i),
1420
                       ptr,
1421
42
                       *(ptr->object()),
1422
                       type_name);
1423
1424
42
    if (ptr->PrepareForSerialization(context, creator)) {
1425
21
      SnapshotIndex index = creator->AddData(context, obj->object());
1426
      per_process::Debug(DebugCategory::MKSNAPSHOT,
1427
                         "Serialized with index=%d\n",
1428
21
                         static_cast<int>(index));
1429
21
      info->native_objects.push_back({type_name, i, index});
1430
    }
1431
21
    i++;
1432
  });
1433
7
}
1434
1435
namespace mksnapshot {
1436
1437
2
void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
1438
4
  CHECK(args[0]->IsString());
1439
4
  Local<String> filename = args[0].As<String>();
1440
4
  Local<String> source = args[1].As<String>();
1441
2
  Isolate* isolate = args.GetIsolate();
1442
2
  Local<Context> context = isolate->GetCurrentContext();
1443
2
  ScriptOrigin origin(isolate, filename, 0, 0, true);
1444
  // TODO(joyeecheung): do we need all of these? Maybe we would want a less
1445
  // internal version of them.
1446
  std::vector<Local<String>> parameters = {
1447
2
      FIXED_ONE_BYTE_STRING(isolate, "require"),
1448
2
      FIXED_ONE_BYTE_STRING(isolate, "__filename"),
1449
2
      FIXED_ONE_BYTE_STRING(isolate, "__dirname"),
1450
6
  };
1451
2
  ScriptCompiler::Source script_source(source, origin);
1452
  Local<Function> fn;
1453
2
  if (ScriptCompiler::CompileFunction(context,
1454
                                      &script_source,
1455
                                      parameters.size(),
1456
                                      parameters.data(),
1457
                                      0,
1458
                                      nullptr,
1459
2
                                      ScriptCompiler::kEagerCompile)
1460
2
          .ToLocal(&fn)) {
1461
4
    args.GetReturnValue().Set(fn);
1462
  }
1463
2
}
1464
1465
2
void SetSerializeCallback(const FunctionCallbackInfo<Value>& args) {
1466
2
  Environment* env = Environment::GetCurrent(args);
1467
4
  CHECK(env->snapshot_serialize_callback().IsEmpty());
1468
2
  CHECK(args[0]->IsFunction());
1469
4
  env->set_snapshot_serialize_callback(args[0].As<Function>());
1470
2
}
1471
1472
2
void SetDeserializeCallback(const FunctionCallbackInfo<Value>& args) {
1473
2
  Environment* env = Environment::GetCurrent(args);
1474
4
  CHECK(env->snapshot_deserialize_callback().IsEmpty());
1475
2
  CHECK(args[0]->IsFunction());
1476
4
  env->set_snapshot_deserialize_callback(args[0].As<Function>());
1477
2
}
1478
1479
void SetDeserializeMainFunction(const FunctionCallbackInfo<Value>& args) {
1480
  Environment* env = Environment::GetCurrent(args);
1481
  CHECK(env->snapshot_deserialize_main().IsEmpty());
1482
  CHECK(args[0]->IsFunction());
1483
  env->set_snapshot_deserialize_main(args[0].As<Function>());
1484
}
1485
1486
819
void Initialize(Local<Object> target,
1487
                Local<Value> unused,
1488
                Local<Context> context,
1489
                void* priv) {
1490
819
  SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
1491
819
  SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
1492
819
  SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
1493
819
  SetMethod(context,
1494
            target,
1495
            "setDeserializeMainFunction",
1496
            SetDeserializeMainFunction);
1497
819
}
1498
1499
5718
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1500
5718
  registry->Register(CompileSerializeMain);
1501
5718
  registry->Register(SetSerializeCallback);
1502
5718
  registry->Register(SetDeserializeCallback);
1503
5718
  registry->Register(SetDeserializeMainFunction);
1504
5718
}
1505
}  // namespace mksnapshot
1506
}  // namespace node
1507
1508
5789
NODE_BINDING_CONTEXT_AWARE_INTERNAL(mksnapshot, node::mksnapshot::Initialize)
1509
5718
NODE_BINDING_EXTERNAL_REFERENCE(mksnapshot,
1510
                                node::mksnapshot::RegisterExternalReferences)