@@ -679,6 +679,57 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
679679 return written_total;
680680}
681681
682+ // Layout of SnapshotMetadata
683+ // [ 1 byte ] type of the snapshot
684+ // [ 4/8 bytes ] length of the node version string
685+ // [ ... ] |length| bytes of node version
686+ // [ 4/8 bytes ] length of the node arch string
687+ // [ ... ] |length| bytes of node arch
688+ // [ 4/8 bytes ] length of the node platform string
689+ // [ ... ] |length| bytes of node platform
690+ // [ 4 bytes ] v8 cache version tag
691+ template <>
692+ SnapshotMetadata FileReader::Read() {
693+ per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n");
694+
695+ SnapshotMetadata result;
696+ result.type = static_cast<SnapshotMetadata::Type>(Read<uint8_t>());
697+ result.node_version = ReadString();
698+ result.node_arch = ReadString();
699+ result.node_platform = ReadString();
700+ result.v8_cache_version_tag = Read<uint32_t>();
701+
702+ if (is_debug) {
703+ std::string str = ToStr(result);
704+ Debug("Read<SnapshotMetadata>() %s\n", str.c_str());
705+ }
706+ return result;
707+ }
708+
709+ template <>
710+ size_t FileWriter::Write(const SnapshotMetadata& data) {
711+ if (is_debug) {
712+ std::string str = ToStr(data);
713+ Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str());
714+ }
715+ size_t written_total = 0;
716+ // We need the Node.js version, platform and arch to match because
717+ // Node.js may perform synchronizations that are platform-specific and they
718+ // can be changed in semver-patches.
719+ Debug("Write snapshot type %" PRIu8 "\n", static_cast<uint8_t>(data.type));
720+ written_total += Write<uint8_t>(static_cast<uint8_t>(data.type));
721+ Debug("Write Node.js version %s\n", data.node_version.c_str());
722+ written_total += WriteString(data.node_version);
723+ Debug("Write Node.js arch %s\n", data.node_arch);
724+ written_total += WriteString(data.node_arch);
725+ Debug("Write Node.js platform %s\n", data.node_platform);
726+ written_total += WriteString(data.node_platform);
727+ Debug("Write V8 cached data version tag %" PRIx32 "\n",
728+ data.v8_cache_version_tag);
729+ written_total += Write<uint32_t>(data.v8_cache_version_tag);
730+ return written_total;
731+ }
732+
682733// Layout of the snapshot blob
683734// [ 4 bytes ] kMagic
684735// [ 4/8 bytes ] length of Node.js version string
@@ -695,13 +746,12 @@ void SnapshotData::ToBlob(FILE* out) const {
695746 w.Debug("SnapshotData::ToBlob()\n");
696747
697748 size_t written_total = 0;
749+
698750 // Metadata
699751 w.Debug("Write magic %" PRIx32 "\n", kMagic);
700752 written_total += w.Write<uint32_t>(kMagic);
701- w.Debug("Write version %s\n", NODE_VERSION);
702- written_total += w.WriteString(NODE_VERSION);
703- w.Debug("Write arch %s\n", NODE_ARCH);
704- written_total += w.WriteString(NODE_ARCH);
753+ w.Debug("Write metadata\n");
754+ written_total += w.Write<SnapshotMetadata>(metadata);
705755
706756 written_total += w.Write<v8::StartupData>(v8_snapshot_blob_data);
707757 w.Debug("Write isolate_data_indices\n");
@@ -712,22 +762,22 @@ void SnapshotData::ToBlob(FILE* out) const {
712762 w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
713763}
714764
715- void SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
765+ bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
716766 FileReader r(in);
717767 r.Debug("SnapshotData::FromBlob()\n");
718768
769+ DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
770+
719771 // Metadata
720772 uint32_t magic = r.Read<uint32_t>();
721- r.Debug("Read magic %" PRIx64 "\n", magic);
773+ r.Debug("Read magic %" PRIx32 "\n", magic);
722774 CHECK_EQ(magic, kMagic);
723- std::string version = r.ReadString();
724- r.Debug("Read version %s\n", version.c_str());
725- CHECK_EQ(version, NODE_VERSION);
726- std::string arch = r.ReadString();
727- r.Debug("Read arch %s\n", arch.c_str());
728- CHECK_EQ(arch, NODE_ARCH);
775+ out->metadata = r.Read<SnapshotMetadata>();
776+ r.Debug("Read metadata\n");
777+ if (!out->Check()) {
778+ return false;
779+ }
729780
730- DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
731781 out->v8_snapshot_blob_data = r.Read<v8::StartupData>();
732782 r.Debug("Read isolate_data_info\n");
733783 out->isolate_data_info = r.Read<IsolateDataSerializeInfo>();
@@ -736,6 +786,54 @@ void SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
736786 out->code_cache = r.ReadVector<builtins::CodeCacheInfo>();
737787
738788 r.Debug("SnapshotData::FromBlob() read %d bytes\n", r.read_total);
789+ return true;
790+ }
791+
792+ bool SnapshotData::Check() const {
793+ if (metadata.node_version != per_process::metadata.versions.node) {
794+ fprintf(stderr,
795+ "Failed to load the startup snapshot because it was built with"
796+ "Node.js version %s and the current Node.js version is %s.\n",
797+ metadata.node_version.c_str(),
798+ NODE_VERSION);
799+ return false;
800+ }
801+
802+ if (metadata.node_arch != per_process::metadata.arch) {
803+ fprintf(stderr,
804+ "Failed to load the startup snapshot because it was built with"
805+ "architecture %s and the architecture is %s.\n",
806+ metadata.node_arch.c_str(),
807+ NODE_ARCH);
808+ return false;
809+ }
810+
811+ if (metadata.node_platform != per_process::metadata.platform) {
812+ fprintf(stderr,
813+ "Failed to load the startup snapshot because it was built with"
814+ "platform %s and the current platform is %s.\n",
815+ metadata.node_platform.c_str(),
816+ NODE_PLATFORM);
817+ return false;
818+ }
819+
820+ uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag();
821+ if (metadata.v8_cache_version_tag != current_cache_version &&
822+ metadata.type == SnapshotMetadata::Type::kFullyCustomized) {
823+ // For now we only do this check for the customized snapshots - we know
824+ // that the flags we use in the default snapshot are limited and safe
825+ // enough so we can relax the constraints for it.
826+ fprintf(stderr,
827+ "Failed to load the startup snapshot because it was built with "
828+ "a different version of V8 or with different V8 configurations.\n"
829+ "Expected tag %" PRIx32 ", read %" PRIx32 "\n",
830+ current_cache_version,
831+ metadata.v8_cache_version_tag);
832+ return false;
833+ }
834+
835+ // TODO(joyeecheung): check incompatible Node.js flags.
836+ return true;
739837}
740838
741839SnapshotData::~SnapshotData() {
@@ -822,6 +920,10 @@ static const int v8_snapshot_blob_size = )"
822920 // -- data_ownership begins --
823921 SnapshotData::DataOwnership::kNotOwned,
824922 // -- data_ownership ends --
923+ // -- metadata begins --
924+ )" << data->metadata
925+ << R"(,
926+ // -- metadata ends --
825927 // -- v8_snapshot_blob_data begins --
826928 { v8_snapshot_blob_data, v8_snapshot_blob_size },
827929 // -- v8_snapshot_blob_data ends --
@@ -908,6 +1010,12 @@ int SnapshotBuilder::Generate(SnapshotData* out,
9081010 per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
9091011 });
9101012
1013+ // It's only possible to be kDefault in node_mksnapshot.
1014+ SnapshotMetadata::Type snapshot_type =
1015+ per_process::cli_options->build_snapshot
1016+ ? SnapshotMetadata::Type::kFullyCustomized
1017+ : SnapshotMetadata::Type::kDefault;
1018+
9111019 {
9121020 HandleScope scope(isolate);
9131021 TryCatch bootstrapCatch(isolate);
@@ -956,7 +1064,7 @@ int SnapshotBuilder::Generate(SnapshotData* out,
9561064 // point (we currently only support this kind of entry point, but we
9571065 // could also explore snapshotting other kinds of execution modes
9581066 // in the future).
959- if (per_process::cli_options->build_snapshot ) {
1067+ if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized ) {
9601068#if HAVE_INSPECTOR
9611069 // TODO(joyeecheung): move this before RunBootstrapping().
9621070 env->InitializeInspector({});
@@ -1020,6 +1128,12 @@ int SnapshotBuilder::Generate(SnapshotData* out,
10201128 return SNAPSHOT_ERROR;
10211129 }
10221130
1131+ out->metadata = SnapshotMetadata{snapshot_type,
1132+ per_process::metadata.versions.node,
1133+ per_process::metadata.arch,
1134+ per_process::metadata.platform,
1135+ v8::ScriptCompiler::CachedDataVersionTag()};
1136+
10231137 // We cannot resurrect the handles from the snapshot, so make sure that
10241138 // no handles are left open in the environment after the blob is created
10251139 // (which should trigger a GC and close all handles that can be closed).
0 commit comments