Skip to content

Commit

Permalink
tools: implement node_mksnapshot
Browse files Browse the repository at this point in the history
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 <anna@addaleax.net>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
joyeecheung authored and targos committed Apr 27, 2019
1 parent 631bea8 commit b44323f
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,8 @@ LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
tools/icu/*.h \
tools/code_cache/*.cc \
tools/code_cache/*.h \
tools/snapshot/*.cc \
tools/snapshot/*.h \
))

# Code blocks don't have newline at the end,
Expand Down
42 changes: 41 additions & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,47 @@
}],
],
}, # mkcodecache
], # end targets
{
'target_name': 'node_mksnapshot',
'type': 'executable',

'dependencies': [
'<(node_lib_target_name)',
'deps/histogram/histogram.gyp:histogram',
],

'includes': [
'node.gypi'
],

'include_dirs': [
'src',
'tools/msvs/genfiles',
'deps/v8/include',
'deps/cares/include',
'deps/uv/include',
],

'defines': [ 'NODE_WANT_INTERNALS=1' ],

'sources': [
'src/node_code_cache_stub.cc',
'tools/snapshot/node_mksnapshot.cc',
'tools/snapshot/snapshot_builder.cc',
'tools/snapshot/snapshot_builder.h',
],

'conditions': [
[ 'node_report=="true"', {
'conditions': [
['OS=="win"', {
'libraries': [ 'Ws2_32' ],
}],
],
}],
],
}, # node_mksnapshot
], # end targets

'conditions': [
['OS=="aix" and node_shared=="true"', {
Expand Down
51 changes: 51 additions & 0 deletions tools/snapshot/node_mksnapshot.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <cstdio>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include "libplatform/libplatform.h"
#include "node_internals.h"
#include "snapshot_builder.h"
#include "v8.h"

#ifdef _WIN32
#include <windows.h>

int wmain(int argc, wchar_t* argv[]) {
#else // UNIX
int main(int argc, char* argv[]) {
#endif // _WIN32

if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <path/to/output.cc>\n";
return 1;
}

std::ofstream out;
out.open(argv[1], std::ios::out | std::ios::binary);
if (!out.is_open()) {
std::cerr << "Cannot open " << argv[1] << "\n";
return 1;
}

int node_argc = 1;
char argv0[] = "node";
char* node_argv[] = {argv0, nullptr};

node::InitializationResult result =
node::InitializeOncePerProcess(node_argc, node_argv);
CHECK(!result.early_return);
CHECK_EQ(result.exit_code, 0);

{
std::string snapshot =
node::SnapshotBuilder::Generate(result.args, result.exec_args);
out << snapshot;
out.close();
}

node::TearDownOncePerProcess();
return 0;
}
122 changes: 122 additions & 0 deletions tools/snapshot/snapshot_builder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "snapshot_builder.h"
#include <iostream>
#include <sstream>
#include "env-inl.h"
#include "node_internals.h"
#include "node_main_instance.h"
#include "node_v8_platform-inl.h"

namespace node {

using v8::Context;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Locker;
using v8::SnapshotCreator;
using v8::StartupData;

std::string FormatBlob(v8::StartupData* blob,
const std::vector<size_t>& isolate_data_indexes) {
std::stringstream ss;
size_t isolate_data_indexes_size = isolate_data_indexes.size();

ss << R"(#include <cstddef>
#include "node_main_instance.h"
#include "v8.h"
// This file is generated by tools/snapshot. Do not edit.
namespace node {
static const uint8_t blob_data[] = {
)";

for (int i = 0; i < blob->raw_size; i++) {
uint8_t ch = blob->data[i];
ss << std::to_string(ch) << ((i == blob->raw_size - 1) ? '\n' : ',');
}

ss << R"(};
static const int blob_size = )"
<< blob->raw_size << R"(;
static v8::StartupData blob = {
reinterpret_cast<const char*>(blob_data),
blob_size
};
)";

ss << R"(v8::StartupData*
NodeMainInstance::GetEmbeddedSnapshotBlob() {
return &blob;
}
static const size_t isolate_data_indexes_raw[] = {
)";
for (size_t i = 0; i < isolate_data_indexes_size; i++) {
ss << std::to_string(isolate_data_indexes[i])
<< ((i == isolate_data_indexes_size - 1) ? '\n' : ',');
}
ss << "};\n\n";

ss << "static const size_t isolate_data_indexes_size = "
<< isolate_data_indexes_size << R"(;
NodeMainInstance::IndexArray isolate_data_indexes {
isolate_data_indexes_raw,
isolate_data_indexes_size
};
const NodeMainInstance::IndexArray*
NodeMainInstance::GetIsolateDataIndexes() {
return &isolate_data_indexes;
}
} // namespace node
)";

return ss.str();
}

std::string SnapshotBuilder::Generate(
const std::vector<std::string> args,
const std::vector<std::string> exec_args) {
// TODO(joyeecheung): collect external references and set it in
// params.external_references.
std::vector<intptr_t> external_references = {
reinterpret_cast<intptr_t>(nullptr)};
Isolate* isolate = Isolate::Allocate();
per_process::v8_platform.Platform()->RegisterIsolate(isolate,
uv_default_loop());
NodeMainInstance* main_instance = nullptr;
std::string result;

{
std::vector<size_t> isolate_data_indexes;
SnapshotCreator creator(isolate, external_references.data());
{
main_instance =
NodeMainInstance::Create(isolate,
uv_default_loop(),
per_process::v8_platform.Platform(),
args,
exec_args);
HandleScope scope(isolate);
creator.SetDefaultContext(Context::New(isolate));
isolate_data_indexes = main_instance->isolate_data()->Serialize(&creator);
}

// Must be out of HandleScope
StartupData blob =
creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
// Must be done while the snapshot creator isolate is entered i.e. the
// creator is still alive.
main_instance->Dispose();
result = FormatBlob(&blob, isolate_data_indexes);
delete blob.data;
}

per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
return result;
}
} // namespace node
15 changes: 15 additions & 0 deletions tools/snapshot/snapshot_builder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_
#define TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_

#include <string>
#include <vector>

namespace node {
class SnapshotBuilder {
public:
static std::string Generate(const std::vector<std::string> args,
const std::vector<std::string> exec_args);
};
} // namespace node

#endif // TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_

0 comments on commit b44323f

Please sign in to comment.