Closed magreenblatt closed 1 week ago
Add tools/API to create the snapshot Add API to load the snapshot
mksnapshot
tool. For example, using electron-link.mksnapshot
tool (available as part of the CEF/Chromium build). This generates a v8_context_snapshot.bin
file.v8_context_snapshot.bin
file in the app installation/bundle.Generate a single JavaScript file that can be provided to the mksnapshot tool.
% npm install --save electron-link
added 44 packages in 21s
snapshot.js
file with the modules that you want to bundle (install the modules first if necessary):
require('react')
require('react-dom')
run.js
script to run electron-link (based on this example):
const vm = require('vm')
const path = require('path')
const fs = require('fs')
const electronLink = require('electron-link')
const excludedModules = {}
async function main () {
const baseDirPath = __dirname
console.log('Creating a linked script..')
const result = await electronLink({
baseDirPath: baseDirPath,
mainPath: `${baseDirPath}/snapshot.js`,
cachePath: `${baseDirPath}/cache`,
shouldExcludeModule: (modulePath) => excludedModules.hasOwnProperty(modulePath)
})
const snapshotScriptPath = `${baseDirPath}/cache/snapshot.js`
fs.writeFileSync(snapshotScriptPath, result.snapshotScript)
// Verify if we will be able to use this in `mksnapshot`
vm.runInNewContext(result.snapshotScript, undefined, {filename: snapshotScriptPath, displayErrors: true})
}
main().catch(err => console.error(err))
% node run.js
Creating a linked script..
cache/snapshot.js
file.
% ls -sh cache/snapshot.js
2880 cache/snapshot.js
Run the mksnapshot tool
(The following instructions are based loosely on Electron's mksnapshot.js)
% cd ~/code/chromium_git/chromium/src
% gn ls out/Debug_GN_arm64 | grep run_mksnapshot //v8:run_mksnapshot_default
% gn outputs out/Debug_GN_arm64 //v8:run_mksnapshot_default gen/v8/embedded.S snapshot_blob.bin
% rm -rf out/Debug_GN_arm64/snapshot_blob.bin
% ninja -v -C out/Debug_GN_arm64 v8:run_mksnapshot_default
ninja: Entering directory `out/Debug_GN_arm64'
[1/2] python3 ../../v8/tools/run.py ./mksnapshot --turbo_instruction_scheduling --stress-turbo-late-spilling --target_os=mac --target_arch=arm64 --embedded_src gen/v8/embedded.S --embedded_variant Default --random-seed 314159265 --startup_blob snapshot_blob.bin --native-code-counters --verify-heap
2. Returning to the temp directory, run the `mksnapshot` tool to generate `snapshot_blob.bin`:
% TOOL_PATH=~/code/chromium_git/chromium/src/out/Debug_GN_arm64 % $TOOL_PATH/mksnapshot --turbo_instruction_scheduling --stress-turbo-late-spilling --target_os=mac --target_arch=arm64 --embedded_src cache/embedded.S --embedded_variant Default --random-seed 314159265 --startup_blob cache/snapshot_blob.bin --native-code-counters --verify-heap cache/snapshot.js Loading script for embedding: cache/snapshot.js
3. Copy the `v8_context_snapshot_generator` tool to the cache directory (it must exist next to `snapshot_blob.bin`) and run to generate `v8_context_snapshot.arm64.bin` (platform/OS-specific name):
% cp -R $TOOL_PATH/v8_context_snapshot_generator cache/ % cd cache % ./v8_context_snapshot_generator --output_file=cache/v8_context_snapshot.arm64.bin
4. Verify the change in file size:
% ls -s cache/*.bin 9048 cache/snapshot_blob.bin 10080 cache/v8_context_snapshot.arm64.bin
% ls -s $TOOL_PATH/*.bin
2680 /Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_arm64/snapshot_blob.bin
3368 /Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_arm64/v8_context_snapshot.arm64.bin
Replace the default v8_context_snapshot.bin file in the app installation/bundle.
v8_context_snapshot.arm64.bin
into the CEF app bundle:
% APP_PATH=~/code/chromium_git/chromium/src/out/Debug_GN_arm64/
% cp -R cache/v8_context_snapshot.arm64.bin $APP_PATH/cefclient.app/Contents/Frameworks/Chromium\ Embedded\ Framework.framework/Resources
% open $APP_PATH/cefclient.app
snapshotResult
global object) exist in DevTools:require
function (example here) to use snapshot contents in a JavaScript application.CEF will need to provide the following:
mksnapshot
and v8_context_snapshot_generator
binary tools.mksnapshot
(platform/OS-specific).v8_context_snapshot.bin
(with correct platform/OS-specific name).Identify the original command-line that Chromium used to generate the snapshot
This could also be extracted from out/Debug_GN_arm64/toolchain.ninja
:
rule __v8_run_mksnapshot_default___build_toolchain_mac_clang_arm64__rule
command = python3 ../../v8/tools/run.py ./mksnapshot --turbo_instruction_scheduling --stress-turbo-late-spilling --target_os=mac --target_arch=arm64 --embedded_src gen/v8/embedded.S --embedded_variant Default --random-seed 314159265 --startup_blob snapshot_blob.bin --native-code-counters --verify-heap
description = ACTION //v8:run_mksnapshot_default(//build/toolchain/mac:clang_arm64)
CEF will need to provide the following
How this might work with a CEF binary distribution (Mac ARM64 example):
tools/snapshot
directory:
% ls -s
total 3068112
190392 mksnapshot 8 run.sh
8 mksnapshot_cmd.txt 2877704 v8_context_snapshot_generator
snapshot.js
file (like here) and copies it into that directory.run.sh
script to generate v8_context_snapshot.arm64.bin
:
% ./run.sh snapshot.js
Running mksnapshot...
Loading script for embedding: snapshot.js
Running v8_context_snapshot_generator...
Done
% ls -s
total 3137344
45184 embedded.S 8 run.sh 10080 v8_context_snapshot.arm64.bin
190392 mksnapshot 2880 snapshot.js 2877704 v8_context_snapshot_generator
8 mksnapshot_cmd.txt 11088 snapshot_blob.bin
v8_context_snapshot.arm64.bin
into the app bundle or installer package (like here).The mksnapshot_cmd.txt
file contains the mksnapshot command-line:
--turbo_instruction_scheduling --stress-turbo-late-spilling --target_os=mac --target_arch=arm64 --embedded_src embedded.S --embedded_variant Default --random-seed 314159265 --startup_blob snapshot_blob.bin --native-code-counters --verify-heap
The run.sh
file (or run.bat
file on Windows) contains:
#!/bin/sh
# Read mksnapshot_cmd.txt into an argument array.
IFS=' ' read -r -a args < mksnapshot_cmd.txt
# Generate snapshot_blob.bin.
echo 'Running mksnapshot...'
./mksnapshot "${args[@]}" "$@"
# Determine the architecture suffix, if any.
suffix=''
if [ "$(uname)" == "Darwin" ]; then
value='--target_arch=arm64'
if [[ " ${args[*]} " =~ [[:space:]]${value}[[:space:]] ]]; then
suffix='.arm64'
else
suffix='.x86_64'
fi
fi
# Generate v8_context_snapshot.bin.
echo 'Running v8_context_snapshot_generator...'
./v8_context_snapshot_generator --output_file=v8_context_snapshot${suffix}.bin
echo 'Done.'
Official builds of mksnapshot
+ v8_context_snapshot_generator
are pretty large (174MB uncompressed on Mac ARM64), so we'll likely distribute them as a separate archive (e.g. cef_binary_[version]_[platform]_tools.tar.bz2
)
Cross-compile builds (e.g. ARM64 MacOS host building x64) place the tools in a subdirectory like:
% autoninja -C out/Debug_GN_x64 v8_context_snapshot
% ls -s out/Debug_GN_x64/clang_arm64_v8_x64
total 3598216
0 angledata 100184 libvk_swiftshader.dylib
2224 bytecode_builtins_list_generator 8 libvk_swiftshader.dylib.TOC
0 gen 0 local_rustc_sysroot
5392 gen-regexp-special-case 194408 mksnapshot
20448 icudtl.dat 0 obj
0 jsproto 0 pyproto
14616 libEGL.dylib 0 resources
16 libEGL.dylib.TOC 2688 snapshot_blob.bin
129080 libGLESv2.dylib 23272 toolchain.ninja
104 libGLESv2.dylib.TOC 7632 torque
10872 libVkICD_mock_icd.dylib 8 v8_build_config.json
8 libVkICD_mock_icd.dylib.TOC 2889848 v8_context_snapshot_generator
197392 libVkLayer_khronos_validation.dylib 8 vk_swiftshader_icd.json
8 libVkLayer_khronos_validation.dylib.TOC
% file out/Debug_GN_x64/clang_arm64_v8_x64/mksnapshot
out/Debug_GN_x64/clang_arm64_v8_x64/mksnapshot: Mach-O 64-bit executable arm64
The final output file (v8_context_snapshot.x86_64.bin
) is in the top-level out/Debug_GN_x64
directory.
Some build configurations will also have a V8 profile input file that needs to be included in the distribution.
Note: use_v8_context_snapshot may be turned off by default in non-Official builds. See this blink-dev thread.
On MacOS ARM64 it's necessary to run xattr -c <binary>
to bypass security blocks after downloading the tools distribution.
Looks like mksnapshot
is crashing for MacOS x64 at M127 when specifying an additional script argument. Filed with Chromium as https://issues.chromium.org/issues/353552530
To debug mksnapshot
crashes (MacOS x64 cross-compile in this example):
% cd ~/code/chromium_git/chromium/src/cef
% export CEF_ENABLE_AMD64=1
% export GN_OUT_CONFIGS=Debug_GN_x64
% export GN_DEFINES=is_official_build=true
% ./cef_create_projects.sh
[...]
Done. Made 27138 targets from 3606 files in 4809ms
% cd ..
% time autoninja -C out/Debug_GN_x64 run_mksnapshot_default
ninja: Entering directory `out/Debug_GN_x64'
[3429/3429] STAMP obj/v8/run_mksnapshot_default.stamp
autoninja -C out/Debug_GN_x64 run_mksnapshot_default 6946.29s user 315.17s system 1376% cpu 8:47.63 total
# Get command-line arguments from mksnapshot_cmd.txt
% lldb -- ./out/Debug_GN_x64/clang_arm64_v8_x64/mksnapshot --turbo_instruction_scheduling --stress-turbo-late-spilling --target_os=mac --target_arch=x64 --embedded_src embedded.S --embedded_variant Default --random-seed 314159265 --startup_blob snapshot_blob.bin --no-native-code-counters ~/tmp/snapshot.js
[...]
(lldb) r
Process 12640 launched: '/Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_x64/clang_arm64_v8_x64/mksnapshot' (arm64)
Loading script for embedding: /Users/marshall/tmp/snapshot.js
Process 12640 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=1, subcode=0xe5894855)
frame #0: 0x000001e80eb49cc0
-> 0x1e80eb49cc0: str z21, [x2, #0x4a, mul vl]
0x1e80eb49cc4: .long 0x8348026a ; unknown opcode
0x1e80eb49cc8: b.gt 0x1e80ebcbde4
0x1e80eb49ccc: .long 0x56415541 ; unknown opcode
Target 0: (mksnapshot) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=1, subcode=0xe5894855)
* frame #0: 0x000001e80eb49cc0
frame #1: 0x000000010031797c mksnapshot`v8::internal::Execution::CallScript(isolate=<unavailable>, script_function=<unavailable>, receiver=<unavailable>, host_defined_options=Handle<v8::internal::Object> @ 0x000000016fde38c8) at execution.cc:517:10 [opt]
frame #2: 0x0000000100043f28 mksnapshot`v8::Script::Run(this=<unavailable>, context=<unavailable>, host_defined_options=<unavailable>) at api.cc:2115:7 [opt]
frame #3: 0x0000000100c4925c mksnapshot`v8::internal::(anonymous namespace)::RunExtraCode(isolate=<unavailable>, context=<unavailable>, utf8_source=<unavailable>, name=<unavailable>) at snapshot.cc:779:15 [opt]
frame #4: 0x0000000100c49050 mksnapshot`v8::internal::CreateSnapshotDataBlobInternal(function_code_handling=<unavailable>, embedded_source=<unavailable>, snapshot_creator=<unavailable>, serializer_flags=<unavailable>) at snapshot.cc:797:10 [opt]
frame #5: 0x000000010002e14c mksnapshot`main [inlined] (anonymous namespace)::CreateSnapshotDataBlob(snapshot_creator=0x000000016fdff150, embedded_source=<unavailable>) at mksnapshot.cc:162:28 [opt]
frame #6: 0x000000010002e118 mksnapshot`main(argc=2, argv=<unavailable>) at mksnapshot.cc:295:16 [opt]
Is your feature request related to a problem? Please describe. Custom startup snapshots can be used to speed up V8/JavaScript load time in the renderer process. See also this example of how they are currently used with Electron.
Describe the solution you'd like
Additional context To support this capability a web application must provide a non-user-specific "ready state" that can be loaded for the purposes of creating the snapshot (e.g. library scripts that are loaded at startup, before the "actual" application runs). The generated snapshot is then distributed with the CEF application as an additional binary file. A new/separate snapshot will need to be created for each platform/architecture, each CEF/Chromium version, and each time the web application's "ready state" changes. Consequently, creation of snapshots should become part of the application build process or packaging step.