• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 #include "node_snapshotable.h"
3 #include <iostream>
4 #include <sstream>
5 #include <vector>
6 #include "base_object-inl.h"
7 #include "blob_serializer_deserializer-inl.h"
8 #include "debug_utils-inl.h"
9 #include "env-inl.h"
10 #include "node_blob.h"
11 #include "node_builtins.h"
12 #include "node_contextify.h"
13 #include "node_errors.h"
14 #include "node_external_reference.h"
15 #include "node_file.h"
16 #include "node_internals.h"
17 #include "node_main_instance.h"
18 #include "node_metadata.h"
19 #include "node_process.h"
20 #include "node_snapshot_builder.h"
21 #include "node_url.h"
22 #include "node_util.h"
23 #include "node_v8.h"
24 #include "node_v8_platform-inl.h"
25 
26 #if HAVE_INSPECTOR
27 #include "inspector/worker_inspector.h"  // ParentInspectorHandle
28 #endif
29 
30 namespace node {
31 
32 using v8::Context;
33 using v8::Function;
34 using v8::FunctionCallbackInfo;
35 using v8::HandleScope;
36 using v8::Isolate;
37 using v8::Local;
38 using v8::Object;
39 using v8::ObjectTemplate;
40 using v8::ScriptCompiler;
41 using v8::ScriptOrigin;
42 using v8::SnapshotCreator;
43 using v8::StartupData;
44 using v8::String;
45 using v8::TryCatch;
46 using v8::Value;
47 
48 const uint32_t SnapshotData::kMagic;
49 
operator <<(std::ostream & output,const builtins::CodeCacheInfo & info)50 std::ostream& operator<<(std::ostream& output,
51                          const builtins::CodeCacheInfo& info) {
52   output << "<builtins::CodeCacheInfo id=" << info.id
53          << ", size=" << info.data.size() << ">\n";
54   return output;
55 }
56 
operator <<(std::ostream & output,const std::vector<builtins::CodeCacheInfo> & vec)57 std::ostream& operator<<(std::ostream& output,
58                          const std::vector<builtins::CodeCacheInfo>& vec) {
59   output << "{\n";
60   for (const auto& info : vec) {
61     output << info;
62   }
63   output << "}\n";
64   return output;
65 }
66 
operator <<(std::ostream & output,const std::vector<uint8_t> & vec)67 std::ostream& operator<<(std::ostream& output,
68                          const std::vector<uint8_t>& vec) {
69   output << "{\n";
70   for (const auto& i : vec) {
71     output << i << ",";
72   }
73   output << "}";
74   return output;
75 }
76 
operator <<(std::ostream & output,const std::vector<PropInfo> & vec)77 std::ostream& operator<<(std::ostream& output,
78                          const std::vector<PropInfo>& vec) {
79   output << "{\n";
80   for (const auto& info : vec) {
81     output << "  " << info << ",\n";
82   }
83   output << "}";
84   return output;
85 }
86 
operator <<(std::ostream & output,const PropInfo & info)87 std::ostream& operator<<(std::ostream& output, const PropInfo& info) {
88   output << "{ \"" << info.name << "\", " << std::to_string(info.id) << ", "
89          << std::to_string(info.index) << " }";
90   return output;
91 }
92 
operator <<(std::ostream & output,const std::vector<std::string> & vec)93 std::ostream& operator<<(std::ostream& output,
94                          const std::vector<std::string>& vec) {
95   output << "{\n";
96   for (const auto& info : vec) {
97     output << "  \"" << info << "\",\n";
98   }
99   output << "}";
100   return output;
101 }
102 
operator <<(std::ostream & output,const RealmSerializeInfo & i)103 std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i) {
104   output << "{\n"
105          << "// -- builtins begins --\n"
106          << i.builtins << ",\n"
107          << "// -- builtins ends --\n"
108          << "// -- persistent_values begins --\n"
109          << i.persistent_values << ",\n"
110          << "// -- persistent_values ends --\n"
111          << "// -- native_objects begins --\n"
112          << i.native_objects << ",\n"
113          << "// -- native_objects ends --\n"
114          << i.context << ",  // context\n"
115          << "}";
116   return output;
117 }
118 
operator <<(std::ostream & output,const EnvSerializeInfo & i)119 std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
120   output << "{\n"
121          << "// -- async_hooks begins --\n"
122          << i.async_hooks << ",\n"
123          << "// -- async_hooks ends --\n"
124          << i.tick_info << ",  // tick_info\n"
125          << i.immediate_info << ",  // immediate_info\n"
126          << i.timeout_info << ",  // timeout_info\n"
127          << "// -- performance_state begins --\n"
128          << i.performance_state << ",\n"
129          << "// -- performance_state ends --\n"
130          << i.exiting << ",  // exiting\n"
131          << i.stream_base_state << ",  // stream_base_state\n"
132          << i.should_abort_on_uncaught_toggle
133          << ",  // should_abort_on_uncaught_toggle\n"
134          << "// -- principal_realm begins --\n"
135          << i.principal_realm << ",\n"
136          << "// -- principal_realm ends --\n"
137          << "}";
138   return output;
139 }
140 
141 class SnapshotSerializerDeserializer {
142  public:
SnapshotSerializerDeserializer()143   SnapshotSerializerDeserializer()
144       : is_debug(per_process::enabled_debug_list.enabled(
145             DebugCategory::MKSNAPSHOT)) {}
146 
147   template <typename... Args>
Debug(const char * format,Args &&...args) const148   void Debug(const char* format, Args&&... args) const {
149     per_process::Debug(
150         DebugCategory::MKSNAPSHOT, format, std::forward<Args>(args)...);
151   }
152 
153   template <typename T>
ToStr(const T & arg) const154   std::string ToStr(const T& arg) const {
155     std::stringstream ss;
156     ss << arg;
157     return ss.str();
158   }
159 
160   template <typename T>
GetName() const161   std::string GetName() const {
162 #define TYPE_LIST(V)                                                           \
163   V(builtins::CodeCacheInfo)                                                   \
164   V(PropInfo)                                                                  \
165   V(std::string)
166 
167 #define V(TypeName)                                                            \
168   if constexpr (std::is_same_v<T, TypeName>) {                                 \
169     return #TypeName;                                                          \
170   } else  // NOLINT(readability/braces)
171     TYPE_LIST(V)
172 #undef V
173 
174     if constexpr (std::is_arithmetic_v<T>) {
175       return (std::is_unsigned_v<T>   ? "uint"
176               : std::is_integral_v<T> ? "int"
177                                       : "float") +
178              std::to_string(sizeof(T) * 8) + "_t";
179     }
180     return "";
181   }
182 
183   bool is_debug = false;
184 };
185 
186 class SnapshotDeserializer : public SnapshotSerializerDeserializer {
187  public:
SnapshotDeserializer(const std::vector<char> & s)188   explicit SnapshotDeserializer(const std::vector<char>& s)
189       : SnapshotSerializerDeserializer(), sink(s) {}
~SnapshotDeserializer()190   ~SnapshotDeserializer() {}
191 
192   // Helper for reading numeric types.
193   template <typename T>
Read()194   T Read() {
195     static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
196     T result;
197     Read(&result, 1);
198     return result;
199   }
200 
201   // Layout of vectors:
202   // [ 4/8 bytes ] count
203   // [   ...     ] contents (count * size of individual elements)
204   template <typename T>
ReadVector()205   std::vector<T> ReadVector() {
206     if (is_debug) {
207       std::string name = GetName<T>();
208       Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T));
209     }
210     size_t count = static_cast<size_t>(Read<size_t>());
211     if (count == 0) {
212       return std::vector<T>();
213     }
214     if (is_debug) {
215       Debug("Reading %d vector elements...\n", count);
216     }
217     std::vector<T> result = ReadVector<T>(count, std::is_arithmetic<T>{});
218     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     return result;
224   }
225 
ReadString()226   std::string ReadString() {
227     size_t length = Read<size_t>();
228 
229     if (is_debug) {
230       Debug("ReadString(), length=%d: ", length);
231     }
232 
233     CHECK_GT(length, 0);  // There should be no empty strings.
234     MallocedBuffer<char> buf(length + 1);
235     memcpy(buf.data, sink.data() + read_total, length + 1);
236     std::string result(buf.data, length);  // This creates a copy of buf.data.
237 
238     if (is_debug) {
239       Debug("\"%s\", read %zu bytes\n", result.c_str(), length + 1);
240     }
241 
242     read_total += length + 1;
243     return result;
244   }
245 
246   size_t read_total = 0;
247   const std::vector<char>& sink;
248 
249  private:
250   // Helper for reading an array of numeric types.
251   template <typename T>
Read(T * out,size_t count)252   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     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     size_t size = sizeof(T) * count;
261     memcpy(out, sink.data() + read_total, size);
262 
263     if (is_debug) {
264       std::string str =
265           "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
266       Debug("%s, read %zu bytes\n", str.c_str(), size);
267     }
268     read_total += size;
269   }
270 
271   // Helper for reading numeric vectors.
272   template <typename Number>
ReadVector(size_t count,std::true_type)273   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     std::vector<Number> result(count);
277     Read(result.data(), count);
278     return result;
279   }
280 
281   // Helper for reading non-numeric vectors.
282   template <typename T>
ReadVector(size_t count,std::false_type)283   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     std::vector<T> result;
287     result.reserve(count);
288     bool original_is_debug = is_debug;
289     is_debug = original_is_debug && !std::is_same_v<T, std::string>;
290     for (size_t i = 0; i < count; ++i) {
291       if (is_debug) {
292         Debug("\n[%d] ", i);
293       }
294       result.push_back(Read<T>());
295     }
296     is_debug = original_is_debug;
297 
298     return result;
299   }
300 };
301 
302 class SnapshotSerializer : public SnapshotSerializerDeserializer {
303  public:
SnapshotSerializer()304   SnapshotSerializer() : SnapshotSerializerDeserializer() {
305     // Currently the snapshot blob built with an empty script is around 4MB.
306     // So use that as the default sink size.
307     sink.reserve(4 * 1024 * 1024);
308   }
~SnapshotSerializer()309   ~SnapshotSerializer() {}
310   std::vector<char> sink;
311 
312   // Helper for writing numeric types.
313   template <typename T>
Write(const T & data)314   size_t Write(const T& data) {
315     static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
316     return Write(&data, 1);
317   }
318 
319   // Layout of vectors:
320   // [ 4/8 bytes ] count
321   // [   ...     ] contents (count * size of individual elements)
322   template <typename T>
WriteVector(const std::vector<T> & data)323   size_t WriteVector(const std::vector<T>& data) {
324     if (is_debug) {
325       std::string str = std::is_arithmetic_v<T> ? "" : ToStr(data);
326       std::string name = GetName<T>();
327       Debug("\nWriteVector<%s>() (%d-byte), count=%d: %s\n",
328             name.c_str(),
329             sizeof(T),
330             data.size(),
331             str.c_str());
332     }
333 
334     size_t written_total = Write<size_t>(data.size());
335     if (data.size() == 0) {
336       return written_total;
337     }
338     written_total += WriteVector<T>(data, std::is_arithmetic<T>{});
339 
340     if (is_debug) {
341       std::string name = GetName<T>();
342       Debug("WriteVector<%s>() wrote %d bytes\n", name.c_str(), written_total);
343     }
344 
345     return written_total;
346   }
347 
348   // The layout of a written string:
349   // [  4/8 bytes     ] length
350   // [ |length| bytes ] contents
WriteString(const std::string & data)351   size_t WriteString(const std::string& data) {
352     CHECK_GT(data.size(), 0);  // No empty strings should be written.
353     size_t written_total = Write<size_t>(data.size());
354     if (is_debug) {
355       std::string str = ToStr(data);
356       Debug("WriteString(), length=%zu: \"%s\"\n", data.size(), data.c_str());
357     }
358 
359     // Write the null-terminated string.
360     size_t length = data.size() + 1;
361     sink.insert(sink.end(), data.c_str(), data.c_str() + length);
362     written_total += length;
363 
364     if (is_debug) {
365       Debug("WriteString() wrote %zu bytes\n", written_total);
366     }
367 
368     return written_total;
369   }
370 
371  private:
372   // Helper for writing an array of numeric types.
373   template <typename T>
Write(const T * data,size_t count)374   size_t Write(const T* data, size_t count) {
375     DCHECK_GT(count, 0);  // Should not write contents for vectors of size 0.
376     if (is_debug) {
377       std::string str =
378           "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
379       std::string name = GetName<T>();
380       Debug("Write<%s>() (%zu-byte), count=%zu: %s",
381             name.c_str(),
382             sizeof(T),
383             count,
384             str.c_str());
385     }
386 
387     size_t size = sizeof(T) * count;
388     const char* pos = reinterpret_cast<const char*>(data);
389     sink.insert(sink.end(), pos, pos + size);
390 
391     if (is_debug) {
392       Debug(", wrote %zu bytes\n", size);
393     }
394     return size;
395   }
396 
397   // Helper for writing numeric vectors.
398   template <typename Number>
WriteVector(const std::vector<Number> & data,std::true_type)399   size_t WriteVector(const std::vector<Number>& data, std::true_type) {
400     return Write(data.data(), data.size());
401   }
402 
403   // Helper for writing non-numeric vectors.
404   template <typename T>
WriteVector(const std::vector<T> & data,std::false_type)405   size_t WriteVector(const std::vector<T>& data, std::false_type) {
406     DCHECK_GT(data.size(),
407               0);  // Should not write contents for vectors of size 0.
408     size_t written_total = 0;
409     bool original_is_debug = is_debug;
410     is_debug = original_is_debug && !std::is_same_v<T, std::string>;
411     for (size_t i = 0; i < data.size(); ++i) {
412       if (is_debug) {
413         Debug("\n[%d] ", i);
414       }
415       written_total += Write<T>(data[i]);
416     }
417     is_debug = original_is_debug;
418 
419     return written_total;
420   }
421 };
422 
423 // Layout of serialized std::string:
424 // [  4/8 bytes     ] length
425 // [ |length| bytes ] contents
426 template <>
Read()427 std::string SnapshotDeserializer::Read() {
428   return ReadString();
429 }
430 template <>
Write(const std::string & data)431 size_t SnapshotSerializer::Write(const std::string& data) {
432   return WriteString(data);
433 }
434 
435 // Layout of v8::StartupData
436 // [  4/8 bytes       ] raw_size
437 // [ |raw_size| bytes ] contents
438 template <>
Read()439 v8::StartupData SnapshotDeserializer::Read() {
440   Debug("Read<v8::StartupData>()\n");
441 
442   int raw_size = Read<int>();
443   Debug("size=%d\n", raw_size);
444 
445   CHECK_GT(raw_size, 0);  // There should be no startup data of size 0.
446   // The data pointer of v8::StartupData would be deleted so it must be new'ed.
447   std::unique_ptr<char> buf = std::unique_ptr<char>(new char[raw_size]);
448   Read<char>(buf.get(), raw_size);
449 
450   return v8::StartupData{buf.release(), raw_size};
451 }
452 
453 template <>
Write(const v8::StartupData & data)454 size_t SnapshotSerializer::Write(const v8::StartupData& data) {
455   Debug("\nWrite<v8::StartupData>() size=%d\n", data.raw_size);
456 
457   CHECK_GT(data.raw_size, 0);  // There should be no startup data of size 0.
458   size_t written_total = Write<int>(data.raw_size);
459   written_total += Write<char>(data.data, static_cast<size_t>(data.raw_size));
460 
461   Debug("Write<v8::StartupData>() wrote %d bytes\n\n", written_total);
462   return written_total;
463 }
464 
465 // Layout of builtins::CodeCacheInfo
466 // [  4/8 bytes ]  length of the module id string
467 // [    ...     ]  |length| bytes of module id
468 // [  4/8 bytes ]  length of module code cache
469 // [    ...     ]  |length| bytes of module code cache
470 template <>
Read()471 builtins::CodeCacheInfo SnapshotDeserializer::Read() {
472   Debug("Read<builtins::CodeCacheInfo>()\n");
473 
474   builtins::CodeCacheInfo result{ReadString(), ReadVector<uint8_t>()};
475 
476   if (is_debug) {
477     std::string str = ToStr(result);
478     Debug("Read<builtins::CodeCacheInfo>() %s\n", str.c_str());
479   }
480   return result;
481 }
482 
483 template <>
Write(const builtins::CodeCacheInfo & data)484 size_t SnapshotSerializer::Write(const builtins::CodeCacheInfo& data) {
485   Debug("\nWrite<builtins::CodeCacheInfo>() id = %s"
486         ", size=%d\n",
487         data.id.c_str(),
488         data.data.size());
489 
490   size_t written_total = WriteString(data.id);
491   written_total += WriteVector<uint8_t>(data.data);
492 
493   Debug("Write<builtins::CodeCacheInfo>() wrote %d bytes\n", written_total);
494   return written_total;
495 }
496 
497 // Layout of PropInfo
498 // [ 4/8 bytes ]  length of the data name string
499 // [    ...    ]  |length| bytes of data name
500 // [  4 bytes  ]  index in the PropInfo vector
501 // [ 4/8 bytes ]  index in the snapshot blob, can be used with
502 //                GetDataFromSnapshotOnce().
503 template <>
Read()504 PropInfo SnapshotDeserializer::Read() {
505   Debug("Read<PropInfo>()\n");
506 
507   PropInfo result;
508   result.name = ReadString();
509   result.id = Read<uint32_t>();
510   result.index = Read<SnapshotIndex>();
511 
512   if (is_debug) {
513     std::string str = ToStr(result);
514     Debug("Read<PropInfo>() %s\n", str.c_str());
515   }
516 
517   return result;
518 }
519 
520 template <>
Write(const PropInfo & data)521 size_t SnapshotSerializer::Write(const PropInfo& data) {
522   if (is_debug) {
523     std::string str = ToStr(data);
524     Debug("Write<PropInfo>() %s\n", str.c_str());
525   }
526 
527   size_t written_total = WriteString(data.name);
528   written_total += Write<uint32_t>(data.id);
529   written_total += Write<SnapshotIndex>(data.index);
530 
531   Debug("Write<PropInfo>() wrote %d bytes\n", written_total);
532   return written_total;
533 }
534 
535 // Layout of AsyncHooks::SerializeInfo
536 // [ 4/8 bytes ]  snapshot index of async_ids_stack
537 // [ 4/8 bytes ]  snapshot index of fields
538 // [ 4/8 bytes ]  snapshot index of async_id_fields
539 // [ 4/8 bytes ]  snapshot index of js_execution_async_resources
540 // [ 4/8 bytes ]  length of native_execution_async_resources
541 // [   ...     ]  snapshot indices of each element in
542 //                native_execution_async_resources
543 template <>
Read()544 AsyncHooks::SerializeInfo SnapshotDeserializer::Read() {
545   Debug("Read<AsyncHooks::SerializeInfo>()\n");
546 
547   AsyncHooks::SerializeInfo result;
548   result.async_ids_stack = Read<AliasedBufferIndex>();
549   result.fields = Read<AliasedBufferIndex>();
550   result.async_id_fields = Read<AliasedBufferIndex>();
551   result.js_execution_async_resources = Read<SnapshotIndex>();
552   result.native_execution_async_resources = ReadVector<SnapshotIndex>();
553 
554   if (is_debug) {
555     std::string str = ToStr(result);
556     Debug("Read<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
557   }
558 
559   return result;
560 }
561 template <>
Write(const AsyncHooks::SerializeInfo & data)562 size_t SnapshotSerializer::Write(const AsyncHooks::SerializeInfo& data) {
563   if (is_debug) {
564     std::string str = ToStr(data);
565     Debug("Write<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
566   }
567 
568   size_t written_total = Write<AliasedBufferIndex>(data.async_ids_stack);
569   written_total += Write<AliasedBufferIndex>(data.fields);
570   written_total += Write<AliasedBufferIndex>(data.async_id_fields);
571   written_total += Write<SnapshotIndex>(data.js_execution_async_resources);
572   written_total +=
573       WriteVector<SnapshotIndex>(data.native_execution_async_resources);
574 
575   Debug("Write<AsyncHooks::SerializeInfo>() wrote %d bytes\n", written_total);
576   return written_total;
577 }
578 
579 // Layout of TickInfo::SerializeInfo
580 // [ 4/8 bytes ]  snapshot index of fields
581 template <>
Read()582 TickInfo::SerializeInfo SnapshotDeserializer::Read() {
583   Debug("Read<TickInfo::SerializeInfo>()\n");
584 
585   TickInfo::SerializeInfo result;
586   result.fields = Read<AliasedBufferIndex>();
587 
588   if (is_debug) {
589     std::string str = ToStr(result);
590     Debug("Read<TickInfo::SerializeInfo>() %s\n", str.c_str());
591   }
592 
593   return result;
594 }
595 
596 template <>
Write(const TickInfo::SerializeInfo & data)597 size_t SnapshotSerializer::Write(const TickInfo::SerializeInfo& data) {
598   if (is_debug) {
599     std::string str = ToStr(data);
600     Debug("Write<TickInfo::SerializeInfo>() %s\n", str.c_str());
601   }
602 
603   size_t written_total = Write<AliasedBufferIndex>(data.fields);
604 
605   Debug("Write<TickInfo::SerializeInfo>() wrote %d bytes\n", written_total);
606   return written_total;
607 }
608 
609 // Layout of TickInfo::SerializeInfo
610 // [ 4/8 bytes ]  snapshot index of fields
611 template <>
Read()612 ImmediateInfo::SerializeInfo SnapshotDeserializer::Read() {
613   per_process::Debug(DebugCategory::MKSNAPSHOT,
614                      "Read<ImmediateInfo::SerializeInfo>()\n");
615 
616   ImmediateInfo::SerializeInfo result;
617   result.fields = Read<AliasedBufferIndex>();
618   if (is_debug) {
619     std::string str = ToStr(result);
620     Debug("Read<ImmediateInfo::SerializeInfo>() %s\n", str.c_str());
621   }
622   return result;
623 }
624 
625 template <>
Write(const ImmediateInfo::SerializeInfo & data)626 size_t SnapshotSerializer::Write(const ImmediateInfo::SerializeInfo& data) {
627   if (is_debug) {
628     std::string str = ToStr(data);
629     Debug("Write<ImmediateInfo::SerializeInfo>() %s\n", str.c_str());
630   }
631 
632   size_t written_total = Write<AliasedBufferIndex>(data.fields);
633 
634   Debug("Write<ImmediateInfo::SerializeInfo>() wrote %d bytes\n",
635         written_total);
636   return written_total;
637 }
638 
639 // Layout of PerformanceState::SerializeInfo
640 // [ 4/8 bytes ]  snapshot index of root
641 // [ 4/8 bytes ]  snapshot index of milestones
642 // [ 4/8 bytes ]  snapshot index of observers
643 template <>
Read()644 performance::PerformanceState::SerializeInfo SnapshotDeserializer::Read() {
645   per_process::Debug(DebugCategory::MKSNAPSHOT,
646                      "Read<PerformanceState::SerializeInfo>()\n");
647 
648   performance::PerformanceState::SerializeInfo result;
649   result.root = Read<AliasedBufferIndex>();
650   result.milestones = Read<AliasedBufferIndex>();
651   result.observers = Read<AliasedBufferIndex>();
652   if (is_debug) {
653     std::string str = ToStr(result);
654     Debug("Read<PerformanceState::SerializeInfo>() %s\n", str.c_str());
655   }
656   return result;
657 }
658 
659 template <>
Write(const performance::PerformanceState::SerializeInfo & data)660 size_t SnapshotSerializer::Write(
661     const performance::PerformanceState::SerializeInfo& data) {
662   if (is_debug) {
663     std::string str = ToStr(data);
664     Debug("Write<PerformanceState::SerializeInfo>() %s\n", str.c_str());
665   }
666 
667   size_t written_total = Write<AliasedBufferIndex>(data.root);
668   written_total += Write<AliasedBufferIndex>(data.milestones);
669   written_total += Write<AliasedBufferIndex>(data.observers);
670 
671   Debug("Write<PerformanceState::SerializeInfo>() wrote %d bytes\n",
672         written_total);
673   return written_total;
674 }
675 
676 // Layout of IsolateDataSerializeInfo
677 // [ 4/8 bytes ]  length of primitive_values vector
678 // [    ...    ]  |length| of primitive_values indices
679 // [ 4/8 bytes ]  length of template_values vector
680 // [    ...    ]  |length| of PropInfo data
681 template <>
Read()682 IsolateDataSerializeInfo SnapshotDeserializer::Read() {
683   per_process::Debug(DebugCategory::MKSNAPSHOT,
684                      "Read<IsolateDataSerializeInfo>()\n");
685 
686   IsolateDataSerializeInfo result;
687   result.primitive_values = ReadVector<SnapshotIndex>();
688   result.template_values = ReadVector<PropInfo>();
689   if (is_debug) {
690     std::string str = ToStr(result);
691     Debug("Read<IsolateDataSerializeInfo>() %s\n", str.c_str());
692   }
693   return result;
694 }
695 
696 template <>
Write(const IsolateDataSerializeInfo & data)697 size_t SnapshotSerializer::Write(const IsolateDataSerializeInfo& data) {
698   if (is_debug) {
699     std::string str = ToStr(data);
700     Debug("Write<IsolateDataSerializeInfo>() %s\n", str.c_str());
701   }
702 
703   size_t written_total = WriteVector<SnapshotIndex>(data.primitive_values);
704   written_total += WriteVector<PropInfo>(data.template_values);
705 
706   Debug("Write<IsolateDataSerializeInfo>() wrote %d bytes\n", written_total);
707   return written_total;
708 }
709 
710 template <>
Read()711 RealmSerializeInfo SnapshotDeserializer::Read() {
712   per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<RealmSerializeInfo>()\n");
713   RealmSerializeInfo result;
714   result.builtins = ReadVector<std::string>();
715   result.persistent_values = ReadVector<PropInfo>();
716   result.native_objects = ReadVector<PropInfo>();
717   result.context = Read<SnapshotIndex>();
718   return result;
719 }
720 
721 template <>
Write(const RealmSerializeInfo & data)722 size_t SnapshotSerializer::Write(const RealmSerializeInfo& data) {
723   if (is_debug) {
724     std::string str = ToStr(data);
725     Debug("\nWrite<RealmSerializeInfo>() %s\n", str.c_str());
726   }
727 
728   // Use += here to ensure order of evaluation.
729   size_t written_total = WriteVector<std::string>(data.builtins);
730   written_total += WriteVector<PropInfo>(data.persistent_values);
731   written_total += WriteVector<PropInfo>(data.native_objects);
732   written_total += Write<SnapshotIndex>(data.context);
733 
734   Debug("Write<RealmSerializeInfo>() wrote %d bytes\n", written_total);
735   return written_total;
736 }
737 
738 template <>
Read()739 EnvSerializeInfo SnapshotDeserializer::Read() {
740   per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n");
741   EnvSerializeInfo result;
742   result.async_hooks = Read<AsyncHooks::SerializeInfo>();
743   result.tick_info = Read<TickInfo::SerializeInfo>();
744   result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
745   result.timeout_info = Read<AliasedBufferIndex>();
746   result.performance_state =
747       Read<performance::PerformanceState::SerializeInfo>();
748   result.exiting = Read<AliasedBufferIndex>();
749   result.stream_base_state = Read<AliasedBufferIndex>();
750   result.should_abort_on_uncaught_toggle = Read<AliasedBufferIndex>();
751   result.principal_realm = Read<RealmSerializeInfo>();
752   return result;
753 }
754 
755 template <>
Write(const EnvSerializeInfo & data)756 size_t SnapshotSerializer::Write(const EnvSerializeInfo& data) {
757   if (is_debug) {
758     std::string str = ToStr(data);
759     Debug("\nWrite<EnvSerializeInfo>() %s\n", str.c_str());
760   }
761 
762   // Use += here to ensure order of evaluation.
763   size_t written_total = Write<AsyncHooks::SerializeInfo>(data.async_hooks);
764   written_total += Write<TickInfo::SerializeInfo>(data.tick_info);
765   written_total += Write<ImmediateInfo::SerializeInfo>(data.immediate_info);
766   written_total += Write<AliasedBufferIndex>(data.timeout_info);
767   written_total += Write<performance::PerformanceState::SerializeInfo>(
768       data.performance_state);
769   written_total += Write<AliasedBufferIndex>(data.exiting);
770   written_total += Write<AliasedBufferIndex>(data.stream_base_state);
771   written_total +=
772       Write<AliasedBufferIndex>(data.should_abort_on_uncaught_toggle);
773   written_total += Write<RealmSerializeInfo>(data.principal_realm);
774 
775   Debug("Write<EnvSerializeInfo>() wrote %d bytes\n", written_total);
776   return written_total;
777 }
778 
779 // Layout of SnapshotMetadata
780 // [  1 byte   ]  type of the snapshot
781 // [ 4/8 bytes ]  length of the node version string
782 // [    ...    ]  |length| bytes of node version
783 // [ 4/8 bytes ]  length of the node arch string
784 // [    ...    ]  |length| bytes of node arch
785 // [ 4/8 bytes ]  length of the node platform string
786 // [    ...    ]  |length| bytes of node platform
787 // [  4 bytes  ]  v8 cache version tag
788 template <>
Read()789 SnapshotMetadata SnapshotDeserializer::Read() {
790   per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n");
791 
792   SnapshotMetadata result;
793   result.type = static_cast<SnapshotMetadata::Type>(Read<uint8_t>());
794   result.node_version = ReadString();
795   result.node_arch = ReadString();
796   result.node_platform = ReadString();
797   result.v8_cache_version_tag = Read<uint32_t>();
798 
799   if (is_debug) {
800     std::string str = ToStr(result);
801     Debug("Read<SnapshotMetadata>() %s\n", str.c_str());
802   }
803   return result;
804 }
805 
806 template <>
Write(const SnapshotMetadata & data)807 size_t SnapshotSerializer::Write(const SnapshotMetadata& data) {
808   if (is_debug) {
809     std::string str = ToStr(data);
810     Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str());
811   }
812   size_t written_total = 0;
813   // We need the Node.js version, platform and arch to match because
814   // Node.js may perform synchronizations that are platform-specific and they
815   // can be changed in semver-patches.
816   Debug("Write snapshot type %" PRIu8 "\n", static_cast<uint8_t>(data.type));
817   written_total += Write<uint8_t>(static_cast<uint8_t>(data.type));
818   Debug("Write Node.js version %s\n", data.node_version.c_str());
819   written_total += WriteString(data.node_version);
820   Debug("Write Node.js arch %s\n", data.node_arch);
821   written_total += WriteString(data.node_arch);
822   Debug("Write Node.js platform %s\n", data.node_platform);
823   written_total += WriteString(data.node_platform);
824   Debug("Write V8 cached data version tag %" PRIx32 "\n",
825         data.v8_cache_version_tag);
826   written_total += Write<uint32_t>(data.v8_cache_version_tag);
827   return written_total;
828 }
829 
830 // Layout of the snapshot blob
831 // [   4 bytes    ]  kMagic
832 // [   4/8 bytes  ]  length of Node.js version string
833 // [    ...       ]  contents of Node.js version string
834 // [   4/8 bytes  ]  length of Node.js arch string
835 // [    ...       ]  contents of Node.js arch string
836 // [    ...       ]  v8_snapshot_blob_data from SnapshotCreator::CreateBlob()
837 // [    ...       ]  isolate_data_info
838 // [    ...       ]  env_info
839 // [    ...       ]  code_cache
840 
ToBlob(FILE * out) const841 void SnapshotData::ToBlob(FILE* out) const {
842   SnapshotSerializer w;
843   w.Debug("SnapshotData::ToBlob()\n");
844 
845   size_t written_total = 0;
846 
847   // Metadata
848   w.Debug("Write magic %" PRIx32 "\n", kMagic);
849   written_total += w.Write<uint32_t>(kMagic);
850   w.Debug("Write metadata\n");
851   written_total += w.Write<SnapshotMetadata>(metadata);
852 
853   written_total += w.Write<v8::StartupData>(v8_snapshot_blob_data);
854   w.Debug("Write isolate_data_indices\n");
855   written_total += w.Write<IsolateDataSerializeInfo>(isolate_data_info);
856   written_total += w.Write<EnvSerializeInfo>(env_info);
857   w.Debug("Write code_cache\n");
858   written_total += w.WriteVector<builtins::CodeCacheInfo>(code_cache);
859   size_t num_written = fwrite(w.sink.data(), w.sink.size(), 1, out);
860   CHECK_EQ(num_written, 1);
861   w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
862   CHECK_EQ(fflush(out), 0);
863 }
864 
FromBlob(SnapshotData * out,FILE * in)865 bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
866   CHECK_EQ(ftell(in), 0);
867   int err = fseek(in, 0, SEEK_END);
868   CHECK_EQ(err, 0);
869   size_t size = ftell(in);
870   CHECK_NE(size, static_cast<size_t>(-1L));
871   err = fseek(in, 0, SEEK_SET);
872   CHECK_EQ(err, 0);
873 
874   std::vector<char> sink(size);
875   size_t num_read = fread(sink.data(), size, 1, in);
876   CHECK_EQ(num_read, 1);
877 
878   SnapshotDeserializer r(sink);
879   r.Debug("SnapshotData::FromBlob()\n");
880 
881   DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
882 
883   // Metadata
884   uint32_t magic = r.Read<uint32_t>();
885   r.Debug("Read magic %" PRIx32 "\n", magic);
886   CHECK_EQ(magic, kMagic);
887   out->metadata = r.Read<SnapshotMetadata>();
888   r.Debug("Read metadata\n");
889   if (!out->Check()) {
890     return false;
891   }
892 
893   out->v8_snapshot_blob_data = r.Read<v8::StartupData>();
894   r.Debug("Read isolate_data_info\n");
895   out->isolate_data_info = r.Read<IsolateDataSerializeInfo>();
896   out->env_info = r.Read<EnvSerializeInfo>();
897   r.Debug("Read code_cache\n");
898   out->code_cache = r.ReadVector<builtins::CodeCacheInfo>();
899 
900   r.Debug("SnapshotData::FromBlob() read %d bytes\n", r.read_total);
901   return true;
902 }
903 
Check() const904 bool SnapshotData::Check() const {
905   if (metadata.node_version != per_process::metadata.versions.node) {
906     fprintf(stderr,
907             "Failed to load the startup snapshot because it was built with"
908             "Node.js version %s and the current Node.js version is %s.\n",
909             metadata.node_version.c_str(),
910             NODE_VERSION);
911     return false;
912   }
913 
914   if (metadata.node_arch != per_process::metadata.arch) {
915     fprintf(stderr,
916             "Failed to load the startup snapshot because it was built with"
917             "architecture %s and the architecture is %s.\n",
918             metadata.node_arch.c_str(),
919             NODE_ARCH);
920     return false;
921   }
922 
923   if (metadata.node_platform != per_process::metadata.platform) {
924     fprintf(stderr,
925             "Failed to load the startup snapshot because it was built with"
926             "platform %s and the current platform is %s.\n",
927             metadata.node_platform.c_str(),
928             NODE_PLATFORM);
929     return false;
930   }
931 
932   uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag();
933   if (metadata.v8_cache_version_tag != current_cache_version &&
934       metadata.type == SnapshotMetadata::Type::kFullyCustomized) {
935     // For now we only do this check for the customized snapshots - we know
936     // that the flags we use in the default snapshot are limited and safe
937     // enough so we can relax the constraints for it.
938     fprintf(stderr,
939             "Failed to load the startup snapshot because it was built with "
940             "a different version of V8 or with different V8 configurations.\n"
941             "Expected tag %" PRIx32 ", read %" PRIx32 "\n",
942             current_cache_version,
943             metadata.v8_cache_version_tag);
944     return false;
945   }
946 
947   // TODO(joyeecheung): check incompatible Node.js flags.
948   return true;
949 }
950 
~SnapshotData()951 SnapshotData::~SnapshotData() {
952   if (data_ownership == DataOwnership::kOwned &&
953       v8_snapshot_blob_data.data != nullptr) {
954     delete[] v8_snapshot_blob_data.data;
955   }
956 }
957 
958 template <typename T>
WriteVector(std::ostream * ss,const T * vec,size_t size)959 void WriteVector(std::ostream* ss, const T* vec, size_t size) {
960   for (size_t i = 0; i < size; i++) {
961     *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
962   }
963 }
964 
GetCodeCacheDefName(const std::string & id)965 static std::string GetCodeCacheDefName(const std::string& id) {
966   char buf[64] = {0};
967   size_t size = id.size();
968   CHECK_LT(size, sizeof(buf));
969   for (size_t i = 0; i < size; ++i) {
970     char ch = id[i];
971     buf[i] = (ch == '-' || ch == '/') ? '_' : ch;
972   }
973   return std::string(buf) + std::string("_cache_data");
974 }
975 
FormatSize(size_t size)976 static std::string FormatSize(size_t size) {
977   char buf[64] = {0};
978   if (size < 1024) {
979     snprintf(buf, sizeof(buf), "%.2fB", static_cast<double>(size));
980   } else if (size < 1024 * 1024) {
981     snprintf(buf, sizeof(buf), "%.2fKB", static_cast<double>(size / 1024));
982   } else {
983     snprintf(
984         buf, sizeof(buf), "%.2fMB", static_cast<double>(size / 1024 / 1024));
985   }
986   return buf;
987 }
988 
WriteStaticCodeCacheData(std::ostream * ss,const builtins::CodeCacheInfo & info)989 static void WriteStaticCodeCacheData(std::ostream* ss,
990                                      const builtins::CodeCacheInfo& info) {
991   *ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n";
992   WriteVector(ss, info.data.data(), info.data.size());
993   *ss << "};";
994 }
995 
WriteCodeCacheInitializer(std::ostream * ss,const std::string & id)996 static void WriteCodeCacheInitializer(std::ostream* ss, const std::string& id) {
997   std::string def_name = GetCodeCacheDefName(id);
998   *ss << "    { \"" << id << "\",\n";
999   *ss << "      {" << def_name << ",\n";
1000   *ss << "       " << def_name << " + arraysize(" << def_name << "),\n";
1001   *ss << "      }\n";
1002   *ss << "    },\n";
1003 }
1004 
FormatBlob(std::ostream & ss,const SnapshotData * data)1005 void FormatBlob(std::ostream& ss, const SnapshotData* data) {
1006   ss << R"(#include <cstddef>
1007 #include "env.h"
1008 #include "node_snapshot_builder.h"
1009 #include "v8.h"
1010 
1011 // This file is generated by tools/snapshot. Do not edit.
1012 
1013 namespace node {
1014 
1015 static const char v8_snapshot_blob_data[] = {
1016 )";
1017   WriteVector(&ss,
1018               data->v8_snapshot_blob_data.data,
1019               data->v8_snapshot_blob_data.raw_size);
1020   ss << R"(};
1021 
1022 static const int v8_snapshot_blob_size = )"
1023      << data->v8_snapshot_blob_data.raw_size << ";";
1024 
1025   // Windows can't deal with too many large vector initializers.
1026   // Store the data into static arrays first.
1027   for (const auto& item : data->code_cache) {
1028     WriteStaticCodeCacheData(&ss, item);
1029   }
1030 
1031   ss << R"(const SnapshotData snapshot_data {
1032   // -- data_ownership begins --
1033   SnapshotData::DataOwnership::kNotOwned,
1034   // -- data_ownership ends --
1035   // -- metadata begins --
1036 )" << data->metadata
1037      << R"(,
1038   // -- metadata ends --
1039   // -- v8_snapshot_blob_data begins --
1040   { v8_snapshot_blob_data, v8_snapshot_blob_size },
1041   // -- v8_snapshot_blob_data ends --
1042   // -- isolate_data_info begins --
1043 )" << data->isolate_data_info
1044      << R"(
1045   // -- isolate_data_info ends --
1046   ,
1047   // -- env_info begins --
1048 )" << data->env_info
1049      << R"(
1050   // -- env_info ends --
1051   ,
1052   // -- code_cache begins --
1053   {)";
1054   for (const auto& item : data->code_cache) {
1055     WriteCodeCacheInitializer(&ss, item.id);
1056   }
1057   ss << R"(
1058   }
1059   // -- code_cache ends --
1060 };
1061 
1062 const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() {
1063   return &snapshot_data;
1064 }
1065 }  // namespace node
1066 )";
1067 }
1068 
1069 // Reset context settings that need to be initialized again after
1070 // deserialization.
ResetContextSettingsBeforeSnapshot(Local<Context> context)1071 static void ResetContextSettingsBeforeSnapshot(Local<Context> context) {
1072   // Reset the AllowCodeGenerationFromStrings flag to true (default value) so
1073   // that it can be re-initialized with v8 flag
1074   // --disallow-code-generation-from-strings and recognized in
1075   // node::InitializeContextRuntime.
1076   context->AllowCodeGenerationFromStrings(true);
1077 }
1078 
CollectExternalReferences()1079 const std::vector<intptr_t>& SnapshotBuilder::CollectExternalReferences() {
1080   static auto registry = std::make_unique<ExternalReferenceRegistry>();
1081   return registry->external_references();
1082 }
1083 
InitializeIsolateParams(const SnapshotData * data,Isolate::CreateParams * params)1084 void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
1085                                               Isolate::CreateParams* params) {
1086   params->external_references = CollectExternalReferences().data();
1087   params->snapshot_blob =
1088       const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
1089 }
1090 
1091 // TODO(joyeecheung): share these exit code constants across the code base.
1092 constexpr int UNCAUGHT_EXCEPTION_ERROR = 1;
1093 constexpr int BOOTSTRAP_ERROR = 10;
1094 constexpr int SNAPSHOT_ERROR = 14;
1095 
Generate(SnapshotData * out,const std::vector<std::string> args,const std::vector<std::string> exec_args)1096 int SnapshotBuilder::Generate(SnapshotData* out,
1097                               const std::vector<std::string> args,
1098                               const std::vector<std::string> exec_args) {
1099   const std::vector<intptr_t>& external_references =
1100       CollectExternalReferences();
1101   Isolate* isolate = Isolate::Allocate();
1102   // Must be done before the SnapshotCreator creation so  that the
1103   // memory reducer can be initialized.
1104   per_process::v8_platform.Platform()->RegisterIsolate(isolate,
1105                                                        uv_default_loop());
1106 
1107   SnapshotCreator creator(isolate, external_references.data());
1108 
1109   isolate->SetCaptureStackTraceForUncaughtExceptions(
1110       true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
1111 
1112   Environment* env = nullptr;
1113   std::unique_ptr<NodeMainInstance> main_instance =
1114       NodeMainInstance::Create(isolate,
1115                                uv_default_loop(),
1116                                per_process::v8_platform.Platform(),
1117                                args,
1118                                exec_args);
1119 
1120   // The cleanups should be done in case of an early exit due to errors.
1121   auto cleanup = OnScopeLeave([&]() {
1122     // Must be done while the snapshot creator isolate is entered i.e. the
1123     // creator is still alive. The snapshot creator destructor will destroy
1124     // the isolate.
1125     if (env != nullptr) {
1126       FreeEnvironment(env);
1127     }
1128     main_instance->Dispose();
1129     per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
1130   });
1131 
1132   // It's only possible to be kDefault in node_mksnapshot.
1133   SnapshotMetadata::Type snapshot_type =
1134       per_process::cli_options->build_snapshot
1135           ? SnapshotMetadata::Type::kFullyCustomized
1136           : SnapshotMetadata::Type::kDefault;
1137 
1138   {
1139     HandleScope scope(isolate);
1140     TryCatch bootstrapCatch(isolate);
1141 
1142     auto print_Exception = OnScopeLeave([&]() {
1143       if (bootstrapCatch.HasCaught()) {
1144         PrintCaughtException(
1145             isolate, isolate->GetCurrentContext(), bootstrapCatch);
1146       }
1147     });
1148 
1149     // The default context with only things created by V8.
1150     Local<Context> default_context = Context::New(isolate);
1151 
1152     // The context used by the vm module.
1153     Local<Context> vm_context;
1154     {
1155       Local<ObjectTemplate> global_template =
1156           main_instance->isolate_data()->contextify_global_template();
1157       CHECK(!global_template.IsEmpty());
1158       if (!contextify::ContextifyContext::CreateV8Context(
1159                isolate, global_template, nullptr, nullptr)
1160                .ToLocal(&vm_context)) {
1161         return SNAPSHOT_ERROR;
1162       }
1163     }
1164 
1165     // The Node.js-specific context with primodials, can be used by workers
1166     // TODO(joyeecheung): investigate if this can be used by vm contexts
1167     // without breaking compatibility.
1168     Local<Context> base_context = NewContext(isolate);
1169     if (base_context.IsEmpty()) {
1170       return BOOTSTRAP_ERROR;
1171     }
1172     ResetContextSettingsBeforeSnapshot(base_context);
1173 
1174     Local<Context> main_context = NewContext(isolate);
1175     if (main_context.IsEmpty()) {
1176       return BOOTSTRAP_ERROR;
1177     }
1178     // Initialize the main instance context.
1179     {
1180       Context::Scope context_scope(main_context);
1181 
1182       // Create the environment.
1183       // It's not guaranteed that a context that goes through
1184       // v8_inspector::V8Inspector::contextCreated() is runtime-independent,
1185       // so do not start the inspector on the main context when building
1186       // the default snapshot.
1187       uint64_t env_flags = EnvironmentFlags::kDefaultFlags |
1188                            EnvironmentFlags::kNoCreateInspector;
1189 
1190       env = CreateEnvironment(main_instance->isolate_data(),
1191                               main_context,
1192                               args,
1193                               exec_args,
1194                               static_cast<EnvironmentFlags::Flags>(env_flags));
1195 
1196       // This already ran scripts in lib/internal/bootstrap/, if it fails return
1197       if (env == nullptr) {
1198         return BOOTSTRAP_ERROR;
1199       }
1200       // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
1201       // loaded via LoadEnvironment() to execute process.argv[1] as the entry
1202       // point (we currently only support this kind of entry point, but we
1203       // could also explore snapshotting other kinds of execution modes
1204       // in the future).
1205       if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) {
1206 #if HAVE_INSPECTOR
1207         // TODO(joyeecheung): move this before RunBootstrapping().
1208         env->InitializeInspector({});
1209 #endif
1210         if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) {
1211           return UNCAUGHT_EXCEPTION_ERROR;
1212         }
1213         // FIXME(joyeecheung): right now running the loop in the snapshot
1214         // builder seems to introduces inconsistencies in JS land that need to
1215         // be synchronized again after snapshot restoration.
1216         int exit_code = SpinEventLoop(env).FromMaybe(UNCAUGHT_EXCEPTION_ERROR);
1217         if (exit_code != 0) {
1218           return exit_code;
1219         }
1220       }
1221 
1222       if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1223         env->ForEachRealm([](Realm* realm) { realm->PrintInfoForSnapshot(); });
1224         printf("Environment = %p\n", env);
1225       }
1226 
1227       // Serialize the native states
1228       out->isolate_data_info =
1229           main_instance->isolate_data()->Serialize(&creator);
1230       out->env_info = env->Serialize(&creator);
1231 
1232 #ifdef NODE_USE_NODE_CODE_CACHE
1233       // Regenerate all the code cache.
1234       if (!env->builtin_loader()->CompileAllBuiltins(main_context)) {
1235         return UNCAUGHT_EXCEPTION_ERROR;
1236       }
1237       env->builtin_loader()->CopyCodeCache(&(out->code_cache));
1238       for (const auto& item : out->code_cache) {
1239         std::string size_str = FormatSize(item.data.size());
1240         per_process::Debug(DebugCategory::MKSNAPSHOT,
1241                            "Generated code cache for %d: %s\n",
1242                            item.id.c_str(),
1243                            size_str.c_str());
1244       }
1245 #endif
1246 
1247       ResetContextSettingsBeforeSnapshot(main_context);
1248     }
1249 
1250     // Global handles to the contexts can't be disposed before the
1251     // blob is created. So initialize all the contexts before adding them.
1252     // TODO(joyeecheung): figure out how to remove this restriction.
1253     creator.SetDefaultContext(default_context);
1254     size_t index = creator.AddContext(vm_context);
1255     CHECK_EQ(index, SnapshotData::kNodeVMContextIndex);
1256     index = creator.AddContext(base_context);
1257     CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
1258     index = creator.AddContext(main_context,
1259                                {SerializeNodeContextInternalFields, env});
1260     CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
1261   }
1262 
1263   // Must be out of HandleScope
1264   out->v8_snapshot_blob_data =
1265       creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep);
1266 
1267   // We must be able to rehash the blob when we restore it or otherwise
1268   // the hash seed would be fixed by V8, introducing a vulnerability.
1269   if (!out->v8_snapshot_blob_data.CanBeRehashed()) {
1270     return SNAPSHOT_ERROR;
1271   }
1272 
1273   out->metadata = SnapshotMetadata{snapshot_type,
1274                                    per_process::metadata.versions.node,
1275                                    per_process::metadata.arch,
1276                                    per_process::metadata.platform,
1277                                    v8::ScriptCompiler::CachedDataVersionTag()};
1278 
1279   // We cannot resurrect the handles from the snapshot, so make sure that
1280   // no handles are left open in the environment after the blob is created
1281   // (which should trigger a GC and close all handles that can be closed).
1282   bool queues_are_empty =
1283       env->req_wrap_queue()->IsEmpty() && env->handle_wrap_queue()->IsEmpty();
1284   if (!queues_are_empty ||
1285       per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1286     PrintLibuvHandleInformation(env->event_loop(), stderr);
1287   }
1288   if (!queues_are_empty) {
1289     return SNAPSHOT_ERROR;
1290   }
1291   return 0;
1292 }
1293 
Generate(std::ostream & out,const std::vector<std::string> args,const std::vector<std::string> exec_args)1294 int SnapshotBuilder::Generate(std::ostream& out,
1295                               const std::vector<std::string> args,
1296                               const std::vector<std::string> exec_args) {
1297   SnapshotData data;
1298   int exit_code = Generate(&data, args, exec_args);
1299   if (exit_code != 0) {
1300     return exit_code;
1301   }
1302   FormatBlob(out, &data);
1303   return exit_code;
1304 }
1305 
SnapshotableObject(Realm * realm,Local<Object> wrap,EmbedderObjectType type)1306 SnapshotableObject::SnapshotableObject(Realm* realm,
1307                                        Local<Object> wrap,
1308                                        EmbedderObjectType type)
1309     : BaseObject(realm, wrap), type_(type) {}
1310 
GetTypeName() const1311 std::string SnapshotableObject::GetTypeName() const {
1312   switch (type_) {
1313 #define V(PropertyName, NativeTypeName)                                        \
1314   case EmbedderObjectType::k_##PropertyName: {                                 \
1315     return #NativeTypeName;                                                    \
1316   }
1317     SERIALIZABLE_OBJECT_TYPES(V)
1318 #undef V
1319     default: { UNREACHABLE(); }
1320   }
1321 }
1322 
DeserializeNodeInternalFields(Local<Object> holder,int index,StartupData payload,void * env)1323 void DeserializeNodeInternalFields(Local<Object> holder,
1324                                    int index,
1325                                    StartupData payload,
1326                                    void* env) {
1327   if (payload.raw_size == 0) {
1328     holder->SetAlignedPointerInInternalField(index, nullptr);
1329     return;
1330   }
1331   per_process::Debug(DebugCategory::MKSNAPSHOT,
1332                      "Deserialize internal field %d of %p, size=%d\n",
1333                      static_cast<int>(index),
1334                      (*holder),
1335                      static_cast<int>(payload.raw_size));
1336 
1337   if (payload.raw_size == 0) {
1338     holder->SetAlignedPointerInInternalField(index, nullptr);
1339     return;
1340   }
1341 
1342   DCHECK_EQ(index, BaseObject::kEmbedderType);
1343 
1344   Environment* env_ptr = static_cast<Environment*>(env);
1345   const InternalFieldInfoBase* info =
1346       reinterpret_cast<const InternalFieldInfoBase*>(payload.data);
1347   // TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
1348   // beginning of every InternalFieldInfoBase to ensure that we don't
1349   // step on payloads that were not serialized by Node.js.
1350   switch (info->type) {
1351 #define V(PropertyName, NativeTypeName)                                        \
1352   case EmbedderObjectType::k_##PropertyName: {                                 \
1353     per_process::Debug(DebugCategory::MKSNAPSHOT,                              \
1354                        "Object %p is %s\n",                                    \
1355                        (*holder),                                              \
1356                        #NativeTypeName);                                       \
1357     env_ptr->EnqueueDeserializeRequest(                                        \
1358         NativeTypeName::Deserialize,                                           \
1359         holder,                                                                \
1360         index,                                                                 \
1361         info->Copy<NativeTypeName::InternalFieldInfo>());                      \
1362     break;                                                                     \
1363   }
1364     SERIALIZABLE_OBJECT_TYPES(V)
1365 #undef V
1366     default: {
1367       // This should only be reachable during development when trying to
1368       // deserialize a snapshot blob built by a version of Node.js that
1369       // has more recognizable EmbedderObjectTypes than the deserializing
1370       // Node.js binary.
1371       fprintf(stderr,
1372               "Unknown embedder object type %" PRIu8 ", possibly caused by "
1373               "mismatched Node.js versions\n",
1374               static_cast<uint8_t>(info->type));
1375       ABORT();
1376     }
1377   }
1378 }
1379 
SerializeNodeContextInternalFields(Local<Object> holder,int index,void * env)1380 StartupData SerializeNodeContextInternalFields(Local<Object> holder,
1381                                                int index,
1382                                                void* env) {
1383   // We only do one serialization for the kEmbedderType slot, the result
1384   // contains everything necessary for deserializing the entire object,
1385   // including the fields whose index is bigger than kEmbedderType
1386   // (most importantly, BaseObject::kSlot).
1387   // For Node.js this design is enough for all the native binding that are
1388   // serializable.
1389   if (index != BaseObject::kEmbedderType) {
1390     return StartupData{nullptr, 0};
1391   }
1392 
1393   void* type_ptr = holder->GetAlignedPointerFromInternalField(index);
1394   if (type_ptr == nullptr) {
1395     return StartupData{nullptr, 0};
1396   }
1397 
1398   uint16_t type = *(static_cast<uint16_t*>(type_ptr));
1399   per_process::Debug(DebugCategory::MKSNAPSHOT, "type = 0x%x\n", type);
1400   if (type != kNodeEmbedderId) {
1401     return StartupData{nullptr, 0};
1402   }
1403 
1404   per_process::Debug(DebugCategory::MKSNAPSHOT,
1405                      "Serialize internal field, index=%d, holder=%p\n",
1406                      static_cast<int>(index),
1407                      *holder);
1408 
1409   void* native_ptr =
1410       holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
1411   per_process::Debug(DebugCategory::MKSNAPSHOT, "native = %p\n", native_ptr);
1412   DCHECK(static_cast<BaseObject*>(native_ptr)->is_snapshotable());
1413   SnapshotableObject* obj = static_cast<SnapshotableObject*>(native_ptr);
1414 
1415   per_process::Debug(DebugCategory::MKSNAPSHOT,
1416                      "Object %p is %s, ",
1417                      *holder,
1418                      obj->GetTypeName());
1419   InternalFieldInfoBase* info = obj->Serialize(index);
1420 
1421   per_process::Debug(DebugCategory::MKSNAPSHOT,
1422                      "payload size=%d\n",
1423                      static_cast<int>(info->length));
1424   return StartupData{reinterpret_cast<const char*>(info),
1425                      static_cast<int>(info->length)};
1426 }
1427 
SerializeSnapshotableObjects(Realm * realm,SnapshotCreator * creator,RealmSerializeInfo * info)1428 void SerializeSnapshotableObjects(Realm* realm,
1429                                   SnapshotCreator* creator,
1430                                   RealmSerializeInfo* info) {
1431   HandleScope scope(realm->isolate());
1432   Local<Context> context = realm->context();
1433   uint32_t i = 0;
1434   realm->ForEachBaseObject([&](BaseObject* obj) {
1435     // If there are any BaseObjects that are not snapshotable left
1436     // during context serialization, V8 would crash due to unregistered
1437     // global handles and print detailed information about them.
1438     if (!obj->is_snapshotable()) {
1439       return;
1440     }
1441     SnapshotableObject* ptr = static_cast<SnapshotableObject*>(obj);
1442 
1443     std::string type_name = ptr->GetTypeName();
1444     per_process::Debug(DebugCategory::MKSNAPSHOT,
1445                        "Serialize snapshotable object %i (%p), "
1446                        "object=%p, type=%s\n",
1447                        static_cast<int>(i),
1448                        ptr,
1449                        *(ptr->object()),
1450                        type_name);
1451 
1452     if (ptr->PrepareForSerialization(context, creator)) {
1453       SnapshotIndex index = creator->AddData(context, obj->object());
1454       per_process::Debug(DebugCategory::MKSNAPSHOT,
1455                          "Serialized with index=%d\n",
1456                          static_cast<int>(index));
1457       info->native_objects.push_back({type_name, i, index});
1458     }
1459     i++;
1460   });
1461 }
1462 
1463 namespace mksnapshot {
1464 
CompileSerializeMain(const FunctionCallbackInfo<Value> & args)1465 void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
1466   CHECK(args[0]->IsString());
1467   Local<String> filename = args[0].As<String>();
1468   Local<String> source = args[1].As<String>();
1469   Isolate* isolate = args.GetIsolate();
1470   Local<Context> context = isolate->GetCurrentContext();
1471   ScriptOrigin origin(isolate, filename, 0, 0, true);
1472   // TODO(joyeecheung): do we need all of these? Maybe we would want a less
1473   // internal version of them.
1474   std::vector<Local<String>> parameters = {
1475       FIXED_ONE_BYTE_STRING(isolate, "require"),
1476       FIXED_ONE_BYTE_STRING(isolate, "__filename"),
1477       FIXED_ONE_BYTE_STRING(isolate, "__dirname"),
1478   };
1479   ScriptCompiler::Source script_source(source, origin);
1480   Local<Function> fn;
1481   if (ScriptCompiler::CompileFunction(context,
1482                                       &script_source,
1483                                       parameters.size(),
1484                                       parameters.data(),
1485                                       0,
1486                                       nullptr,
1487                                       ScriptCompiler::kNoCompileOptions)
1488           .ToLocal(&fn)) {
1489     args.GetReturnValue().Set(fn);
1490   }
1491 }
1492 
SetSerializeCallback(const FunctionCallbackInfo<Value> & args)1493 void SetSerializeCallback(const FunctionCallbackInfo<Value>& args) {
1494   Environment* env = Environment::GetCurrent(args);
1495   CHECK(env->snapshot_serialize_callback().IsEmpty());
1496   CHECK(args[0]->IsFunction());
1497   env->set_snapshot_serialize_callback(args[0].As<Function>());
1498 }
1499 
SetDeserializeCallback(const FunctionCallbackInfo<Value> & args)1500 void SetDeserializeCallback(const FunctionCallbackInfo<Value>& args) {
1501   Environment* env = Environment::GetCurrent(args);
1502   CHECK(env->snapshot_deserialize_callback().IsEmpty());
1503   CHECK(args[0]->IsFunction());
1504   env->set_snapshot_deserialize_callback(args[0].As<Function>());
1505 }
1506 
SetDeserializeMainFunction(const FunctionCallbackInfo<Value> & args)1507 void SetDeserializeMainFunction(const FunctionCallbackInfo<Value>& args) {
1508   Environment* env = Environment::GetCurrent(args);
1509   CHECK(env->snapshot_deserialize_main().IsEmpty());
1510   CHECK(args[0]->IsFunction());
1511   env->set_snapshot_deserialize_main(args[0].As<Function>());
1512 }
1513 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)1514 void Initialize(Local<Object> target,
1515                 Local<Value> unused,
1516                 Local<Context> context,
1517                 void* priv) {
1518   SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
1519   SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
1520   SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
1521   SetMethod(context,
1522             target,
1523             "setDeserializeMainFunction",
1524             SetDeserializeMainFunction);
1525 }
1526 
RegisterExternalReferences(ExternalReferenceRegistry * registry)1527 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1528   registry->Register(CompileSerializeMain);
1529   registry->Register(SetSerializeCallback);
1530   registry->Register(SetDeserializeCallback);
1531   registry->Register(SetDeserializeMainFunction);
1532 }
1533 }  // namespace mksnapshot
1534 }  // namespace node
1535 
1536 NODE_BINDING_CONTEXT_AWARE_INTERNAL(mksnapshot, node::mksnapshot::Initialize)
1537 NODE_BINDING_EXTERNAL_REFERENCE(mksnapshot,
1538                                 node::mksnapshot::RegisterExternalReferences)
1539