Skip to content

Commit

Permalink
src: support snapshot in single executable applications
Browse files Browse the repository at this point in the history
  • Loading branch information
joyeecheung committed Feb 24, 2023
1 parent b3663e0 commit d830718
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 36 deletions.
2 changes: 2 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,10 +525,12 @@ struct SnapshotData {
bool Check() const;
static bool FromFile(SnapshotData* out, FILE* in);
static bool FromBlob(SnapshotData* out, const std::vector<char>& in);
static bool FromBlob(SnapshotData* out, std::string_view in);
static const SnapshotData* FromEmbedderWrapper(
const EmbedderSnapshotData* data);
EmbedderSnapshotData::Pointer AsEmbedderWrapper() const;

static bool IsSnapshotBlob(std::string_view data);
~SnapshotData();
};

Expand Down
58 changes: 44 additions & 14 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,31 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
}));
}

#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
if (sea::IsSingleExecutable()) {
const std::string_view sea_code = sea::FindSingleExecutableCode();
if (SnapshotData::IsSnapshotBlob(sea_code)) {
if (env->snapshot_deserialize_main().IsEmpty()) {
fprintf(
stderr,
"No deserialized main function found for the snapshot blob found"
" in single executable binary.\n");
return MaybeLocal<Value>();
} else {
return env->RunSnapshotDeserializeMain();
}
}

// TODO(addaleax): Find a way to reuse:
//
// LoadEnvironment(Environment*, const char*)
//
// instead and not add yet another main entry point here because this
// already duplicates existing code.
return StartExecution(env, "internal/main/single_executable_application");
}
#endif

// TODO(joyeecheung): move these conditions into JS land and let the
// deserialize main function take precedence. For workers, we need to
// move the pre-execution part into a different file that can be
Expand All @@ -312,18 +337,6 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
first_argv = env->argv()[1];
}

#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
if (sea::IsSingleExecutable()) {
// TODO(addaleax): Find a way to reuse:
//
// LoadEnvironment(Environment*, const char*)
//
// instead and not add yet another main entry point here because this
// already duplicates existing code.
return StartExecution(env, "internal/main/single_executable_application");
}
#endif

if (first_argv == "inspect") {
return StartExecution(env, "internal/main/inspect");
}
Expand Down Expand Up @@ -1177,8 +1190,25 @@ ExitCode LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
ExitCode exit_code = result->exit_code_enum();
// nullptr indicates there's no snapshot data.
DCHECK_NULL(*snapshot_data_ptr);
// --snapshot-blob indicates that we are reading a customized snapshot.
if (!per_process::cli_options->snapshot_blob.empty()) {

bool has_cli_snapshot_blob = !per_process::cli_options->snapshot_blob.empty();
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
if (sea::IsSingleExecutable()) {
const std::string_view sea_code = sea::FindSingleExecutableCode();
if (SnapshotData::IsSnapshotBlob(sea_code)) {
std::unique_ptr<SnapshotData> read_data =
std::make_unique<SnapshotData>();
if (SnapshotData::FromBlob(read_data.get(), sea_code)) {
*snapshot_data_ptr = read_data.release();
} else {
fprintf(stderr, "Invalid snapshot data in single executable binary\n");
return ExitCode::kGenericUserError;
}
}
} else if (has_cli_snapshot_blob) {
#else
if (has_cli_snapshot_blob) {
#endif
std::string filename = per_process::cli_options->snapshot_blob;
FILE* fp = fopen(filename.c_str(), "rb");
if (fp == nullptr) {
Expand Down
39 changes: 20 additions & 19 deletions src/node_sea.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,11 @@ using v8::Value;

namespace {

const std::string_view FindSingleExecutableCode() {
static const std::string_view sea_code = []() -> std::string_view {
size_t size;
#ifdef __APPLE__
postject_options options;
postject_options_init(&options);
options.macho_segment_name = "NODE_JS";
const char* code = static_cast<const char*>(
postject_find_resource("NODE_JS_CODE", &size, &options));
#else
const char* code = static_cast<const char*>(
postject_find_resource("NODE_JS_CODE", &size, nullptr));
#endif
return {code, size};
}();
return sea_code;
}

void GetSingleExecutableCode(const FunctionCallbackInfo<Value>& args) {
node::Environment* env = node::Environment::GetCurrent(args);

static const std::string_view sea_code = FindSingleExecutableCode();
static const std::string_view sea_code =
node::sea::FindSingleExecutableCode();

if (sea_code.empty()) {
return;
Expand Down Expand Up @@ -83,6 +66,24 @@ void GetSingleExecutableCode(const FunctionCallbackInfo<Value>& args) {
namespace node {
namespace sea {

const std::string_view FindSingleExecutableCode() {
static const std::string_view sea_code = []() -> std::string_view {
size_t size;
#ifdef __APPLE__
postject_options options;
postject_options_init(&options);
options.macho_segment_name = "NODE_JS";
const char* code = static_cast<const char*>(
postject_find_resource("NODE_JS_CODE", &size, &options));
#else
const char* code = static_cast<const char*>(
postject_find_resource("NODE_JS_CODE", &size, nullptr));
#endif
return {code, size};
}();
return sea_code;
}

bool IsSingleExecutable() {
return postject_has_resource();
}
Expand Down
3 changes: 2 additions & 1 deletion src/node_sea.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@

#if !defined(DISABLE_SINGLE_EXECUTABLE_APPLICATION)

#include <string_view>
#include <tuple>

namespace node {
namespace sea {

bool IsSingleExecutable();
std::tuple<int, char**> FixupArgsForSEA(int argc, char** argv);

const std::string_view FindSingleExecutableCode();
} // namespace sea
} // namespace node

Expand Down
13 changes: 11 additions & 2 deletions src/node_snapshotable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class SnapshotSerializerDeserializer {

class SnapshotDeserializer : public SnapshotSerializerDeserializer {
public:
explicit SnapshotDeserializer(const std::vector<char>& s)
explicit SnapshotDeserializer(const std::string_view s)
: SnapshotSerializerDeserializer(), sink(s) {}
~SnapshotDeserializer() {}

Expand Down Expand Up @@ -246,7 +246,7 @@ class SnapshotDeserializer : public SnapshotSerializerDeserializer {
}

size_t read_total = 0;
const std::vector<char>& sink;
const std::string_view sink;

private:
// Helper for reading an array of numeric types.
Expand Down Expand Up @@ -883,6 +883,10 @@ bool SnapshotData::FromFile(SnapshotData* out, FILE* in) {
}

bool SnapshotData::FromBlob(SnapshotData* out, const std::vector<char>& in) {
return FromBlob(out, std::string_view(in.data(), in.size()));
}

bool SnapshotData::FromBlob(SnapshotData* out, const std::string_view in) {
SnapshotDeserializer r(in);
r.Debug("SnapshotData::FromBlob()\n");

Expand All @@ -909,6 +913,11 @@ bool SnapshotData::FromBlob(SnapshotData* out, const std::vector<char>& in) {
return true;
}

bool SnapshotData::IsSnapshotBlob(const std::string_view data) {
const uint32_t* ptr = reinterpret_cast<const uint32_t*>(data.data());
return (ptr[0] == kMagic);
}

bool SnapshotData::Check() const {
if (metadata.node_version != per_process::metadata.versions.node) {
fprintf(stderr,
Expand Down

0 comments on commit d830718

Please sign in to comment.