Skip to content

Commit b44323f

Browse files
joyeecheungtargos
authored andcommitted
tools: implement node_mksnapshot
Implements a node_mksnapshot target that generates a snapshot blob from a Node.js main instance's isolate, and serializes the data blob with other additional data into a C++ file that can be embedded into the Node.js binary. PR-URL: #27321 Refs: #17058 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 631bea8 commit b44323f

File tree

5 files changed

+231
-1
lines changed

5 files changed

+231
-1
lines changed

Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,8 @@ LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
12261226
tools/icu/*.h \
12271227
tools/code_cache/*.cc \
12281228
tools/code_cache/*.h \
1229+
tools/snapshot/*.cc \
1230+
tools/snapshot/*.h \
12291231
))
12301232

12311233
# Code blocks don't have newline at the end,

node.gyp

+41-1
Original file line numberDiff line numberDiff line change
@@ -1156,7 +1156,47 @@
11561156
}],
11571157
],
11581158
}, # mkcodecache
1159-
], # end targets
1159+
{
1160+
'target_name': 'node_mksnapshot',
1161+
'type': 'executable',
1162+
1163+
'dependencies': [
1164+
'<(node_lib_target_name)',
1165+
'deps/histogram/histogram.gyp:histogram',
1166+
],
1167+
1168+
'includes': [
1169+
'node.gypi'
1170+
],
1171+
1172+
'include_dirs': [
1173+
'src',
1174+
'tools/msvs/genfiles',
1175+
'deps/v8/include',
1176+
'deps/cares/include',
1177+
'deps/uv/include',
1178+
],
1179+
1180+
'defines': [ 'NODE_WANT_INTERNALS=1' ],
1181+
1182+
'sources': [
1183+
'src/node_code_cache_stub.cc',
1184+
'tools/snapshot/node_mksnapshot.cc',
1185+
'tools/snapshot/snapshot_builder.cc',
1186+
'tools/snapshot/snapshot_builder.h',
1187+
],
1188+
1189+
'conditions': [
1190+
[ 'node_report=="true"', {
1191+
'conditions': [
1192+
['OS=="win"', {
1193+
'libraries': [ 'Ws2_32' ],
1194+
}],
1195+
],
1196+
}],
1197+
],
1198+
}, # node_mksnapshot
1199+
], # end targets
11601200

11611201
'conditions': [
11621202
['OS=="aix" and node_shared=="true"', {

tools/snapshot/node_mksnapshot.cc

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <cstdio>
2+
#include <fstream>
3+
#include <iostream>
4+
#include <sstream>
5+
#include <string>
6+
#include <vector>
7+
8+
#include "libplatform/libplatform.h"
9+
#include "node_internals.h"
10+
#include "snapshot_builder.h"
11+
#include "v8.h"
12+
13+
#ifdef _WIN32
14+
#include <windows.h>
15+
16+
int wmain(int argc, wchar_t* argv[]) {
17+
#else // UNIX
18+
int main(int argc, char* argv[]) {
19+
#endif // _WIN32
20+
21+
if (argc < 2) {
22+
std::cerr << "Usage: " << argv[0] << " <path/to/output.cc>\n";
23+
return 1;
24+
}
25+
26+
std::ofstream out;
27+
out.open(argv[1], std::ios::out | std::ios::binary);
28+
if (!out.is_open()) {
29+
std::cerr << "Cannot open " << argv[1] << "\n";
30+
return 1;
31+
}
32+
33+
int node_argc = 1;
34+
char argv0[] = "node";
35+
char* node_argv[] = {argv0, nullptr};
36+
37+
node::InitializationResult result =
38+
node::InitializeOncePerProcess(node_argc, node_argv);
39+
CHECK(!result.early_return);
40+
CHECK_EQ(result.exit_code, 0);
41+
42+
{
43+
std::string snapshot =
44+
node::SnapshotBuilder::Generate(result.args, result.exec_args);
45+
out << snapshot;
46+
out.close();
47+
}
48+
49+
node::TearDownOncePerProcess();
50+
return 0;
51+
}

tools/snapshot/snapshot_builder.cc

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include "snapshot_builder.h"
2+
#include <iostream>
3+
#include <sstream>
4+
#include "env-inl.h"
5+
#include "node_internals.h"
6+
#include "node_main_instance.h"
7+
#include "node_v8_platform-inl.h"
8+
9+
namespace node {
10+
11+
using v8::Context;
12+
using v8::HandleScope;
13+
using v8::Isolate;
14+
using v8::Local;
15+
using v8::Locker;
16+
using v8::SnapshotCreator;
17+
using v8::StartupData;
18+
19+
std::string FormatBlob(v8::StartupData* blob,
20+
const std::vector<size_t>& isolate_data_indexes) {
21+
std::stringstream ss;
22+
size_t isolate_data_indexes_size = isolate_data_indexes.size();
23+
24+
ss << R"(#include <cstddef>
25+
#include "node_main_instance.h"
26+
#include "v8.h"
27+
28+
// This file is generated by tools/snapshot. Do not edit.
29+
30+
namespace node {
31+
32+
static const uint8_t blob_data[] = {
33+
)";
34+
35+
for (int i = 0; i < blob->raw_size; i++) {
36+
uint8_t ch = blob->data[i];
37+
ss << std::to_string(ch) << ((i == blob->raw_size - 1) ? '\n' : ',');
38+
}
39+
40+
ss << R"(};
41+
42+
static const int blob_size = )"
43+
<< blob->raw_size << R"(;
44+
static v8::StartupData blob = {
45+
reinterpret_cast<const char*>(blob_data),
46+
blob_size
47+
};
48+
)";
49+
50+
ss << R"(v8::StartupData*
51+
NodeMainInstance::GetEmbeddedSnapshotBlob() {
52+
return &blob;
53+
}
54+
55+
static const size_t isolate_data_indexes_raw[] = {
56+
)";
57+
for (size_t i = 0; i < isolate_data_indexes_size; i++) {
58+
ss << std::to_string(isolate_data_indexes[i])
59+
<< ((i == isolate_data_indexes_size - 1) ? '\n' : ',');
60+
}
61+
ss << "};\n\n";
62+
63+
ss << "static const size_t isolate_data_indexes_size = "
64+
<< isolate_data_indexes_size << R"(;
65+
66+
NodeMainInstance::IndexArray isolate_data_indexes {
67+
isolate_data_indexes_raw,
68+
isolate_data_indexes_size
69+
};
70+
71+
const NodeMainInstance::IndexArray*
72+
NodeMainInstance::GetIsolateDataIndexes() {
73+
return &isolate_data_indexes;
74+
}
75+
} // namespace node
76+
)";
77+
78+
return ss.str();
79+
}
80+
81+
std::string SnapshotBuilder::Generate(
82+
const std::vector<std::string> args,
83+
const std::vector<std::string> exec_args) {
84+
// TODO(joyeecheung): collect external references and set it in
85+
// params.external_references.
86+
std::vector<intptr_t> external_references = {
87+
reinterpret_cast<intptr_t>(nullptr)};
88+
Isolate* isolate = Isolate::Allocate();
89+
per_process::v8_platform.Platform()->RegisterIsolate(isolate,
90+
uv_default_loop());
91+
NodeMainInstance* main_instance = nullptr;
92+
std::string result;
93+
94+
{
95+
std::vector<size_t> isolate_data_indexes;
96+
SnapshotCreator creator(isolate, external_references.data());
97+
{
98+
main_instance =
99+
NodeMainInstance::Create(isolate,
100+
uv_default_loop(),
101+
per_process::v8_platform.Platform(),
102+
args,
103+
exec_args);
104+
HandleScope scope(isolate);
105+
creator.SetDefaultContext(Context::New(isolate));
106+
isolate_data_indexes = main_instance->isolate_data()->Serialize(&creator);
107+
}
108+
109+
// Must be out of HandleScope
110+
StartupData blob =
111+
creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
112+
// Must be done while the snapshot creator isolate is entered i.e. the
113+
// creator is still alive.
114+
main_instance->Dispose();
115+
result = FormatBlob(&blob, isolate_data_indexes);
116+
delete blob.data;
117+
}
118+
119+
per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
120+
return result;
121+
}
122+
} // namespace node

tools/snapshot/snapshot_builder.h

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_
2+
#define TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_
3+
4+
#include <string>
5+
#include <vector>
6+
7+
namespace node {
8+
class SnapshotBuilder {
9+
public:
10+
static std::string Generate(const std::vector<std::string> args,
11+
const std::vector<std::string> exec_args);
12+
};
13+
} // namespace node
14+
15+
#endif // TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_

0 commit comments

Comments
 (0)