mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-09-15 07:19:20 -04:00
161 lines
6.7 KiB
Bash
Executable File
161 lines
6.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Regenerates orderfile/lightpanda.ld and orderfile/v8.txt from a profile of
|
|
# the CDP bench (demo/puppeteer/cdp.js), see orderfile/README.md.
|
|
#
|
|
# usage: orderfile/tools/regen.sh [zig build args...]
|
|
# The build args are those of the release build minus -Dorderfile, e.g.
|
|
# -Doptimize=ReleaseFast -Dsnapshot_path=../../snapshot.bin -Dcpu=x86_64
|
|
#
|
|
# Needs root (sudo) for /sys/kernel/debug/fault_around_bytes, a checkout of
|
|
# lightpanda-io/demo (DEMO_DIR, npm install done), node, go, python3 and
|
|
# binutils. Run from the repository root.
|
|
#
|
|
# Steps:
|
|
# 1. Build with an empty linker script: the unordered binary. Profiling
|
|
# it, rather than one laid out by the current profile, keeps stale
|
|
# entries from surviving forever by sharing a page with a hot neighbour.
|
|
# 2. Bench it with 4KB fault-around, dump the resident pages and turn them
|
|
# into the new lightpanda.ld / v8.txt.
|
|
# 3. Re-run the build's ld.lld line with the new script to check that it
|
|
# links. The script is a cache input of the whole exe compilation, so
|
|
# building with it would be a second full release compile; the manual
|
|
# relink is the same link in seconds (only V8's hot-marked archive is
|
|
# the one from the current v8.txt, which does not affect parsing).
|
|
# Whether the new profile is any good is measured by the e2e bench.
|
|
#
|
|
# Environment:
|
|
# DEMO_DIR demo checkout (default ../demo)
|
|
# RUNS bench iterations (default 100)
|
|
# OUT scratch directory (default /tmp/orderfile-regen)
|
|
# RAMDIR tmpfs the binary is benched from (default /dev/shm)
|
|
set -euo pipefail
|
|
|
|
DEMO_DIR=${DEMO_DIR:-../demo}
|
|
RUNS=${RUNS:-100}
|
|
OUT=${OUT:-/tmp/orderfile-regen}
|
|
RAMDIR=${RAMDIR:-/dev/shm}
|
|
FAULT_AROUND=/sys/kernel/debug/fault_around_bytes
|
|
|
|
ROOT=$(pwd)
|
|
DEMO_DIR=$(cd "$DEMO_DIR" && pwd)
|
|
TOOLS=$ROOT/orderfile/tools
|
|
LD=$ROOT/orderfile/lightpanda.ld
|
|
V8_TXT=$ROOT/orderfile/v8.txt
|
|
|
|
[ -f "$LD" ] || { echo "run from the repository root" >&2; exit 2; }
|
|
[ -f "$DEMO_DIR/puppeteer/cdp.js" ] || { echo "DEMO_DIR=$DEMO_DIR is not a demo checkout" >&2; exit 2; }
|
|
mkdir -p "$OUT"
|
|
|
|
# Fault-around is a global knob: whatever happens, put it back.
|
|
# debugfs is 0700 root, so every probe under it has to run as root.
|
|
if ! mountpoint -q /sys/kernel/debug; then
|
|
sudo mount -t debugfs none /sys/kernel/debug
|
|
fi
|
|
sudo test -f "$FAULT_AROUND" || { echo "$FAULT_AROUND missing: kernel lacks CONFIG_DEBUG_FS fault-around knob" >&2; exit 2; }
|
|
FAULT_AROUND_DEFAULT=$(sudo cat "$FAULT_AROUND")
|
|
set_fault_around() { echo "$1" | sudo tee "$FAULT_AROUND" > /dev/null; }
|
|
|
|
PIDS=()
|
|
cleanup() {
|
|
set_fault_around "$FAULT_AROUND_DEFAULT" || true
|
|
for pid in "${PIDS[@]:-}"; do
|
|
[ -n "$pid" ] && kill "$pid" 2> /dev/null || true
|
|
done
|
|
rm -f "$RAMDIR"/lightpanda-regen-$$-*
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
log() { echo "== $*" >&2; }
|
|
|
|
# The demo web server the bench navigates to (port 1234).
|
|
if ! curl -sf -o /dev/null http://127.0.0.1:1234/campfire-commerce/; then
|
|
(cd "$DEMO_DIR" && go run runner/main.go -serve > "$OUT/runner.log" 2>&1) &
|
|
PIDS+=($!)
|
|
for _ in $(seq 50); do
|
|
curl -sf -o /dev/null http://127.0.0.1:1234/campfire-commerce/ && break
|
|
sleep 0.2
|
|
done
|
|
fi
|
|
|
|
# The tmpfs copy the binary is benched from. It outlives the bench: the
|
|
# resident pages dump names it, and hotlist.py matches mappings by path.
|
|
ram_copy() { echo "$RAMDIR/lightpanda-regen-$$-$(basename "$1")"; }
|
|
|
|
# profile <binary> <resident.json>
|
|
# Runs the CDP bench against <binary> served from tmpfs with 4KB fault-around,
|
|
# dumps its resident pages while it is still alive and prints its VmHWM (KB).
|
|
profile() {
|
|
local bin=$1 resident=$2
|
|
local ram
|
|
ram=$(ram_copy "$bin")
|
|
cp "$bin" "$ram"
|
|
set_fault_around 4096
|
|
"$ram" serve --insecure-disable-tls-host-verification > /dev/null 2>&1 &
|
|
local pid=$!
|
|
sleep 1
|
|
(cd "$DEMO_DIR" && RUNS=$RUNS node puppeteer/cdp.js > "$OUT/bench.out")
|
|
sleep 2
|
|
python3 "$TOOLS/pagemap.py" "$pid" "$resident" >&2
|
|
local hwm
|
|
hwm=$(grep VmHWM "/proc/$pid/status" | grep -oP '\d+')
|
|
kill "$pid"
|
|
while kill -0 "$pid" 2> /dev/null; do sleep 0.2; done
|
|
set_fault_around "$FAULT_AROUND_DEFAULT"
|
|
echo "$hwm"
|
|
}
|
|
|
|
# 1. Unordered build. The script's content is a cache input, so the timestamp
|
|
# guarantees a fresh link and thus a --verbose-link line listing every
|
|
# object and archive of the link.
|
|
log "building without ordering"
|
|
printf '/* unordered profile build, %s */\n' "$(date +%s%N)" > "$OUT/empty.ld"
|
|
zig build "$@" -Dorderfile="$OUT/empty.ld" --verbose-link 2> "$OUT/link.log" \
|
|
|| { cat "$OUT/link.log" >&2; exit 1; }
|
|
cp zig-out/bin/lightpanda "$OUT/unordered"
|
|
# The verbose-link line is "ld.lld <args>", indented, and prefixed with
|
|
# "error: " on some paths; only the args replay under `zig ld.lld`.
|
|
grep -m1 -F 'lightpanda_zcu.o' "$OUT/link.log" | sed -E 's/^[[:space:]]*(error: )?ld\.lld //' > "$OUT/link.line"
|
|
[ -s "$OUT/link.line" ] || { echo "no ld.lld line in $OUT/link.log" >&2; exit 1; }
|
|
case $(head -c 1 "$OUT/link.line") in
|
|
-) ;;
|
|
*) echo "link line is not an ld.lld invocation: $(cut -c 1-100 "$OUT/link.line")" >&2; exit 1 ;;
|
|
esac
|
|
|
|
# Link inputs -> objects/archives for gen_order.py; the V8 archive is
|
|
# handled separately (its symbols go to v8.txt).
|
|
V8_ARCHIVE=
|
|
OBJS=()
|
|
for arg in $(cat "$OUT/link.line"); do
|
|
case $arg in
|
|
*.o | *.a) [ -f "$arg" ] || continue ;;
|
|
*) continue ;;
|
|
esac
|
|
case $(basename "$arg") in
|
|
libc_v8*.a) V8_ARCHIVE=$arg ;;
|
|
*) case " ${OBJS[*]:-} " in *" $arg "*) ;; *) OBJS+=("$arg") ;; esac ;;
|
|
esac
|
|
done
|
|
[ -n "$V8_ARCHIVE" ] || { echo "no V8 archive in the link line" >&2; exit 1; }
|
|
log "${#OBJS[@]} link inputs, V8 archive $V8_ARCHIVE"
|
|
|
|
# 2. Profile the unordered binary at 4KB fault-around.
|
|
log "profiling the unordered binary (RUNS=$RUNS)"
|
|
HOT_SET_KB=$(profile "$OUT/unordered" "$OUT/resident.json")
|
|
python3 "$TOOLS/hotlist.py" "$(ram_copy "$OUT/unordered")" "$OUT/resident.json" "$OUT/hot" >&2
|
|
[ -s "$OUT/hot.text" ] || { echo "empty profile, see $OUT/resident.json" >&2; exit 1; }
|
|
python3 "$TOOLS/gen_order.py" "$OUT/hot.text" "$OUT/hot.rodata" "$OUT/lightpanda.ld" \
|
|
--v8 "$V8_ARCHIVE" "$OUT/v8.txt" "${OBJS[@]}" > "$OUT/gen_order.stats"
|
|
cat "$OUT/gen_order.stats" >&2
|
|
[ -s "$OUT/v8.txt" ] || { echo "no V8 symbols in the profile" >&2; exit 1; }
|
|
|
|
# 3. Does it link? Same objects, same flags, new script.
|
|
log "linking with the new orderfile"
|
|
# shellcheck disable=SC2046
|
|
zig ld.lld $(sed "s#-T [^ ]*#-T $OUT/lightpanda.ld#; s#-o [^ ]*#-o $OUT/linked#" "$OUT/link.line")
|
|
"$OUT/linked" version > /dev/null
|
|
|
|
cp "$OUT/lightpanda.ld" "$LD"
|
|
cp "$OUT/v8.txt" "$V8_TXT"
|
|
printf 'hot set %sKB resident at 4KB fault-around: %s text, %s rodata symbols\n' \
|
|
"$HOT_SET_KB" "$(wc -l < "$OUT/hot.text")" "$(wc -l < "$OUT/hot.rodata")" | tee "$OUT/result.txt"
|