@@ -679,6 +679,57 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) { |
679 | 679 | return written_total; |
680 | 680 | } |
681 | 681 |
|
| 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 | + |
682 | 733 | // Layout of the snapshot blob |
683 | 734 | // [ 4 bytes ] kMagic |
684 | 735 | // [ 4/8 bytes ] length of Node.js version string |
@@ -695,13 +746,12 @@ void SnapshotData::ToBlob(FILE* out) const { |
695 | 746 | w.Debug("SnapshotData::ToBlob()\n"); |
696 | 747 |
|
697 | 748 | size_t written_total = 0; |
| 749 | + |
698 | 750 | // Metadata |
699 | 751 | w.Debug("Write magic %" PRIx32 "\n", kMagic); |
700 | 752 | 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); |
705 | 755 |
|
706 | 756 | written_total += w.Write<v8::StartupData>(v8_snapshot_blob_data); |
707 | 757 | w.Debug("Write isolate_data_indices\n"); |
@@ -712,22 +762,22 @@ void SnapshotData::ToBlob(FILE* out) const { |
712 | 762 | w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total); |
713 | 763 | } |
714 | 764 |
|
715 | | -void SnapshotData::FromBlob(SnapshotData* out, FILE* in) { |
| 765 | +bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) { |
716 | 766 | FileReader r(in); |
717 | 767 | r.Debug("SnapshotData::FromBlob()\n"); |
718 | 768 |
|
| 769 | + DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned); |
| 770 | + |
719 | 771 | // Metadata |
720 | 772 | uint32_t magic = r.Read<uint32_t>(); |
721 | | - r.Debug("Read magic %" PRIx64 "\n", magic); |
| 773 | + r.Debug("Read magic %" PRIx32 "\n", magic); |
722 | 774 | 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 | + } |
729 | 780 |
|
730 | | - DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned); |
731 | 781 | out->v8_snapshot_blob_data = r.Read<v8::StartupData>(); |
732 | 782 | r.Debug("Read isolate_data_info\n"); |
733 | 783 | out->isolate_data_info = r.Read<IsolateDataSerializeInfo>(); |
@@ -736,6 +786,54 @@ void SnapshotData::FromBlob(SnapshotData* out, FILE* in) { |
736 | 786 | out->code_cache = r.ReadVector<builtins::CodeCacheInfo>(); |
737 | 787 |
|
738 | 788 | 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; |
739 | 837 | } |
740 | 838 |
|
741 | 839 | SnapshotData::~SnapshotData() { |
@@ -822,6 +920,10 @@ static const int v8_snapshot_blob_size = )" |
822 | 920 | // -- data_ownership begins -- |
823 | 921 | SnapshotData::DataOwnership::kNotOwned, |
824 | 922 | // -- data_ownership ends -- |
| 923 | + // -- metadata begins -- |
| 924 | +)" << data->metadata |
| 925 | + << R"(, |
| 926 | + // -- metadata ends -- |
825 | 927 | // -- v8_snapshot_blob_data begins -- |
826 | 928 | { v8_snapshot_blob_data, v8_snapshot_blob_size }, |
827 | 929 | // -- v8_snapshot_blob_data ends -- |
@@ -908,6 +1010,12 @@ int SnapshotBuilder::Generate(SnapshotData* out, |
908 | 1010 | per_process::v8_platform.Platform()->UnregisterIsolate(isolate); |
909 | 1011 | }); |
910 | 1012 |
|
| 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 | + |
911 | 1019 | { |
912 | 1020 | HandleScope scope(isolate); |
913 | 1021 | TryCatch bootstrapCatch(isolate); |
@@ -956,7 +1064,7 @@ int SnapshotBuilder::Generate(SnapshotData* out, |
956 | 1064 | // point (we currently only support this kind of entry point, but we |
957 | 1065 | // could also explore snapshotting other kinds of execution modes |
958 | 1066 | // in the future). |
959 | | - if (per_process::cli_options->build_snapshot) { |
| 1067 | + if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) { |
960 | 1068 | #if HAVE_INSPECTOR |
961 | 1069 | // TODO(joyeecheung): move this before RunBootstrapping(). |
962 | 1070 | env->InitializeInspector({}); |
@@ -1020,6 +1128,12 @@ int SnapshotBuilder::Generate(SnapshotData* out, |
1020 | 1128 | return SNAPSHOT_ERROR; |
1021 | 1129 | } |
1022 | 1130 |
|
| 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 | + |
1023 | 1137 | // We cannot resurrect the handles from the snapshot, so make sure that |
1024 | 1138 | // no handles are left open in the environment after the blob is created |
1025 | 1139 | // (which should trigger a GC and close all handles that can be closed). |
|