Skip to content

Commit fbe1478

Browse files
committedMay 17, 2022
bootstrap: include code cache in the embedded snapshot
Since V8 code cache encodes indices to the read-only space it is safer to make sure that the code cache is generated in the same heap used to generate the embdded snapshot. This patch merges the code cache builder into the snapshot builder and makes the code cache part of node::SnapshotData that is deserialized into the native module loader during bootstrap. PR-URL: nodejs#43023 Fixes: nodejs#31074 Refs: nodejs#35711 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent de0b6dc commit fbe1478

19 files changed

+216
-427
lines changed
 

‎.github/CODEOWNERS

-2
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,8 @@
107107

108108
/benchmark/misc/startup.js @nodejs/startup
109109
/src/node.cc @nodejs/startup
110-
/src/node_code_cache_stub.cc @nodejs/startup
111110
/src/node_native_module* @nodejs/startup
112111
/lib/internal/bootstrap/* @nodejs/startup
113-
/tools/code_cache/* @nodejs/startup
114112
/tools/snapshot/* @nodejs/startup
115113

116114
# V8

‎configure.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ def configure_node(o):
12491249
o['variables']['node_use_node_snapshot'] = b(
12501250
not cross_compiling and not options.shared)
12511251

1252-
if options.without_node_code_cache or options.node_builtin_modules_path:
1252+
if options.without_node_code_cache or options.without_node_snapshot or options.node_builtin_modules_path:
12531253
o['variables']['node_use_node_code_cache'] = 'false'
12541254
else:
12551255
# TODO(refack): fix this when implementing embedded code-cache when cross-compiling.

‎lib/internal/bootstrap/node.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,8 @@ const features = {
270270
tls_sni: hasOpenSSL,
271271
tls_ocsp: hasOpenSSL,
272272
tls: hasOpenSSL,
273-
// This needs to be dynamic because snapshot is built without code cache.
274-
// TODO(joyeecheung): https://github.com/nodejs/node/issues/31074
275-
// Make it possible to build snapshot with code cache
273+
// This needs to be dynamic because --no-node-snapshot disables the
274+
// code cache even if the binary is built with embedded code cache.
276275
get cached_builtins() {
277276
return nativeModule.hasCachedBuiltins();
278277
}

‎node.gyp

+11-95
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
'deps/undici/undici.js',
5656
],
5757
'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)',
58-
'mkcodecache_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mkcodecache<(EXECUTABLE_SUFFIX)',
5958
'conditions': [
6059
['GENERATOR == "ninja"', {
6160
'node_text_start_object_path': 'src/large_pages/node_text_start.node_text_start.o'
@@ -304,32 +303,7 @@
304303
},
305304
},
306305
}],
307-
['node_use_node_code_cache=="true"', {
308-
'dependencies': [
309-
'mkcodecache',
310-
],
311-
'actions': [
312-
{
313-
'action_name': 'run_mkcodecache',
314-
'process_outputs_as_sources': 1,
315-
'inputs': [
316-
'<(mkcodecache_exec)',
317-
],
318-
'outputs': [
319-
'<(SHARED_INTERMEDIATE_DIR)/node_code_cache.cc',
320-
],
321-
'action': [
322-
'<@(_inputs)',
323-
'<@(_outputs)',
324-
],
325-
},
326-
],
327-
}, {
328-
'sources': [
329-
'src/node_code_cache_stub.cc'
330-
],
331-
}],
332-
['node_use_node_snapshot=="true"', {
306+
['node_use_node_snapshot=="true"', {
333307
'dependencies': [
334308
'node_mksnapshot',
335309
],
@@ -737,7 +711,6 @@
737711
[ 'node_shared=="true"', {
738712
'sources': [
739713
'src/node_snapshot_stub.cc',
740-
'src/node_code_cache_stub.cc',
741714
]
742715
}],
743716
[ 'node_shared=="true" and node_module_version!="" and OS!="win"', {
@@ -747,6 +720,11 @@
747720
'@rpath/lib<(node_core_target_name).<(shlib_suffix)'
748721
},
749722
}],
723+
[ 'node_use_node_code_cache=="true"', {
724+
'defines': [
725+
'NODE_USE_NODE_CODE_CACHE=1',
726+
],
727+
}],
750728
['node_shared=="true" and OS=="aix"', {
751729
'product_name': 'node_base',
752730
}],
@@ -1147,7 +1125,6 @@
11471125
],
11481126
'sources': [
11491127
'src/node_snapshot_stub.cc',
1150-
'src/node_code_cache_stub.cc',
11511128
'test/fuzzers/fuzz_url.cc',
11521129
],
11531130
'conditions': [
@@ -1190,7 +1167,6 @@
11901167
],
11911168
'sources': [
11921169
'src/node_snapshot_stub.cc',
1193-
'src/node_code_cache_stub.cc',
11941170
'test/fuzzers/fuzz_env.cc',
11951171
],
11961172
'conditions': [
@@ -1240,7 +1216,6 @@
12401216

12411217
'sources': [
12421218
'src/node_snapshot_stub.cc',
1243-
'src/node_code_cache_stub.cc',
12441219
'test/cctest/node_test_fixture.cc',
12451220
'test/cctest/node_test_fixture.h',
12461221
'test/cctest/test_aliased_buffer.cc',
@@ -1333,7 +1308,6 @@
13331308

13341309
'sources': [
13351310
'src/node_snapshot_stub.cc',
1336-
'src/node_code_cache_stub.cc',
13371311
'test/embedding/embedtest.cc',
13381312
],
13391313

@@ -1377,68 +1351,6 @@
13771351
}],
13781352
]
13791353
}, # overlapped-checker
1380-
1381-
# TODO(joyeecheung): do not depend on node_lib,
1382-
# instead create a smaller static library node_lib_base that does
1383-
# just enough for node_native_module.cc and the cache builder to
1384-
# compile without compiling the generated code cache C++ file.
1385-
# So generate_code_cache -> mkcodecache -> node_lib_base,
1386-
# node_lib -> node_lib_base & generate_code_cache
1387-
{
1388-
'target_name': 'mkcodecache',
1389-
'type': 'executable',
1390-
1391-
'dependencies': [
1392-
'<(node_lib_target_name)',
1393-
'deps/histogram/histogram.gyp:histogram',
1394-
'deps/uvwasi/uvwasi.gyp:uvwasi',
1395-
],
1396-
1397-
'includes': [
1398-
'node.gypi'
1399-
],
1400-
1401-
'include_dirs': [
1402-
'src',
1403-
'tools/msvs/genfiles',
1404-
'deps/v8/include',
1405-
'deps/cares/include',
1406-
'deps/uv/include',
1407-
'deps/uvwasi/include',
1408-
],
1409-
1410-
'defines': [
1411-
'NODE_WANT_INTERNALS=1'
1412-
],
1413-
'sources': [
1414-
'src/node_snapshot_stub.cc',
1415-
'src/node_code_cache_stub.cc',
1416-
'tools/code_cache/mkcodecache.cc',
1417-
'tools/code_cache/cache_builder.cc',
1418-
'tools/code_cache/cache_builder.h',
1419-
],
1420-
1421-
'conditions': [
1422-
[ 'node_use_openssl=="true"', {
1423-
'defines': [
1424-
'HAVE_OPENSSL=1',
1425-
],
1426-
}],
1427-
['v8_enable_inspector==1', {
1428-
'defines': [
1429-
'HAVE_INSPECTOR=1',
1430-
],
1431-
}],
1432-
['OS=="win"', {
1433-
'libraries': [
1434-
'dbghelp.lib',
1435-
'PsApi.lib',
1436-
'winmm.lib',
1437-
'Ws2_32.lib',
1438-
],
1439-
}],
1440-
],
1441-
}, # mkcodecache
14421354
{
14431355
'target_name': 'node_mksnapshot',
14441356
'type': 'executable',
@@ -1466,7 +1378,6 @@
14661378

14671379
'sources': [
14681380
'src/node_snapshot_stub.cc',
1469-
'src/node_code_cache_stub.cc',
14701381
'tools/snapshot/node_mksnapshot.cc',
14711382
],
14721383

@@ -1476,6 +1387,11 @@
14761387
'HAVE_OPENSSL=1',
14771388
],
14781389
}],
1390+
[ 'node_use_node_code_cache=="true"', {
1391+
'defines': [
1392+
'NODE_USE_NODE_CODE_CACHE=1',
1393+
],
1394+
}],
14791395
['v8_enable_inspector==1', {
14801396
'defines': [
14811397
'HAVE_INSPECTOR=1',

‎src/env.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
#include "handle_wrap.h"
3535
#include "node.h"
3636
#include "node_binding.h"
37-
#include "node_external_reference.h"
3837
#include "node_main_instance.h"
3938
#include "node_native_module.h"
4039
#include "node_options.h"
@@ -996,6 +995,11 @@ struct SnapshotData {
996995
// TODO(joyeecheung): there should be a vector of env_info once we snapshot
997996
// the worker environments.
998997
EnvSerializeInfo env_info;
998+
// A vector of built-in ids and v8::ScriptCompiler::CachedData, this can be
999+
// shared across Node.js instances because they are supposed to share the
1000+
// read only space. We use native_module::CodeCacheInfo because
1001+
// v8::ScriptCompiler::CachedData is not copyable.
1002+
std::vector<native_module::CodeCacheInfo> code_cache;
9991003
};
10001004

10011005
class Environment : public MemoryRetainer {

‎src/node.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -979,8 +979,6 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
979979

980980
#endif // defined(NODE_HAVE_I18N_SUPPORT)
981981

982-
NativeModuleEnv::InitializeCodeCache();
983-
984982
// We should set node_is_initialized here instead of in node::Start,
985983
// otherwise embedders using node::Init to initialize everything will not be
986984
// able to set it and native modules will not load for them.
@@ -1174,6 +1172,10 @@ int Start(int argc, char** argv) {
11741172
: nullptr;
11751173
uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME);
11761174

1175+
if (snapshot_data != nullptr) {
1176+
native_module::NativeModuleEnv::RefreshCodeCache(
1177+
snapshot_data->code_cache);
1178+
}
11771179
NodeMainInstance main_instance(snapshot_data,
11781180
uv_default_loop(),
11791181
per_process::v8_platform.Platform(),

‎src/node_code_cache_stub.cc

-22
This file was deleted.

‎src/node_main_instance.cc

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "debug_utils-inl.h"
77
#include "node_external_reference.h"
88
#include "node_internals.h"
9+
#include "node_native_module_env.h"
910
#include "node_options-inl.h"
1011
#include "node_snapshot_builder.h"
1112
#include "node_snapshotable.h"
@@ -189,6 +190,7 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) {
189190

190191
CHECK(!context.IsEmpty());
191192
Context::Scope context_scope(context);
193+
192194
CHECK(InitializeContextRuntime(context).IsJust());
193195
SetIsolateErrorHandlers(isolate_, {});
194196
env->InitializeMainContext(context, &(snapshot_data_->env_info));

‎src/node_native_module.cc

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "node_native_module.h"
2-
#include "util-inl.h"
32
#include "debug_utils-inl.h"
3+
#include "node_internals.h"
4+
#include "util-inl.h"
45

56
namespace node {
67
namespace native_module {
@@ -277,6 +278,11 @@ MaybeLocal<Function> NativeModuleLoader::LookupAndCompile(
277278
: ScriptCompiler::kEagerCompile;
278279
ScriptCompiler::Source script_source(source, origin, cached_data);
279280

281+
per_process::Debug(DebugCategory::CODE_CACHE,
282+
"Compiling %s %s code cache\n",
283+
id,
284+
has_cache ? "with" : "without");
285+
280286
MaybeLocal<Function> maybe_fun =
281287
ScriptCompiler::CompileFunctionInContext(context,
282288
&script_source,
@@ -304,17 +310,34 @@ MaybeLocal<Function> NativeModuleLoader::LookupAndCompile(
304310
*result = (has_cache && !script_source.GetCachedData()->rejected)
305311
? Result::kWithCache
306312
: Result::kWithoutCache;
313+
314+
if (has_cache) {
315+
per_process::Debug(DebugCategory::CODE_CACHE,
316+
"Code cache of %s (%s) %s\n",
317+
id,
318+
script_source.GetCachedData()->buffer_policy ==
319+
ScriptCompiler::CachedData::BufferNotOwned
320+
? "BufferNotOwned"
321+
: "BufferOwned",
322+
script_source.GetCachedData()->rejected ? "is rejected"
323+
: "is accepted");
324+
}
325+
307326
// Generate new cache for next compilation
308327
std::unique_ptr<ScriptCompiler::CachedData> new_cached_data(
309328
ScriptCompiler::CreateCodeCacheForFunction(fun));
310329
CHECK_NOT_NULL(new_cached_data);
311330

312331
{
313332
Mutex::ScopedLock lock(code_cache_mutex_);
314-
// The old entry should've been erased by now so we can just emplace.
315-
// If another thread did the same thing in the meantime, that should not
316-
// be an issue.
317-
code_cache_.emplace(id, std::move(new_cached_data));
333+
const auto it = code_cache_.find(id);
334+
// TODO(joyeecheung): it's safer for each thread to have its own
335+
// copy of the code cache map.
336+
if (it == code_cache_.end()) {
337+
code_cache_.emplace(id, std::move(new_cached_data));
338+
} else {
339+
it->second.reset(new_cached_data.release());
340+
}
318341
}
319342

320343
return scope.Escape(fun);

‎src/node_native_module.h

+8
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@
1515
class PerProcessTest;
1616

1717
namespace node {
18+
class SnapshotBuilder;
1819
namespace native_module {
1920

2021
using NativeModuleRecordMap = std::map<std::string, UnionBytes>;
2122
using NativeModuleCacheMap =
2223
std::unordered_map<std::string,
2324
std::unique_ptr<v8::ScriptCompiler::CachedData>>;
2425

26+
struct CodeCacheInfo {
27+
std::string id;
28+
std::vector<uint8_t> data;
29+
};
30+
2531
// The native (C++) side of the NativeModule in JS land, which
2632
// handles compilation and caching of builtin modules (NativeModule)
2733
// and bootstrappers, whose source are bundled into the binary
@@ -66,6 +72,8 @@ class NODE_EXTERN_PRIVATE NativeModuleLoader {
6672
bool CannotBeRequired(const char* id);
6773

6874
NativeModuleCacheMap* code_cache();
75+
const Mutex& code_cache_mutex() const { return code_cache_mutex_; }
76+
6977
v8::ScriptCompiler::CachedData* GetCodeCache(const char* id) const;
7078
enum class Result { kWithCache, kWithoutCache };
7179
v8::MaybeLocal<v8::String> LoadBuiltinModuleSource(v8::Isolate* isolate,

0 commit comments

Comments
 (0)
Please sign in to comment.