src: back snapshot I/O with a std::vector sink

PR-URL: https://github.com/nodejs/node/pull/46463
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
Joyee Cheung 2023-02-03 16:57:24 +01:00 committed by GitHub
parent 3aaeb309b3
commit a466feaca7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,6 +2,7 @@
#include "node_snapshotable.h" #include "node_snapshotable.h"
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <vector>
#include "base_object-inl.h" #include "base_object-inl.h"
#include "debug_utils-inl.h" #include "debug_utils-inl.h"
#include "env-inl.h" #include "env-inl.h"
@ -135,11 +136,10 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
return output; return output;
} }
class FileIO { class SnapshotSerializerDeserializer {
public: public:
explicit FileIO(FILE* file) SnapshotSerializerDeserializer()
: f(file), : is_debug(per_process::enabled_debug_list.enabled(
is_debug(per_process::enabled_debug_list.enabled(
DebugCategory::MKSNAPSHOT)) {} DebugCategory::MKSNAPSHOT)) {}
template <typename... Args> template <typename... Args>
@ -181,14 +181,14 @@ class FileIO {
return name; return name;
} }
FILE* f = nullptr;
bool is_debug = false; bool is_debug = false;
}; };
class FileReader : public FileIO { class SnapshotDeserializer : public SnapshotSerializerDeserializer {
public: public:
explicit FileReader(FILE* file) : FileIO(file) {} explicit SnapshotDeserializer(const std::vector<char>& s)
~FileReader() {} : SnapshotSerializerDeserializer(), sink(s) {}
~SnapshotDeserializer() {}
// Helper for reading numeric types. // Helper for reading numeric types.
template <typename T> template <typename T>
@ -233,19 +233,19 @@ class FileReader : public FileIO {
CHECK_GT(length, 0); // There should be no empty strings. CHECK_GT(length, 0); // There should be no empty strings.
MallocedBuffer<char> buf(length + 1); MallocedBuffer<char> buf(length + 1);
size_t r = fread(buf.data, 1, length + 1, f); memcpy(buf.data, sink.data() + read_total, length + 1);
CHECK_EQ(r, length + 1);
std::string result(buf.data, length); // This creates a copy of buf.data. std::string result(buf.data, length); // This creates a copy of buf.data.
if (is_debug) { if (is_debug) {
Debug("\"%s\", read %d bytes\n", result.c_str(), r); Debug("\"%s\", read %zu bytes\n", result.c_str(), length + 1);
} }
read_total += r; read_total += length + 1;
return result; return result;
} }
size_t read_total = 0; size_t read_total = 0;
const std::vector<char>& sink;
private: private:
// Helper for reading an array of numeric types. // Helper for reading an array of numeric types.
@ -258,15 +258,15 @@ class FileReader : public FileIO {
Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count); Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count);
} }
size_t r = fread(out, sizeof(T), count, f); size_t size = sizeof(T) * count;
CHECK_EQ(r, count); memcpy(out, sink.data() + read_total, size);
if (is_debug) { if (is_debug) {
std::string str = std::string str =
"{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }"); "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
Debug("%s, read %d bytes\n", str.c_str(), r); Debug("%s, read %zu bytes\n", str.c_str(), size);
} }
read_total += r; read_total += size;
} }
// Helper for reading numeric vectors. // Helper for reading numeric vectors.
@ -300,10 +300,15 @@ class FileReader : public FileIO {
} }
}; };
class FileWriter : public FileIO { class SnapshotSerializer : public SnapshotSerializerDeserializer {
public: public:
explicit FileWriter(FILE* file) : FileIO(file) {} SnapshotSerializer() : SnapshotSerializerDeserializer() {
~FileWriter() {} // Currently the snapshot blob built with an empty script is around 4MB.
// So use that as the default sink size.
sink.reserve(4 * 1024 * 1024);
}
~SnapshotSerializer() {}
std::vector<char> sink;
// Helper for writing numeric types. // Helper for writing numeric types.
template <typename T> template <typename T>
@ -349,15 +354,16 @@ class FileWriter : public FileIO {
size_t written_total = Write<size_t>(data.size()); size_t written_total = Write<size_t>(data.size());
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("WriteString(), length=%d: \"%s\"\n", data.size(), data.c_str()); Debug("WriteString(), length=%zu: \"%s\"\n", data.size(), data.c_str());
} }
size_t r = fwrite(data.c_str(), 1, data.size() + 1, f); // Write the null-terminated string.
CHECK_EQ(r, data.size() + 1); size_t length = data.size() + 1;
written_total += r; sink.insert(sink.end(), data.c_str(), data.c_str() + length);
written_total += length;
if (is_debug) { if (is_debug) {
Debug("WriteString() wrote %d bytes\n", written_total); Debug("WriteString() wrote %zu bytes\n", written_total);
} }
return written_total; return written_total;
@ -372,20 +378,21 @@ class FileWriter : public FileIO {
std::string str = std::string str =
"{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }"); "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
std::string name = GetName<T>(); std::string name = GetName<T>();
Debug("Write<%s>() (%d-byte), count=%d: %s", Debug("Write<%s>() (%zu-byte), count=%zu: %s",
name.c_str(), name.c_str(),
sizeof(T), sizeof(T),
count, count,
str.c_str()); str.c_str());
} }
size_t r = fwrite(data, sizeof(T), count, f); size_t size = sizeof(T) * count;
CHECK_EQ(r, count); const char* pos = reinterpret_cast<const char*>(data);
sink.insert(sink.end(), pos, pos + size);
if (is_debug) { if (is_debug) {
Debug(", wrote %d bytes\n", r); Debug(", wrote %zu bytes\n", size);
} }
return r; return size;
} }
// Helper for writing numeric vectors. // Helper for writing numeric vectors.
@ -418,11 +425,11 @@ class FileWriter : public FileIO {
// [ 4/8 bytes ] length // [ 4/8 bytes ] length
// [ |length| bytes ] contents // [ |length| bytes ] contents
template <> template <>
std::string FileReader::Read() { std::string SnapshotDeserializer::Read() {
return ReadString(); return ReadString();
} }
template <> template <>
size_t FileWriter::Write(const std::string& data) { size_t SnapshotSerializer::Write(const std::string& data) {
return WriteString(data); return WriteString(data);
} }
@ -430,7 +437,7 @@ size_t FileWriter::Write(const std::string& data) {
// [ 4/8 bytes ] raw_size // [ 4/8 bytes ] raw_size
// [ |raw_size| bytes ] contents // [ |raw_size| bytes ] contents
template <> template <>
v8::StartupData FileReader::Read() { v8::StartupData SnapshotDeserializer::Read() {
Debug("Read<v8::StartupData>()\n"); Debug("Read<v8::StartupData>()\n");
int raw_size = Read<int>(); int raw_size = Read<int>();
@ -445,7 +452,7 @@ v8::StartupData FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const v8::StartupData& data) { size_t SnapshotSerializer::Write(const v8::StartupData& data) {
Debug("\nWrite<v8::StartupData>() size=%d\n", data.raw_size); Debug("\nWrite<v8::StartupData>() size=%d\n", data.raw_size);
CHECK_GT(data.raw_size, 0); // There should be no startup data of size 0. CHECK_GT(data.raw_size, 0); // There should be no startup data of size 0.
@ -462,7 +469,7 @@ size_t FileWriter::Write(const v8::StartupData& data) {
// [ 4/8 bytes ] length of module code cache // [ 4/8 bytes ] length of module code cache
// [ ... ] |length| bytes of module code cache // [ ... ] |length| bytes of module code cache
template <> template <>
builtins::CodeCacheInfo FileReader::Read() { builtins::CodeCacheInfo SnapshotDeserializer::Read() {
Debug("Read<builtins::CodeCacheInfo>()\n"); Debug("Read<builtins::CodeCacheInfo>()\n");
builtins::CodeCacheInfo result{ReadString(), ReadVector<uint8_t>()}; builtins::CodeCacheInfo result{ReadString(), ReadVector<uint8_t>()};
@ -475,7 +482,7 @@ builtins::CodeCacheInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const builtins::CodeCacheInfo& data) { size_t SnapshotSerializer::Write(const builtins::CodeCacheInfo& data) {
Debug("\nWrite<builtins::CodeCacheInfo>() id = %s" Debug("\nWrite<builtins::CodeCacheInfo>() id = %s"
", size=%d\n", ", size=%d\n",
data.id.c_str(), data.id.c_str(),
@ -495,7 +502,7 @@ size_t FileWriter::Write(const builtins::CodeCacheInfo& data) {
// [ 4/8 bytes ] index in the snapshot blob, can be used with // [ 4/8 bytes ] index in the snapshot blob, can be used with
// GetDataFromSnapshotOnce(). // GetDataFromSnapshotOnce().
template <> template <>
PropInfo FileReader::Read() { PropInfo SnapshotDeserializer::Read() {
Debug("Read<PropInfo>()\n"); Debug("Read<PropInfo>()\n");
PropInfo result; PropInfo result;
@ -512,7 +519,7 @@ PropInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const PropInfo& data) { size_t SnapshotSerializer::Write(const PropInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("Write<PropInfo>() %s\n", str.c_str()); Debug("Write<PropInfo>() %s\n", str.c_str());
@ -535,7 +542,7 @@ size_t FileWriter::Write(const PropInfo& data) {
// [ ... ] snapshot indices of each element in // [ ... ] snapshot indices of each element in
// native_execution_async_resources // native_execution_async_resources
template <> template <>
AsyncHooks::SerializeInfo FileReader::Read() { AsyncHooks::SerializeInfo SnapshotDeserializer::Read() {
Debug("Read<AsyncHooks::SerializeInfo>()\n"); Debug("Read<AsyncHooks::SerializeInfo>()\n");
AsyncHooks::SerializeInfo result; AsyncHooks::SerializeInfo result;
@ -553,7 +560,7 @@ AsyncHooks::SerializeInfo FileReader::Read() {
return result; return result;
} }
template <> template <>
size_t FileWriter::Write(const AsyncHooks::SerializeInfo& data) { size_t SnapshotSerializer::Write(const AsyncHooks::SerializeInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("Write<AsyncHooks::SerializeInfo>() %s\n", str.c_str()); Debug("Write<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
@ -573,7 +580,7 @@ size_t FileWriter::Write(const AsyncHooks::SerializeInfo& data) {
// Layout of TickInfo::SerializeInfo // Layout of TickInfo::SerializeInfo
// [ 4/8 bytes ] snapshot index of fields // [ 4/8 bytes ] snapshot index of fields
template <> template <>
TickInfo::SerializeInfo FileReader::Read() { TickInfo::SerializeInfo SnapshotDeserializer::Read() {
Debug("Read<TickInfo::SerializeInfo>()\n"); Debug("Read<TickInfo::SerializeInfo>()\n");
TickInfo::SerializeInfo result; TickInfo::SerializeInfo result;
@ -588,7 +595,7 @@ TickInfo::SerializeInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const TickInfo::SerializeInfo& data) { size_t SnapshotSerializer::Write(const TickInfo::SerializeInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("Write<TickInfo::SerializeInfo>() %s\n", str.c_str()); Debug("Write<TickInfo::SerializeInfo>() %s\n", str.c_str());
@ -603,7 +610,7 @@ size_t FileWriter::Write(const TickInfo::SerializeInfo& data) {
// Layout of TickInfo::SerializeInfo // Layout of TickInfo::SerializeInfo
// [ 4/8 bytes ] snapshot index of fields // [ 4/8 bytes ] snapshot index of fields
template <> template <>
ImmediateInfo::SerializeInfo FileReader::Read() { ImmediateInfo::SerializeInfo SnapshotDeserializer::Read() {
per_process::Debug(DebugCategory::MKSNAPSHOT, per_process::Debug(DebugCategory::MKSNAPSHOT,
"Read<ImmediateInfo::SerializeInfo>()\n"); "Read<ImmediateInfo::SerializeInfo>()\n");
@ -617,7 +624,7 @@ ImmediateInfo::SerializeInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const ImmediateInfo::SerializeInfo& data) { size_t SnapshotSerializer::Write(const ImmediateInfo::SerializeInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("Write<ImmediateInfo::SerializeInfo>() %s\n", str.c_str()); Debug("Write<ImmediateInfo::SerializeInfo>() %s\n", str.c_str());
@ -635,7 +642,7 @@ size_t FileWriter::Write(const ImmediateInfo::SerializeInfo& data) {
// [ 4/8 bytes ] snapshot index of milestones // [ 4/8 bytes ] snapshot index of milestones
// [ 4/8 bytes ] snapshot index of observers // [ 4/8 bytes ] snapshot index of observers
template <> template <>
performance::PerformanceState::SerializeInfo FileReader::Read() { performance::PerformanceState::SerializeInfo SnapshotDeserializer::Read() {
per_process::Debug(DebugCategory::MKSNAPSHOT, per_process::Debug(DebugCategory::MKSNAPSHOT,
"Read<PerformanceState::SerializeInfo>()\n"); "Read<PerformanceState::SerializeInfo>()\n");
@ -651,7 +658,7 @@ performance::PerformanceState::SerializeInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write( size_t SnapshotSerializer::Write(
const performance::PerformanceState::SerializeInfo& data) { const performance::PerformanceState::SerializeInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
@ -673,7 +680,7 @@ size_t FileWriter::Write(
// [ 4/8 bytes ] length of template_values vector // [ 4/8 bytes ] length of template_values vector
// [ ... ] |length| of PropInfo data // [ ... ] |length| of PropInfo data
template <> template <>
IsolateDataSerializeInfo FileReader::Read() { IsolateDataSerializeInfo SnapshotDeserializer::Read() {
per_process::Debug(DebugCategory::MKSNAPSHOT, per_process::Debug(DebugCategory::MKSNAPSHOT,
"Read<IsolateDataSerializeInfo>()\n"); "Read<IsolateDataSerializeInfo>()\n");
@ -688,7 +695,7 @@ IsolateDataSerializeInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const IsolateDataSerializeInfo& data) { size_t SnapshotSerializer::Write(const IsolateDataSerializeInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("Write<IsolateDataSerializeInfo>() %s\n", str.c_str()); Debug("Write<IsolateDataSerializeInfo>() %s\n", str.c_str());
@ -702,7 +709,7 @@ size_t FileWriter::Write(const IsolateDataSerializeInfo& data) {
} }
template <> template <>
RealmSerializeInfo FileReader::Read() { RealmSerializeInfo SnapshotDeserializer::Read() {
per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<RealmSerializeInfo>()\n"); per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<RealmSerializeInfo>()\n");
RealmSerializeInfo result; RealmSerializeInfo result;
result.builtins = ReadVector<std::string>(); result.builtins = ReadVector<std::string>();
@ -713,7 +720,7 @@ RealmSerializeInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const RealmSerializeInfo& data) { size_t SnapshotSerializer::Write(const RealmSerializeInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("\nWrite<RealmSerializeInfo>() %s\n", str.c_str()); Debug("\nWrite<RealmSerializeInfo>() %s\n", str.c_str());
@ -730,7 +737,7 @@ size_t FileWriter::Write(const RealmSerializeInfo& data) {
} }
template <> template <>
EnvSerializeInfo FileReader::Read() { EnvSerializeInfo SnapshotDeserializer::Read() {
per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n"); per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n");
EnvSerializeInfo result; EnvSerializeInfo result;
result.async_hooks = Read<AsyncHooks::SerializeInfo>(); result.async_hooks = Read<AsyncHooks::SerializeInfo>();
@ -747,7 +754,7 @@ EnvSerializeInfo FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const EnvSerializeInfo& data) { size_t SnapshotSerializer::Write(const EnvSerializeInfo& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("\nWrite<EnvSerializeInfo>() %s\n", str.c_str()); Debug("\nWrite<EnvSerializeInfo>() %s\n", str.c_str());
@ -780,7 +787,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
// [ ... ] |length| bytes of node platform // [ ... ] |length| bytes of node platform
// [ 4 bytes ] v8 cache version tag // [ 4 bytes ] v8 cache version tag
template <> template <>
SnapshotMetadata FileReader::Read() { SnapshotMetadata SnapshotDeserializer::Read() {
per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n"); per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n");
SnapshotMetadata result; SnapshotMetadata result;
@ -798,7 +805,7 @@ SnapshotMetadata FileReader::Read() {
} }
template <> template <>
size_t FileWriter::Write(const SnapshotMetadata& data) { size_t SnapshotSerializer::Write(const SnapshotMetadata& data) {
if (is_debug) { if (is_debug) {
std::string str = ToStr(data); std::string str = ToStr(data);
Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str()); Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str());
@ -833,7 +840,7 @@ size_t FileWriter::Write(const SnapshotMetadata& data) {
// [ ... ] code_cache // [ ... ] code_cache
void SnapshotData::ToBlob(FILE* out) const { void SnapshotData::ToBlob(FILE* out) const {
FileWriter w(out); SnapshotSerializer w;
w.Debug("SnapshotData::ToBlob()\n"); w.Debug("SnapshotData::ToBlob()\n");
size_t written_total = 0; size_t written_total = 0;
@ -850,11 +857,24 @@ void SnapshotData::ToBlob(FILE* out) const {
written_total += w.Write<EnvSerializeInfo>(env_info); written_total += w.Write<EnvSerializeInfo>(env_info);
w.Debug("Write code_cache\n"); w.Debug("Write code_cache\n");
written_total += w.WriteVector<builtins::CodeCacheInfo>(code_cache); written_total += w.WriteVector<builtins::CodeCacheInfo>(code_cache);
size_t num_written = fwrite(w.sink.data(), w.sink.size(), 1, out);
CHECK_EQ(num_written, 1);
w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total); w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
} }
bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) { bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
FileReader r(in); CHECK_EQ(ftell(in), 0);
int err = fseek(in, 0, SEEK_END);
CHECK_EQ(err, 0);
size_t size = ftell(in);
err = fseek(in, 0, SEEK_SET);
CHECK_EQ(err, 0);
std::vector<char> sink(size);
size_t num_read = fread(sink.data(), size, 1, in);
CHECK_EQ(num_read, 1);
SnapshotDeserializer r(sink);
r.Debug("SnapshotData::FromBlob()\n"); r.Debug("SnapshotData::FromBlob()\n");
DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned); DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);