@@ -681,6 +681,57 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) { |
681 | 681 | return written_total; |
682 | 682 | } |
683 | 683 |
|
| 684 | +// Layout of SnapshotMetadata |
| 685 | +// [ 1 byte ] type of the snapshot |
| 686 | +// [ 4/8 bytes ] length of the node version string |
| 687 | +// [ ... ] |length| bytes of node version |
| 688 | +// [ 4/8 bytes ] length of the node arch string |
| 689 | +// [ ... ] |length| bytes of node arch |
| 690 | +// [ 4/8 bytes ] length of the node platform string |
| 691 | +// [ ... ] |length| bytes of node platform |
| 692 | +// [ 4 bytes ] v8 cache version tag |
| 693 | +template <> |
| 694 | +SnapshotMetadata FileReader::Read() { |
| 695 | + per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n"); |
| 696 | + |
| 697 | + SnapshotMetadata result; |
| 698 | + result.type = static_cast<SnapshotMetadata::Type>(Read<uint8_t>()); |
| 699 | + result.node_version = ReadString(); |
| 700 | + result.node_arch = ReadString(); |
| 701 | + result.node_platform = ReadString(); |
| 702 | + result.v8_cache_version_tag = Read<uint32_t>(); |
| 703 | + |
| 704 | + if (is_debug) { |
| 705 | + std::string str = ToStr(result); |
| 706 | + Debug("Read<SnapshotMetadata>() %s\n", str.c_str()); |
| 707 | + } |
| 708 | + return result; |
| 709 | +} |
| 710 | + |
| 711 | +template <> |
| 712 | +size_t FileWriter::Write(const SnapshotMetadata& data) { |
| 713 | + if (is_debug) { |
| 714 | + std::string str = ToStr(data); |
| 715 | + Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str()); |
| 716 | + } |
| 717 | + size_t written_total = 0; |
| 718 | + // We need the Node.js version, platform and arch to match because |
| 719 | + // Node.js may perform synchronizations that are platform-specific and they |
| 720 | + // can be changed in semver-patches. |
| 721 | + Debug("Write snapshot type %" PRIu8 "\n", static_cast<uint8_t>(data.type)); |
| 722 | + written_total += Write<uint8_t>(static_cast<uint8_t>(data.type)); |
| 723 | + Debug("Write Node.js version %s\n", data.node_version.c_str()); |
| 724 | + written_total += WriteString(data.node_version); |
| 725 | + Debug("Write Node.js arch %s\n", data.node_arch); |
| 726 | + written_total += WriteString(data.node_arch); |
| 727 | + Debug("Write Node.js platform %s\n", data.node_platform); |
| 728 | + written_total += WriteString(data.node_platform); |
| 729 | + Debug("Write V8 cached data version tag %" PRIx32 "\n", |
| 730 | + data.v8_cache_version_tag); |
| 731 | + written_total += Write<uint32_t>(data.v8_cache_version_tag); |
| 732 | + return written_total; |
| 733 | +} |
| 734 | + |
684 | 735 | // Layout of the snapshot blob |
685 | 736 | // [ 4 bytes ] kMagic |
686 | 737 | // [ 4/8 bytes ] length of Node.js version string |
@@ -697,13 +748,12 @@ void SnapshotData::ToBlob(FILE* out) const { |
697 | 748 | w.Debug("SnapshotData::ToBlob()\n"); |
698 | 749 |
|
699 | 750 | size_t written_total = 0; |
| 751 | + |
700 | 752 | // Metadata |
701 | 753 | w.Debug("Write magic %" PRIx32 "\n", kMagic); |
702 | 754 | written_total += w.Write<uint32_t>(kMagic); |
703 | | - w.Debug("Write version %s\n", NODE_VERSION); |
704 | | - written_total += w.WriteString(NODE_VERSION); |
705 | | - w.Debug("Write arch %s\n", NODE_ARCH); |
706 | | - written_total += w.WriteString(NODE_ARCH); |
| 755 | + w.Debug("Write metadata\n"); |
| 756 | + written_total += w.Write<SnapshotMetadata>(metadata); |
707 | 757 |
|
708 | 758 | written_total += w.Write<v8::StartupData>(v8_snapshot_blob_data); |
709 | 759 | w.Debug("Write isolate_data_indices\n"); |
@@ -714,22 +764,22 @@ void SnapshotData::ToBlob(FILE* out) const { |
714 | 764 | w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total); |
715 | 765 | } |
716 | 766 |
|
717 | | -void SnapshotData::FromBlob(SnapshotData* out, FILE* in) { |
| 767 | +bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) { |
718 | 768 | FileReader r(in); |
719 | 769 | r.Debug("SnapshotData::FromBlob()\n"); |
720 | 770 |
|
| 771 | + DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned); |
| 772 | + |
721 | 773 | // Metadata |
722 | 774 | uint32_t magic = r.Read<uint32_t>(); |
723 | | - r.Debug("Read magic %" PRIx64 "\n", magic); |
| 775 | + r.Debug("Read magic %" PRIx32 "\n", magic); |
724 | 776 | CHECK_EQ(magic, kMagic); |
725 | | - std::string version = r.ReadString(); |
726 | | - r.Debug("Read version %s\n", version.c_str()); |
727 | | - CHECK_EQ(version, NODE_VERSION); |
728 | | - std::string arch = r.ReadString(); |
729 | | - r.Debug("Read arch %s\n", arch.c_str()); |
730 | | - CHECK_EQ(arch, NODE_ARCH); |
| 777 | + out->metadata = r.Read<SnapshotMetadata>(); |
| 778 | + r.Debug("Read metadata\n"); |
| 779 | + if (!out->Check()) { |
| 780 | + return false; |
| 781 | + } |
731 | 782 |
|
732 | | - DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned); |
733 | 783 | out->v8_snapshot_blob_data = r.Read<v8::StartupData>(); |
734 | 784 | r.Debug("Read isolate_data_info\n"); |
735 | 785 | out->isolate_data_info = r.Read<IsolateDataSerializeInfo>(); |
@@ -738,6 +788,54 @@ void SnapshotData::FromBlob(SnapshotData* out, FILE* in) { |
738 | 788 | out->code_cache = r.ReadVector<builtins::CodeCacheInfo>(); |
739 | 789 |
|
740 | 790 | r.Debug("SnapshotData::FromBlob() read %d bytes\n", r.read_total); |
| 791 | + return true; |
| 792 | +} |
| 793 | + |
| 794 | +bool SnapshotData::Check() const { |
| 795 | + if (metadata.node_version != per_process::metadata.versions.node) { |
| 796 | + fprintf(stderr, |
| 797 | + "Failed to load the startup snapshot because it was built with" |
| 798 | + "Node.js version %s and the current Node.js version is %s.\n", |
| 799 | + metadata.node_version.c_str(), |
| 800 | + NODE_VERSION); |
| 801 | + return false; |
| 802 | + } |
| 803 | + |
| 804 | + if (metadata.node_arch != per_process::metadata.arch) { |
| 805 | + fprintf(stderr, |
| 806 | + "Failed to load the startup snapshot because it was built with" |
| 807 | + "architecture %s and the architecture is %s.\n", |
| 808 | + metadata.node_arch.c_str(), |
| 809 | + NODE_ARCH); |
| 810 | + return false; |
| 811 | + } |
| 812 | + |
| 813 | + if (metadata.node_platform != per_process::metadata.platform) { |
| 814 | + fprintf(stderr, |
| 815 | + "Failed to load the startup snapshot because it was built with" |
| 816 | + "platform %s and the current platform is %s.\n", |
| 817 | + metadata.node_platform.c_str(), |
| 818 | + NODE_PLATFORM); |
| 819 | + return false; |
| 820 | + } |
| 821 | + |
| 822 | + uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag(); |
| 823 | + if (metadata.v8_cache_version_tag != current_cache_version && |
| 824 | + metadata.type == SnapshotMetadata::Type::kFullyCustomized) { |
| 825 | + // For now we only do this check for the customized snapshots - we know |
| 826 | + // that the flags we use in the default snapshot are limited and safe |
| 827 | + // enough so we can relax the constraints for it. |
| 828 | + fprintf(stderr, |
| 829 | + "Failed to load the startup snapshot because it was built with " |
| 830 | + "a different version of V8 or with different V8 configurations.\n" |
| 831 | + "Expected tag %" PRIx32 ", read %" PRIx32 "\n", |
| 832 | + current_cache_version, |
| 833 | + metadata.v8_cache_version_tag); |
| 834 | + return false; |
| 835 | + } |
| 836 | + |
| 837 | + // TODO(joyeecheung): check incompatible Node.js flags. |
| 838 | + return true; |
741 | 839 | } |
742 | 840 |
|
743 | 841 | SnapshotData::~SnapshotData() { |
@@ -824,6 +922,10 @@ static const int v8_snapshot_blob_size = )" |
824 | 922 | // -- data_ownership begins -- |
825 | 923 | SnapshotData::DataOwnership::kNotOwned, |
826 | 924 | // -- data_ownership ends -- |
| 925 | + // -- metadata begins -- |
| 926 | +)" << data->metadata |
| 927 | + << R"(, |
| 928 | + // -- metadata ends -- |
827 | 929 | // -- v8_snapshot_blob_data begins -- |
828 | 930 | { v8_snapshot_blob_data, v8_snapshot_blob_size }, |
829 | 931 | // -- v8_snapshot_blob_data ends -- |
@@ -920,6 +1022,12 @@ int SnapshotBuilder::Generate(SnapshotData* out, |
920 | 1022 | per_process::v8_platform.Platform()->UnregisterIsolate(isolate); |
921 | 1023 | }); |
922 | 1024 |
|
| 1025 | + // It's only possible to be kDefault in node_mksnapshot. |
| 1026 | + SnapshotMetadata::Type snapshot_type = |
| 1027 | + per_process::cli_options->build_snapshot |
| 1028 | + ? SnapshotMetadata::Type::kFullyCustomized |
| 1029 | + : SnapshotMetadata::Type::kDefault; |
| 1030 | + |
923 | 1031 | { |
924 | 1032 | HandleScope scope(isolate); |
925 | 1033 | TryCatch bootstrapCatch(isolate); |
@@ -982,7 +1090,7 @@ int SnapshotBuilder::Generate(SnapshotData* out, |
982 | 1090 | // point (we currently only support this kind of entry point, but we |
983 | 1091 | // could also explore snapshotting other kinds of execution modes |
984 | 1092 | // in the future). |
985 | | - if (per_process::cli_options->build_snapshot) { |
| 1093 | + if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) { |
986 | 1094 | #if HAVE_INSPECTOR |
987 | 1095 | // TODO(joyeecheung): move this before RunBootstrapping(). |
988 | 1096 | env->InitializeInspector({}); |
@@ -1050,6 +1158,12 @@ int SnapshotBuilder::Generate(SnapshotData* out, |
1050 | 1158 | return SNAPSHOT_ERROR; |
1051 | 1159 | } |
1052 | 1160 |
|
| 1161 | + out->metadata = SnapshotMetadata{snapshot_type, |
| 1162 | + per_process::metadata.versions.node, |
| 1163 | + per_process::metadata.arch, |
| 1164 | + per_process::metadata.platform, |
| 1165 | + v8::ScriptCompiler::CachedDataVersionTag()}; |
| 1166 | + |
1053 | 1167 | // We cannot resurrect the handles from the snapshot, so make sure that |
1054 | 1168 | // no handles are left open in the environment after the blob is created |
1055 | 1169 | // (which should trigger a GC and close all handles that can be closed). |
|