Skip to content

Commit

Permalink
tools: refactor snapshot builder
Browse files Browse the repository at this point in the history
This patch:

- Moves the snapshot building code to src/ so that we can reuse it
  later when generating custom snapshots from an entry point accepted
  by the node binary.
- Create a SnapshotData struct that incorporates all the data useful
  for a snapshot blob, including both the V8 data and the Node.js
  data.

PR-URL: #38902
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
  • Loading branch information
joyeecheung authored and targos committed Jun 11, 2021
1 parent 711916a commit 9bf9ddb
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 196 deletions.
2 changes: 0 additions & 2 deletions node.gyp
Expand Up @@ -1554,8 +1554,6 @@
'src/node_snapshot_stub.cc',
'src/node_code_cache_stub.cc',
'tools/snapshot/node_mksnapshot.cc',
'tools/snapshot/snapshot_builder.cc',
'tools/snapshot/snapshot_builder.h',
],

'conditions': [
Expand Down
7 changes: 7 additions & 0 deletions src/env.h
Expand Up @@ -955,6 +955,13 @@ struct EnvSerializeInfo {
friend std::ostream& operator<<(std::ostream& o, const EnvSerializeInfo& i);
};

struct SnapshotData {
SnapshotData() { blob.data = nullptr; }
v8::StartupData blob;
std::vector<size_t> isolate_data_indices;
EnvSerializeInfo env_info;
};

class Environment : public MemoryRetainer {
public:
Environment(const Environment&) = delete;
Expand Down
6 changes: 3 additions & 3 deletions src/node.cc
Expand Up @@ -1130,15 +1130,15 @@ int Start(int argc, char** argv) {

{
Isolate::CreateParams params;
const std::vector<size_t>* indexes = nullptr;
const std::vector<size_t>* indices = nullptr;
const EnvSerializeInfo* env_info = nullptr;
bool force_no_snapshot =
per_process::cli_options->per_isolate->no_node_snapshot;
if (!force_no_snapshot) {
v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob();
if (blob != nullptr) {
params.snapshot_blob = blob;
indexes = NodeMainInstance::GetIsolateDataIndexes();
indices = NodeMainInstance::GetIsolateDataIndices();
env_info = NodeMainInstance::GetEnvSerializeInfo();
}
}
Expand All @@ -1149,7 +1149,7 @@ int Start(int argc, char** argv) {
per_process::v8_platform.Platform(),
result.args,
result.exec_args,
indexes);
indices);
result.exit_code = main_instance.Run(env_info);
}

Expand Down
2 changes: 1 addition & 1 deletion src/node_main_instance.h
Expand Up @@ -67,7 +67,7 @@ class NodeMainInstance {

// If nullptr is returned, the binary is not built with embedded
// snapshot.
static const std::vector<size_t>* GetIsolateDataIndexes();
static const std::vector<size_t>* GetIsolateDataIndices();
static v8::StartupData* GetEmbeddedSnapshotBlob();
static const EnvSerializeInfo* GetEnvSerializeInfo();
static const std::vector<intptr_t>& CollectExternalReferences();
Expand Down
2 changes: 1 addition & 1 deletion src/node_snapshot_stub.cc
Expand Up @@ -10,7 +10,7 @@ v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() {
return nullptr;
}

const std::vector<size_t>* NodeMainInstance::GetIsolateDataIndexes() {
const std::vector<size_t>* NodeMainInstance::GetIsolateDataIndices() {
return nullptr;
}

Expand Down
165 changes: 165 additions & 0 deletions src/node_snapshotable.cc
@@ -1,16 +1,181 @@

#include "node_snapshotable.h"
#include <iostream>
#include <sstream>
#include "base_object-inl.h"
#include "debug_utils-inl.h"
#include "env-inl.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_file.h"
#include "node_internals.h"
#include "node_main_instance.h"
#include "node_v8.h"
#include "node_v8_platform-inl.h"

namespace node {

using v8::Context;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::SnapshotCreator;
using v8::StartupData;
using v8::TryCatch;
using v8::Value;

template <typename T>
void WriteVector(std::ostringstream* ss, const T* vec, size_t size) {
for (size_t i = 0; i < size; i++) {
*ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
}
}

std::string FormatBlob(SnapshotData* data) {
std::ostringstream ss;

ss << R"(#include <cstddef>
#include "env.h"
#include "node_main_instance.h"
#include "v8.h"
// This file is generated by tools/snapshot. Do not edit.
namespace node {
static const char blob_data[] = {
)";
WriteVector(&ss, data->blob.data, data->blob.raw_size);
ss << R"(};
static const int blob_size = )"
<< data->blob.raw_size << R"(;
static v8::StartupData blob = { blob_data, blob_size };
)";

ss << R"(v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() {
return &blob;
}
static const std::vector<size_t> isolate_data_indices {
)";
WriteVector(&ss,
data->isolate_data_indices.data(),
data->isolate_data_indices.size());
ss << R"(};
const std::vector<size_t>* NodeMainInstance::GetIsolateDataIndices() {
return &isolate_data_indices;
}
static const EnvSerializeInfo env_info )"
<< data->env_info << R"(;
const EnvSerializeInfo* NodeMainInstance::GetEnvSerializeInfo() {
return &env_info;
}
} // namespace node
)";

return ss.str();
}

void SnapshotBuilder::Generate(SnapshotData* out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args) {
Isolate* isolate = Isolate::Allocate();
isolate->SetCaptureStackTraceForUncaughtExceptions(
true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
per_process::v8_platform.Platform()->RegisterIsolate(isolate,
uv_default_loop());
std::unique_ptr<NodeMainInstance> main_instance;
std::string result;

{
const std::vector<intptr_t>& external_references =
NodeMainInstance::CollectExternalReferences();
SnapshotCreator creator(isolate, external_references.data());
Environment* env;
{
main_instance =
NodeMainInstance::Create(isolate,
uv_default_loop(),
per_process::v8_platform.Platform(),
args,
exec_args);

HandleScope scope(isolate);
creator.SetDefaultContext(Context::New(isolate));
out->isolate_data_indices =
main_instance->isolate_data()->Serialize(&creator);

// Run the per-context scripts
Local<Context> context;
{
TryCatch bootstrapCatch(isolate);
context = NewContext(isolate);
if (bootstrapCatch.HasCaught()) {
PrintCaughtException(isolate, context, bootstrapCatch);
abort();
}
}
Context::Scope context_scope(context);

// Create the environment
env = new Environment(main_instance->isolate_data(),
context,
args,
exec_args,
nullptr,
node::EnvironmentFlags::kDefaultFlags,
{});
// Run scripts in lib/internal/bootstrap/
{
TryCatch bootstrapCatch(isolate);
v8::MaybeLocal<Value> result = env->RunBootstrapping();
if (bootstrapCatch.HasCaught()) {
PrintCaughtException(isolate, context, bootstrapCatch);
}
result.ToLocalChecked();
}

if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
env->PrintAllBaseObjects();
printf("Environment = %p\n", env);
}

// Serialize the native states
out->env_info = env->Serialize(&creator);
// Serialize the context
size_t index = creator.AddContext(
context, {SerializeNodeContextInternalFields, env});
CHECK_EQ(index, NodeMainInstance::kNodeContextIndex);
}

// Must be out of HandleScope
out->blob =
creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
CHECK(out->blob.CanBeRehashed());
// Must be done while the snapshot creator isolate is entered i.e. the
// creator is still alive.
FreeEnvironment(env);
main_instance->Dispose();
}

per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
}

std::string SnapshotBuilder::Generate(
const std::vector<std::string> args,
const std::vector<std::string> exec_args) {
SnapshotData data;
Generate(&data, args, exec_args);
std::string result = FormatBlob(&data);
delete[] data.blob.data;
return result;
}

SnapshotableObject::SnapshotableObject(Environment* env,
Local<Object> wrap,
Expand Down
10 changes: 10 additions & 0 deletions src/node_snapshotable.h
Expand Up @@ -11,6 +11,7 @@ namespace node {

class Environment;
struct EnvSerializeInfo;
struct SnapshotData;

#define SERIALIZABLE_OBJECT_TYPES(V) \
V(fs_binding_data, fs::BindingData) \
Expand Down Expand Up @@ -119,6 +120,15 @@ void SerializeBindingData(Environment* env,
EnvSerializeInfo* info);

bool IsSnapshotableType(FastStringKey key);

class SnapshotBuilder {
public:
static std::string Generate(const std::vector<std::string> args,
const std::vector<std::string> exec_args);
static void Generate(SnapshotData* out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args);
};
} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
Expand Down
2 changes: 1 addition & 1 deletion tools/snapshot/README.md
Expand Up @@ -23,7 +23,7 @@ into the Node.js executable, `libnode` is first built with these unresolved
symbols:

- `node::NodeMainInstance::GetEmbeddedSnapshotBlob`
- `node::NodeMainInstance::GetIsolateDataIndexes`
- `node::NodeMainInstance::GetIsolateDataIndices`

Then the `node_mksnapshot` executable is built with C++ files in this
directory, as well as `src/node_snapshot_stub.cc` which defines the unresolved
Expand Down
2 changes: 1 addition & 1 deletion tools/snapshot/node_mksnapshot.cc
Expand Up @@ -7,7 +7,7 @@

#include "libplatform/libplatform.h"
#include "node_internals.h"
#include "snapshot_builder.h"
#include "node_snapshotable.h"
#include "util-inl.h"
#include "v8.h"

Expand Down

0 comments on commit 9bf9ddb

Please sign in to comment.