Skip to content

Commit e94ac97

Browse files
committed
src: support snapshot in single executable applications
1 parent a8c3d03 commit e94ac97

File tree

5 files changed

+65
-6
lines changed

5 files changed

+65
-6
lines changed

src/env.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,13 +493,17 @@ struct SnapshotMetadata {
493493

494494
struct SnapshotData {
495495
enum class DataOwnership { kOwned, kNotOwned };
496+
// TODO(joyeecheung): consider setting this in the blob. For now it's
497+
// only set at deserialization time.
498+
enum class IsForSea { kDefault, kIsSea };
496499

497500
static const uint32_t kMagic = 0x143da19;
498501
static const SnapshotIndex kNodeVMContextIndex = 0;
499502
static const SnapshotIndex kNodeBaseContextIndex = kNodeVMContextIndex + 1;
500503
static const SnapshotIndex kNodeMainContextIndex = kNodeBaseContextIndex + 1;
501504

502505
DataOwnership data_ownership = DataOwnership::kOwned;
506+
IsForSea is_for_sea = IsForSea::kDefault;
503507

504508
SnapshotMetadata metadata;
505509

@@ -525,10 +529,12 @@ struct SnapshotData {
525529
bool Check() const;
526530
static bool FromFile(SnapshotData* out, FILE* in);
527531
static bool FromBlob(SnapshotData* out, const std::vector<char>& in);
532+
static bool FromBlob(SnapshotData* out, std::string_view in);
528533
static const SnapshotData* FromEmbedderWrapper(
529534
const EmbedderSnapshotData* data);
530535
EmbedderSnapshotData::Pointer AsEmbedderWrapper() const;
531536

537+
static bool IsSnapshotBlob(std::string_view data);
532538
~SnapshotData();
533539
};
534540

src/node.cc

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,8 +1158,28 @@ ExitCode LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
11581158
ExitCode exit_code = result->exit_code_enum();
11591159
// nullptr indicates there's no snapshot data.
11601160
DCHECK_NULL(*snapshot_data_ptr);
1161-
// --snapshot-blob indicates that we are reading a customized snapshot.
1162-
if (!per_process::cli_options->snapshot_blob.empty()) {
1161+
1162+
bool has_cli_snapshot_blob = !per_process::cli_options->snapshot_blob.empty();
1163+
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
1164+
// TODO(joyeecheung): make the snapshot part of the blob, not **the** blob.
1165+
// https://github.com/nodejs/single-executable/discussions/58
1166+
if (sea::IsSingleExecutable()) {
1167+
const std::string_view sea_code = sea::FindSingleExecutableCode();
1168+
if (SnapshotData::IsSnapshotBlob(sea_code)) {
1169+
std::unique_ptr<SnapshotData> read_data =
1170+
std::make_unique<SnapshotData>();
1171+
if (SnapshotData::FromBlob(read_data.get(), sea_code)) {
1172+
read_data->is_for_sea = SnapshotData::IsForSea::kIsSea;
1173+
*snapshot_data_ptr = read_data.release();
1174+
} else {
1175+
fprintf(stderr, "Invalid snapshot data in single executable binary\n");
1176+
return ExitCode::kGenericUserError;
1177+
}
1178+
}
1179+
} else if (has_cli_snapshot_blob) {
1180+
#else
1181+
if (has_cli_snapshot_blob) {
1182+
#endif
11631183
std::string filename = per_process::cli_options->snapshot_blob;
11641184
FILE* fp = fopen(filename.c_str(), "rb");
11651185
if (fp == nullptr) {
@@ -1239,6 +1259,9 @@ static ExitCode StartInternal(int argc, char** argv) {
12391259
return GenerateAndWriteSnapshotData(&snapshot_data, result.get());
12401260
}
12411261

1262+
// TODO(joyeecheung): parse SEA config here and generate a blob that
1263+
// can be injected into SEA if a certain flag is set.
1264+
12421265
// Without --build-snapshot, we are in snapshot loading mode.
12431266
return LoadSnapshotDataAndRun(&snapshot_data, result.get());
12441267
}

src/node_main_instance.cc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,20 @@ void NodeMainInstance::Run(ExitCode* exit_code, Environment* env) {
8989
if (*exit_code == ExitCode::kNoFailure) {
9090
bool is_sea = false;
9191
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
92-
if (sea::IsSingleExecutable()) {
92+
if (snapshot_data_->is_for_sea == SnapshotData::IsForSea::kIsSea) {
93+
is_sea = true;
94+
if (env->snapshot_deserialize_main().IsEmpty()) {
95+
fprintf(
96+
stderr,
97+
"No deserialized main function found for the snapshot blob found"
98+
" in single executable binary.\n");
99+
*exit_code = ExitCode::kStartupSnapshotFailure;
100+
return;
101+
}
102+
// LoadEnvironment would load and run the deserialized snapshot main
103+
// function.
104+
LoadEnvironment(env, StartExecutionCallback{});
105+
} else if (sea::IsSingleExecutable()) {
93106
is_sea = true;
94107
LoadEnvironment(env, sea::FindSingleExecutableCode());
95108
}

src/node_sea.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace sea {
1414
bool IsSingleExecutable();
1515
std::string_view FindSingleExecutableCode();
1616
std::tuple<int, char**> FixupArgsForSEA(int argc, char** argv);
17-
17+
std::string_view FindSingleExecutableCode();
1818
} // namespace sea
1919
} // namespace node
2020

src/node_snapshotable.cc

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ class SnapshotSerializerDeserializer {
188188

189189
class SnapshotDeserializer : public SnapshotSerializerDeserializer {
190190
public:
191-
explicit SnapshotDeserializer(const std::vector<char>& s)
191+
explicit SnapshotDeserializer(const std::string_view s)
192192
: SnapshotSerializerDeserializer(), sink(s) {}
193193
~SnapshotDeserializer() {}
194194

@@ -247,7 +247,7 @@ class SnapshotDeserializer : public SnapshotSerializerDeserializer {
247247
}
248248

249249
size_t read_total = 0;
250-
const std::vector<char>& sink;
250+
const std::string_view sink;
251251

252252
private:
253253
// Helper for reading an array of numeric types.
@@ -884,10 +884,19 @@ bool SnapshotData::FromFile(SnapshotData* out, FILE* in) {
884884
}
885885

886886
bool SnapshotData::FromBlob(SnapshotData* out, const std::vector<char>& in) {
887+
return FromBlob(out, std::string_view(in.data(), in.size()));
888+
}
889+
890+
bool SnapshotData::FromBlob(SnapshotData* out, const std::string_view in) {
887891
SnapshotDeserializer r(in);
888892
r.Debug("SnapshotData::FromBlob()\n");
889893

890894
DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
895+
// For now we don't serialize this bit.
896+
// XXX(joyeecheung): should we require snapshots being injected into
897+
// SEA to be explicitly built for SEA, and check this bit at SEA
898+
// start up?
899+
DCHECK_EQ(out->is_for_sea, SnapshotData::IsForSea::kDefault);
891900

892901
// Metadata
893902
uint32_t magic = r.Read<uint32_t>();
@@ -910,6 +919,11 @@ bool SnapshotData::FromBlob(SnapshotData* out, const std::vector<char>& in) {
910919
return true;
911920
}
912921

922+
bool SnapshotData::IsSnapshotBlob(const std::string_view data) {
923+
const uint32_t* ptr = reinterpret_cast<const uint32_t*>(data.data());
924+
return (ptr[0] == kMagic);
925+
}
926+
913927
bool SnapshotData::Check() const {
914928
if (metadata.node_version != per_process::metadata.versions.node) {
915929
fprintf(stderr,
@@ -1041,6 +1055,9 @@ static const int v8_snapshot_blob_size = )"
10411055
// -- data_ownership begins --
10421056
SnapshotData::DataOwnership::kNotOwned,
10431057
// -- data_ownership ends --
1058+
// -- is_for_sea begins --
1059+
SnapshotData::IsForSea::kDefault,
1060+
// -- is_for_sea ends --
10441061
// -- metadata begins --
10451062
)" << data->metadata
10461063
<< R"(,

0 commit comments

Comments
 (0)