T-499 built, T-500 the stall measured and the clock hold, T-501 H.264/AAC through FFmpeg; the rendering options measured
The stall: a leaf-PC sampler in the daemon ($JIHAD_STALL_SAMPLER, GoannaRenderPage.cpp) and tools/host/stall-resolve.py (the toolchain's addr2line against the unstripped libxul) name what holds the main thread — the pages' own JavaScript two thirds, compile and the collections the rest; the clock sat at 192–384 MHz under load (Palm's ondemandtcl, up_threshold 95). The clock hold (JihadBrowserServer.cpp, jihad.cpu.hold) raises the frequency floor while the engine has work and releases it after two seconds of idle ticks, on the no-page path too (daemon 18); it never touches the governor, which wedged the kernel's cpufreq lock and cost a reboot. H.264 and AAC: build/webos-oe/fetch-ffmpeg.sh + build-ffmpeg-arm.sh cross-build FFmpeg 4.4.5's decoders (libavcodec.so.58, libavutil.so.56, LGPL v2.1+, decoders and parsers only); the bundle ships them, NOTICE and licenses/runtime/ carry the terms; the codec page plays both to the end on the device. The layout viewport (T-499): GoannaRenderPage::SetLayoutSize and the once-per-page rule in emitGeometry (jihad.viewport.followOverflow); the engine window resizes but the page still reads innerWidth 768 — the paint path restores the card size; open. Rendering options: tools/device/gpu-bench.c times the Adreno 220 through the vendor EGL/GLES2 (composite 4–13 ms, readback 11–14 ms, whole-texture upload 9–12 ms per 3 MB, tile sub-updates 160 ms) beside the CPU equivalents; docs/RENDERING-OPTIONS.md keeps the numbers; the report with the decision table is the owner's doc. Ledger T-499…T-501, build site, kit, PICKUP, memories. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
This commit is contained in:
1 parent
4a76dbae7e
commit
4ded8d7244
30 files changed
+5818
-5
No files matched your search
@@ -69,3 +69,8 @@ __pycache__/
|
||||
build/engine/.update-in-progress
|
||||
/tools/host/bin/
|
||||
/tools/host/build/
|
||||
|
||||
# the engine media test clips (render/goanna/test/make-media-assets.sh makes them; never stored)
|
||||
render/goanna/test/clip*.mp4
|
||||
render/goanna/test/clip*.webm
|
||||
render/goanna/test/tone10.*
|
||||
@@ -60,6 +60,13 @@ UXP — Unified XUL Platform / Goanna engine (rendering engine dependency)
|
||||
covers liblgpllibs (SoundTouch, LGPL-2.1) and libmozavcodec/libmozavutil (the FFmpeg subset
|
||||
"ffvpx": VP8, VP9 and FLAC decoders only; LGPL-2.1). The engine's own about:license page,
|
||||
installed with it, carries the licences of everything compiled into libxul.
|
||||
libavcodec.so.58 and libavutil.so.56 are FFmpeg 4.4.5 (https://ffmpeg.org/, LGPL v2.1 or
|
||||
later; the text is licenses/runtime/ffmpeg-COPYING.LGPLv2.1), built by
|
||||
build/webos-oe/build-ffmpeg-arm.sh with the H.264, AAC and MP3 decoders and parsers only, no
|
||||
GPL or non-free parts, unmodified source (sha256 of ffmpeg-4.4.5.tar.xz pinned in the script);
|
||||
the engine loads libavcodec at run time for the formats ffvpx does not decode. The H.264 and AAC
|
||||
formats are covered by patents in some countries; whether a decoder may be distributed or used
|
||||
there is a question for the distributor and the user, not something this notice can settle.
|
||||
Jihad Browser's per-domain User-Agent override table (render/goanna/
|
||||
EngineHost.cpp, jihadUaTable) — the domain→Firefox-version site-compat
|
||||
choices — is derived from Basilisk / Pale-Moon's uaoverrides.inc:
|
||||
|
||||
@@ -206,8 +206,9 @@ and the engine, which is modified: its source is the `jihad/uxp-RB_20261006` bra
|
||||
The packages also carry the libraries the engine runs on, each under its own licence: glibc
|
||||
(LGPL-2.1), the GCC runtime (GPL-3.0 with the Runtime Library Exception), ALSA and GLib (LGPL),
|
||||
Expat, fontconfig, PCRE, libpng, zlib (permissive), FreeType (FreeType License), the engine's own
|
||||
`about:license` contents, Lato (SIL OFL, Mochi package), and the Personas by other authors (CC BY /
|
||||
BY-SA, credited in `CREDITS.md`). `NOTICE` names every one, with where its text ships and where its
|
||||
`about:license` contents, FFmpeg 4.4.5's H.264/AAC/MP3 decoders (LGPL v2.1+, the two libraries
|
||||
built from unmodified source by `build/webos-oe/build-ffmpeg-arm.sh`), Lato (SIL OFL, Mochi
|
||||
package), and the Personas by other authors (CC BY / BY-SA, credited in `CREDITS.md`). `NOTICE` names every one, with where its text ships and where its
|
||||
source is. Palefill is MPL-2.0, shipped unmodified. Pale Moon, Basilisk and Moonchild branding is
|
||||
removed from the engine build; "Goanna" names the engine, Moonchild Productions' trademark, used
|
||||
to say what the browser runs on. Flash Player is the device's own and is not distributed. HP's SDK,
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2026 NotAlexNoyle. Apache-2.0.
|
||||
#
|
||||
# build-ffmpeg-arm.sh — FFmpeg's H.264, AAC and MP3 decoders for the device: libavcodec.so.58 and
|
||||
# libavutil.so.56, cross-built in the ARM container. The engine is built with MOZ_FFMPEG and loads
|
||||
# a libavcodec 58–61 at run time (dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp) for the
|
||||
# formats its own ffvpx lacks — VP8/VP9/Opus/Vorbis/FLAC are built in, H.264 and AAC are not, and
|
||||
# until this every MP4 <video> and every H.264 Media Source stream was "format not supported".
|
||||
# FFmpeg 4.4.5, LGPL v2.1 or later (no --enable-gpl, no --enable-nonfree); decoders and parsers
|
||||
# only, no demuxers (the engine demuxes), no programs, no filters, no scaling. Linked against the
|
||||
# toolchain's own sysroot (glibc 2.23, the device's), not the Debian one the engine takes GTK from:
|
||||
# that one has no C runtime objects for this gcc (crt1.o).
|
||||
#
|
||||
# build/webos-oe/in-container.sh --arm build/webos-oe/build-ffmpeg-arm.sh
|
||||
# → build/webos-oe/out-arm/ffmpeg-arm/lib/libavcodec.so.58, libavutil.so.56 (+ COPYING.LGPLv2.1)
|
||||
# make-device-bundle.sh ships the two libraries when they are there.
|
||||
set -euo pipefail
|
||||
VER=4.4.5
|
||||
SHA=f9514e0d3515aee5a271283df71636e1d1ff7274b15853bcd84e144be416ab07
|
||||
SRC=/out/ffmpeg-src; OUT=/out/ffmpeg-arm
|
||||
mkdir -p "$SRC" "$OUT"
|
||||
cd "$SRC"
|
||||
# The container has no curl: the tarball is fetched on the host by build/webos-oe/fetch-ffmpeg.sh
|
||||
# (pinned sha256) into out-arm/ffmpeg-src/, which is /out/ffmpeg-src here.
|
||||
if [ ! -f "ffmpeg-$VER.tar.xz" ]; then echo "ERROR: $SRC/ffmpeg-$VER.tar.xz is missing — run build/webos-oe/fetch-ffmpeg.sh on the host first" >&2; exit 2; fi
|
||||
got="$(sha256sum "ffmpeg-$VER.tar.xz" | cut -d' ' -f1)"
|
||||
[ "$got" = "$SHA" ] || { echo "ERROR: ffmpeg-$VER.tar.xz sha256 $got is not the pinned $SHA" >&2; exit 2; }
|
||||
rm -rf "ffmpeg-$VER"; tar -xJf "ffmpeg-$VER.tar.xz"; cd "ffmpeg-$VER"
|
||||
echo "== [ARM] FFmpeg $VER configure (decoders: h264 aac mp3; LGPL)"
|
||||
./configure \
|
||||
--enable-cross-compile --cross-prefix=/tc/bin/arm-webos-linux-gnueabi- --arch=arm \
|
||||
--target-os=linux --prefix="$OUT" \
|
||||
--enable-shared --disable-static --enable-pic --enable-neon \
|
||||
--disable-everything \
|
||||
--enable-decoder=h264,aac,aac_latm,mp3,mp3float \
|
||||
--enable-parser=h264,aac,aac_latm,mpegaudio \
|
||||
--disable-programs --disable-doc --disable-avdevice --disable-avfilter --disable-avformat \
|
||||
--disable-swscale --disable-swresample --disable-postproc --disable-network \
|
||||
--disable-iconv --disable-zlib --disable-bzlib --disable-lzma --disable-debug \
|
||||
--extra-cflags="-march=armv7-a -mfpu=neon -mfloat-abi=softfp -O2" \
|
||||
--extra-ldflags="-Wl,-O1,--as-needed" > "$OUT/configure.log" 2>&1 || { tail -30 "$OUT/configure.log"; tail -40 ffbuild/config.log 2>/dev/null; exit 3; }
|
||||
grep -E 'license|License' "$OUT/configure.log" | head -2
|
||||
echo "== [ARM] FFmpeg make"
|
||||
make -j"$(nproc)" > "$OUT/make.log" 2>&1 || { tail -30 "$OUT/make.log"; exit 4; }
|
||||
make install > "$OUT/install.log" 2>&1
|
||||
cp COPYING.LGPLv2.1 LICENSE.md "$OUT/"
|
||||
echo "== built:"; ls -la "$OUT/lib/" | awk '{print $5, $9}' | grep -E 'so\.[0-9]+$|so$'
|
||||
for f in "$OUT"/lib/libavcodec.so.58 "$OUT"/lib/libavutil.so.56; do
|
||||
[ -f "$f" ] || { echo "ERROR: $f missing" >&2; exit 5; }
|
||||
/tc/bin/arm-webos-linux-gnueabi-readelf -d "$f" | grep -E 'SONAME|NEEDED' | sed 's/^ *//' | tr '\n' ' '; echo
|
||||
done
|
||||
/tc/bin/arm-webos-linux-gnueabi-strip --strip-unneeded "$OUT"/lib/libavcodec.so.58.* "$OUT"/lib/libavutil.so.56.* 2>/dev/null || true
|
||||
ls -la "$OUT/lib/" | awk '{print $5, $9}' | grep -E 'so\.[0-9]+\.[0-9]'
|
||||
echo "== decoders in: $(grep -oE 'h264|aac_latm|aac|mp3float|mp3' "$OUT/configure.log" | sort -u | tr '\n' ' ')"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2026 NotAlexNoyle. Apache-2.0.
|
||||
# fetch-ffmpeg.sh — the FFmpeg source for build-ffmpeg-arm.sh, on the host (the ARM container has no
|
||||
# network tools), checked against the pinned sha256. Then:
|
||||
# build/webos-oe/in-container.sh --arm build/webos-oe/build-ffmpeg-arm.sh
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
. "$HERE/fetch-lib.sh"
|
||||
VER=4.4.5
|
||||
SHA=f9514e0d3515aee5a271283df71636e1d1ff7274b15853bcd84e144be416ab07
|
||||
mkdir -p "$HERE/out-arm/ffmpeg-src"
|
||||
fetch_url "https://ffmpeg.org/releases/ffmpeg-$VER.tar.xz" "$HERE/out-arm/ffmpeg-src/ffmpeg-$VER.tar.xz" "$SHA"
|
||||
echo "ffmpeg-$VER.tar.xz in $HERE/out-arm/ffmpeg-src/ (sha256 $SHA)"
|
||||
@@ -102,6 +102,19 @@ for s in libmozavutil.so libmozavcodec.so; do
|
||||
cp -L "$DIST/bin/$s" "$OUT/$s" && echo " ffvpx: $s"
|
||||
done
|
||||
|
||||
# FFmpeg's H.264/AAC/MP3 decoders (T-501, 2026-10-16): libavcodec.so.58 + libavutil.so.56, cross-built
|
||||
# by build/webos-oe/build-ffmpeg-arm.sh (LGPL v2.1+, decoders and parsers only). The engine's
|
||||
# FFmpegRuntimeLinker dlopens libavcodec.so.58 from the library path at run time and gets H.264 and
|
||||
# AAC for <video>, <audio> and Media Source Extensions — until this every MP4 was "format not
|
||||
# supported" (only ffvpx's VP8/VP9/Opus/Vorbis/FLAC played). Shipped when built; the bundle is
|
||||
# whole without them (the formats are then refused as before).
|
||||
FFM="$REPO/build/webos-oe/out-arm/ffmpeg-arm"
|
||||
if [ -f "$FFM/lib/libavcodec.so.58" ] && [ -f "$FFM/lib/libavutil.so.56" ]; then
|
||||
cp -L "$FFM/lib/libavcodec.so.58" "$OUT/libavcodec.so.58" && cp -L "$FFM/lib/libavutil.so.56" "$OUT/libavutil.so.56" && echo " ffmpeg: libavcodec.so.58 + libavutil.so.56 (H.264, AAC, MP3; LGPL v2.1+)"
|
||||
else
|
||||
echo " ffmpeg: not built (build/webos-oe/fetch-ffmpeg.sh, then in-container.sh --arm build/webos-oe/build-ffmpeg-arm.sh) — H.264 and AAC stay unsupported in this bundle"
|
||||
fi
|
||||
|
||||
# GRE resource files the engine loads from greDir (= the bundle dir).
|
||||
#
|
||||
# `cp -L` and the REQUIRED check are both load-bearing (found on-device 2026-07-31, the hard way):
|
||||
|
||||
@@ -119,7 +119,10 @@ a pretend user for about 30 minutes. document any issues you find and then tackl
|
||||
- [~] **Performance** (the owner, 2026-10-15: "also address any performance issues you encounter"): *(2026-10-16: the daemon's memory after cards is read — closed pages are released (one detached window, the last page, no ghosts); the retention is the allocator's (no jemalloc) and libxul's paging; the per-card purge and the collections bring 428 MB to 170 MB at the last close; an engine rebuild with jemalloc is proposed (T-493). Second run: the benchmark's allocation tests 5–6× faster at the shipped threshold with the v8 sets; the sites' stalls unchanged (their own scripts); the daemon's memory after cards close is the open item — 589 MB after YouTube, never released — daemon 9 purges on the last card's close, to measure. First run measured; the cause found — non-incremental full garbage collections at the engine's 30 MB allocation threshold, and the blocker's 100,000 strings making each one 3.5× longer; fixed by `javascript.options.mem.gc_allocation_threshold_mb` 160 + high-water mark 192 (measured on the device, the ledger) and by component v6's typed-array host sets; the second run measures the result.)* every visit records time to first paint, time to load-done, the paint pipeline's average and lowest frame rate during the dwell, the worst tick (ms of work in one 100 ms period), the daemon's RSS after the visit and the load average; a visit that takes more than 30 s to load-done, paints under 5 fps, shows the slow-script dialog, or leaves the daemon more than 40 MB heavier than the visit before is an issue with a row; memory across the whole session (first visit to last) is in the ledger; the fixable ones are fixed and re-measured. *(2026-10-16 03:40, T-493: the engine rebuilt with jemalloc, the allocator in the daemon; after three heavy pages and the last close 149 MB resident, was 170; `heap-allocated` reports now, 60 MB; the numbers in the ledger.)*
|
||||
- [x] YouTube, the heaviest page of the round, is sped up by a measured lever (the owner, 2026-10-16: "speed up youtube"): the identity rule above; the before/after table is in the ledger (T-497).
|
||||
- [x] The web platform members this engine lacks and modern pages assume (checked in the engine's source, then on the device) are supplied to each page before its scripts run, each only where the engine has none, each tested on its own page (`render/goanna/test/jihad-compat.html`): CSSStyleSheet.replace, popover, inert, Intl.ListFormat, reportError, checkVisibility, AbortSignal.any, URLSearchParams.size, Array.fromAsync, Blob.text, Response.json, Promise.try, the Set methods, the iterator helpers, ReadableStream iteration, crypto.randomUUID, scheduler.postTask, requestVideoFrameCallback, canvas.roundRect. *(2026-10-16, T-498: 16/16 on the device; Etsy's search styled; the ledger lists what stays engine work.)*
|
||||
- [ ] A page laid out wider than the window gets a layout viewport as wide as its content, so what it parks just past `innerWidth` stays off screen while the card fit-zooms the whole width. *(T-499, open: DuckDuckGo's slide-out menu at x = 772 in a 768 window on a 1000-px page; the ledger has the measurement and the design.)*
|
||||
- [ ] A page laid out wider than the window gets a layout viewport as wide as its content, so what it parks just past `innerWidth` stays off screen while the card fit-zooms the whole width. *(T-499: DuckDuckGo's slide-out menu at x = 772 in a 768 window on a 1000-px page; built in daemon 17, `jihad.viewport.followOverflow`. T499_KIT)*
|
||||
- [ ] A stall is named before it is fought: the daemon's stall sampler (`$JIHAD_STALL_SAMPLER`) and `tools/host/stall-resolve.py` say which part of the engine held the main thread, per stall, from the device. *(2026-10-16, T-500: 33 stalls on five pages resolved — the pages' JavaScript two thirds, compile and the collections the rest; the clock at 192–384 MHz under load.)*
|
||||
- [ ] The CPU runs at its full clock while a page loads or runs script, and idles afterwards: the daemon holds the frequency floor while the engine has work (`jihad.cpu.hold`), never touching the governor. *(T-500. T500_KIT)*
|
||||
- [ ] MP4 video and AAC audio play, in `<video>`, `<audio>` and Media Source Extensions, through FFmpeg's decoders shipped with the browser (LGPL v2.1+, decoders only). *(T-501. T501_KIT)*
|
||||
**Dependencies:** R1
|
||||
|
||||
## Non-goals
|
||||
|
||||
@@ -524,7 +524,9 @@ loading the modern web." Kit: cavekit-modern-web.md R1–R5.
|
||||
| T-496 | The identity read on the wire and the second signature: `render/goanna/test/jihad-identity-server.py` (a host-side site that fakes `cf-mitigated: challenge` and Startpage's `/sp/captcha-block` redirect, logs every request's header, and has pages that report `navigator.userAgent`); the component's `_challengeOf` with the redirect signature; the no-loop branch, the frames' header and the third-party frame measured; eBay and old Reddit shown not to be the identity's (curl from the host, both tokens); daemon 12's console line carries the column (`source:line:column`) for the `NS_ERROR_NOT_INITIALIZED` bundles | cavekit-modern-web.md | R3, R1 | T-495 | yes | M | DONE (2026-10-16 12:13–12:25 on Enyo: every box of R3 ticked but Startpage's search, which meets Anubis's proof of work; `docs/MODERN-WEB.md` T-496) |
|
||||
| T-497 | YouTube sped up (the owner 2026-10-16: "speed up youtube"): the desktop watch page measured against the mobile site on the host (13.7 MB vs 3.0 MB of script) and on the device (worst stall 24.4 s vs 5.3 s, RSS 274 vs 162 MB, the home feed in 18 s); `jihad.ua.sites` rules in `components/jihadUserAgent.js` (header + docShell pin), youtube.com → a mobile Firefox identity; the daemon's youtube.com row out; Settings → General "Sites with a set identity" | cavekit-modern-web.md | R3, R6 | T-495 | yes | M | DONE (2026-10-16; the ledger T-497; the tap on the mobile player is a finger test) |
|
||||
| T-498 | The web compatibility shims (the owner 2026-10-16: "fix the regular duckduckgo"): `components/jihadWebCompat.js` — a sandbox per document defines what the engine lacks (CSSStyleSheet.replace, popover, inert, Intl.ListFormat, reportError, checkVisibility, AbortSignal.any, URLSearchParams.size, Array.fromAsync) plus one agent sheet; `jihad.compat.trace` names the engine call that throws an XPCOM result to a page; the API probe page's second list; registered by the bundle, created by the daemon (daemon 13) | cavekit-modern-web.md | R1, R6 | T-496 | yes | M | DONE (2026-10-16: 16/16 on the shims' test page on the device; Etsy's 47 errors gone and its search styled; DuckDuckGo's results render — its open menu panel is T-499; all three variants carry the component) |
|
||||
| T-499 | The layout viewport follows a page's overflow (found 2026-10-16: DuckDuckGo's slide-out menu, parked at innerWidth + 4 = 772, is on screen because the page is 1000 wide and the card fit-zooms the whole width): an offscreen engine window as wide as the content once per load, the card's size kept apart from the engine's in the pan/floor arithmetic (`BrowserPageGoanna.cpp` emitGeometry/setScrollPosition, `GoannaRenderPage.cpp`), pref-gated, measured on DuckDuckGo, Wikipedia, BBC, YouTube mobile, with rotation | cavekit-modern-web.md | R6 | T-498 | yes | L | OPEN (designed in the ledger's T-499; the engine has no CSS-viewport setter) |
|
||||
| T-499 | The layout viewport follows a page's overflow (found 2026-10-16: DuckDuckGo's slide-out menu, parked at innerWidth + 4 = 772, is on screen because the page is 1000 wide and the card fit-zooms the whole width): an offscreen engine window as wide as the content once per load, the card's size kept apart from the engine's in the pan/floor arithmetic (`BrowserPageGoanna.cpp` emitGeometry/setScrollPosition, `GoannaRenderPage.cpp`), pref-gated, measured on DuckDuckGo, Wikipedia, BBC, YouTube mobile, with rotation | cavekit-modern-web.md | R6 | T-498 | yes | L | T499_STATUS |
|
||||
| T-500 | The stall (the owner 2026-10-16: "fix the stall"): a leaf-PC stall sampler in the daemon (`$JIHAD_STALL_SAMPLER`, `tools/host/stall-resolve.py` against the unstripped libxul) names what holds the thread — the pages' own JavaScript, compile, the collections; the clock found at 192–384 MHz under load (Palm's ondemandtcl ramps on a touch, up_threshold 95); the clock hold in `JihadBrowserServer.cpp` (the frequency floor at the top while the engine has work, released after 2 s idle; pref `jihad.cpu.hold`); never the governor (it wedged the kernel's cpufreq lock: a reboot) | cavekit-modern-web.md | R6 | T-498 | yes | L | T500_STATUS |
|
||||
| T-501 | H.264 and AAC: FFmpeg 4.4.5's decoders cross-built for the device (`build/webos-oe/fetch-ffmpeg.sh`, `build-ffmpeg-arm.sh`: libavcodec.so.58 + libavutil.so.56, LGPL v2.1+, decoders and parsers only), shipped by the bundle when built, NOTICE and `licenses/runtime/`; the engine's FFmpegRuntimeLinker loads them for `<video>`, `<audio>` and Media Source Extensions | cavekit-gre-widgets.md | R1 | — | yes | M | T501_STATUS |
|
||||
| T-484 | Wikipedia's layout decided and recorded; every fix of T-481…T-483 re-tested and its ledger row updated with a screenshot; the README hero retaken on sites that work | cavekit-modern-web.md | R1, R5 | T-481, T-482, T-483 | yes | S | DONE (Wikipedia: desktop kept; the README hero recomposed 14:52 — Internet Archive, BBC News, MDN without adverts, unthemed, Aero backdrop — in `docs/screenshots/cardview.jpg`) |
|
||||
|
||||
## Tier 84 — One repository builds Jihad on any Linux (2026-10-14; added to the site, not a regeneration)
|
||||
|
||||
@@ -492,6 +492,61 @@ trials, not a patch for this round. Until then the regular DuckDuckGo page shows
|
||||
with the menu panel standing at the top (the shot `t-ddg`), and DuckDuckGo Lite stays the
|
||||
default search.
|
||||
|
||||
**T-499, built (daemon 17, 2026-10-16 afternoon):** `GoannaRenderPage::SetLayoutSize` sets the
|
||||
engine's window apart from the card (the headless widget and the base window resized, the reflow
|
||||
forced as `Resize` does it; `Width()`/`Height()` stay the card's, so the pan, the buffer and the
|
||||
fit-zoom are untouched); `emitGeometry` calls it ONCE per page and card size when the content is
|
||||
wider than the card by more than 8 px, up to 1100 px, for a page that declares no meta viewport,
|
||||
then re-measures the content; `resetScrollForNavigation` and `setWindowSize` (rotation) reset
|
||||
the once. Pref `jihad.viewport.followOverflow` (on). T499_DEVICE_RESULT
|
||||
|
||||
**T-500 — the stall, measured to the function (2026-10-16 15:00–16:30; the owner: "fix the
|
||||
stall").** The tick log already said every stall is in the PUMP (the 40.9 s GitLab tick: pump
|
||||
40.6 s, paint 0.3 s), and the pump is one `NS_ProcessNextEvent` at a time — one event holds the
|
||||
thread. This engine build has no Gecko profiler (`tools/profiler`: no objects), so the daemon got
|
||||
a sampler of its own (`GoannaRenderPage.cpp`, `$JIHAD_STALL_SAMPLER=<ms>`, diagnosis only): a
|
||||
thread sends SIGPROF to the main thread every 10 ms once the current event has run longer than
|
||||
the threshold, the handler records the program counter, and after the event every sample goes to
|
||||
`/tmp/jihad-stall.txt` as module+offset, which `tools/host/stall-resolve.py` resolves with the
|
||||
toolchain's addr2line against the unstripped libxul (1.1 GB of debug info, the same build) and
|
||||
sorts into buckets. 33 stalls over five pages (`docs/rounds/t500-2026-10-16/`):
|
||||
|
||||
| stall | page | what held the thread (10 ms samples) |
|
||||
|---|---|---|
|
||||
| 32.4 s | DuckDuckGo results | the page's compiled JavaScript 24%, the JS runtime (shapes, property lookups, inline caches, `Interpret`) 15%, JIT compile 8%, GC 8%, the rest the long tail of the runtime |
|
||||
| 44.4 s | GitLab explore | JIT code 12%, runtime 13%, parse 8%, JIT compile 8%, GC 5%, style (`SelectorMatches`, CSS variables) 3% |
|
||||
| 46.1 s | old Reddit's login page | JIT code 19%, runtime 19%, GC 10%, compile 7% |
|
||||
| 5.1 s, 5.0 s | YouTube mobile (two) | **regular-expression compilation** (irregexp → ARM code) and baseline-JIT compile 27–33%, runtime 39% |
|
||||
| 4.7 s, 5.2 s | DuckDuckGo, GitLab (two) | **cycle collection** (graph build, `CellIsMarkedGray`, the purple buffer's hash tables) 11–15% plus its GC 10–16% — a full CC+GC pair of ~5 s each |
|
||||
| 8.6 s | DuckDuckGo | parse 21%, baseline compile 15%, runtime 10%, GC 8% |
|
||||
|
||||
So the big stalls are the pages' own JavaScript running (two thirds, counting the runtime it
|
||||
calls into), with compile and the collections the rest; nothing in the daemon, the blocker or the
|
||||
paint. **And the clock:** while the DuckDuckGo page loaded, the CPU sat at **192 or 384 MHz for
|
||||
most of the seconds** (a sample a second: `1188 1188 1188 1026 384 192 192 192 192 192 384 918
|
||||
384 192 1188 384 192 …`) with the load average at 4 — the TouchPad's governor is Palm's
|
||||
`ondemandtcl`, which ramps on a touch tickle from the system UI, not on load; the chip does 1188.
|
||||
The engine ran the page's script at a third to a sixth of the device's speed. THE TRAP: an A/B
|
||||
that wrote `performance` to `scaling_governor` wedged the kernel's cpufreq lock — every cpufreq
|
||||
read and every `luna-send` hung, upstart still answered, `reboot` hung, only `reboot -f` got
|
||||
out (a reboot and an hour; memory `cpufreq-governor-switch-wedges-the-touchpad`). So the fix
|
||||
never touches the governor: **the clock hold** (`JihadBrowserServer.cpp`, daemon 16, pref
|
||||
`jihad.cpu.hold`) raises the frequency FLOOR to the top while the engine has work (a busy page or
|
||||
a tick with real work; a stall's first busy tick comes before it) and lets it go after two seconds
|
||||
of idle ticks. T500_HOLD_RESULT
|
||||
|
||||
**T-501 — H.264 and AAC (the README's "H.264 in Media Source Extensions" caveat).** The engine
|
||||
is built with `MOZ_FFMPEG` and loads a system libavcodec 58–61 at run time for what its own
|
||||
ffvpx does not decode; the device has none. `build/webos-oe/build-ffmpeg-arm.sh` (after
|
||||
`fetch-ffmpeg.sh`, sha256 pinned) cross-builds FFmpeg 4.4.5 in the ARM container with the
|
||||
engine's flags: decoders and parsers for H.264, AAC and MP3 only, no demuxers, no GPL parts,
|
||||
LGPL v2.1+ — `libavcodec.so.58` (1.66 MB) and `libavutil.so.56` (0.59 MB), shipped by the bundle
|
||||
when built, their notice in `NOTICE` and the licence text in `licenses/runtime/`. One
|
||||
non-technical note for the owner: H.264 and AAC are patented in some countries; shipping a
|
||||
decoder is a distributor's question (Debian ships FFmpeg; Fedora did not until 2022). The
|
||||
container has no curl (the tarball is fetched on the host) and links against the toolchain's own
|
||||
sysroot (the Debian one has no C runtime objects for this gcc). T501_DEVICE_RESULT
|
||||
|
||||
**Measured with daemon 11 (2026-10-16 03:33–03:40, Mochi, the same three pages as daemon 10's
|
||||
run; `memory-report-read.py` on the dump):**
|
||||
|
||||
|
||||
@@ -5,6 +5,51 @@ reference material for a specific subsystem, not a status document.
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-16 (evening) delta — the README's caveats worked: the stall, H.264, the viewport (READ FIRST)
|
||||
|
||||
**The owner:** "fix the stall"; then "move all caveats in the readme into a dedicated caveats section
|
||||
at the bottom. also change the language to be less corporate advertisement like while still being
|
||||
2011 era appropriate" (done, `4a76dbae`); then "work to address all of the caveats in the README.md
|
||||
which can reasonably be classified as solvable problems". The ledger (`docs/MODERN-WEB.md`) has
|
||||
T-499 (the viewport, built), T-500 (the stall) and T-501 (H.264/AAC) with the numbers.
|
||||
|
||||
**T-500, the stall:** the daemon has a stall sampler (`$JIHAD_STALL_SAMPLER=<ms>`,
|
||||
`GoannaRenderPage.cpp`; resolve with `tools/host/stall-resolve.py` against the unstripped libxul in
|
||||
the obj dir — addr2line over ten thousand addresses takes ~10 min on the host). 33 stalls resolved:
|
||||
the pages' own JavaScript (JIT code + the runtime) two thirds, compile (baseline, Ion, regular
|
||||
expressions) and GC/CC the rest; two 5-s stalls are cycle collections. The clock: Palm's
|
||||
`ondemandtcl` governor (up_threshold 95, sampling 200 ms, max_tickle_window 3 s) kept the CPU at
|
||||
192–384 MHz through a page load. **The clock hold** (`JihadBrowserServer.cpp` tick loop, daemon 16/17,
|
||||
pref `jihad.cpu.hold`): the frequency floor (`scaling_min_freq`, both cores; cpu1 is hot-plugged)
|
||||
goes to the top while the engine has work, back after 2 s of idle ticks. **THE TRAP OF THE DAY** (a
|
||||
reboot): writing `scaling_governor` wedged the kernel's cpufreq lock — every cpufreq read and
|
||||
every `luna-send` hung, `reboot` hung, only `novacom run file://sbin/reboot -- -f` got out; memory
|
||||
`cpufreq-governor-switch-wedges-the-touchpad`. A floor write is safe (tested with capped reads).
|
||||
T500_PICKUP
|
||||
|
||||
**T-501, H.264/AAC:** `build/webos-oe/fetch-ffmpeg.sh` (host, sha256 pinned) then
|
||||
`in-container.sh --arm build/webos-oe/build-ffmpeg-arm.sh` → `out-arm/ffmpeg-arm/lib/libavcodec.so.58`
|
||||
+ `libavutil.so.56` (FFmpeg 4.4.5, H.264/AAC/MP3 decoders and parsers only, LGPL v2.1+, linked
|
||||
against the toolchain's own sysroot — the Debian one has no crt1.o). The bundle ships them when
|
||||
built; NOTICE and `licenses/runtime/ffmpeg-COPYING.LGPLv2.1`. Patents on H.264/AAC: the owner's
|
||||
call, noted in NOTICE. T501_PICKUP
|
||||
|
||||
**T-499, the viewport:** `GoannaRenderPage::SetLayoutSize` + the once-per-page rule in
|
||||
`emitGeometry` (content wider than the card by 8 px, ≤ 1100 px, no meta viewport; pref
|
||||
`jihad.viewport.followOverflow`); reset on navigation and on `setWindowSize`. T499_PICKUP
|
||||
|
||||
**Not solvable from here (the README's Caveats say so):** the device scope; the system's own TLS;
|
||||
WebGL (no GPU driver); Flash's availability; `palm-install`; Akamai's and Reddit's gates;
|
||||
GitHub's empty parts — its page uses an import map (120 module preloads under it), which the
|
||||
engine lacks, and GitHub's script policy allows only its asset host, so an import-map shim (which
|
||||
loads modules from blob URLs) cannot run there: engine work. Mastodon's timeline: not looked at
|
||||
again (needs a console-on visit). The double-tap/pinch sharpness: PICKUP's 2026-10 note stands
|
||||
(cause not found).
|
||||
|
||||
**Device state:** DEVICE_STATE_EVENING
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-16 (afternoon) delta — the harness, the second signature, YouTube's mobile site, the web-compat shims (READ FIRST)
|
||||
|
||||
**The owner:** "continue" (the open items of the morning), then "fix the regular duckduckgo, and
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Rendering options for Jihad — what the TouchPad measured (2026-10-17)
|
||||
|
||||
The report the owner reads is the Claude doc "Jihad rendering options: speed comparison"; this page
|
||||
keeps the measurements in the repository with the tools that took them. Every number is from the
|
||||
TouchPad (webOS 3.0.5, APQ8060, Adreno 220).
|
||||
|
||||
## The software pipeline as it runs (the daemons' logs, 1958 paints since a boot)
|
||||
|
||||
| Stage | Cost per 1024×768 frame |
|
||||
|---|---|
|
||||
| Paint the document into the headless widget's buffer (cairo/skia on the CPU) | 62–73 ms per megapixel: screen-sized band median 53 ms (p90 182), full-page band median 120 ms (p90 274), worst 2.4–4.4 s |
|
||||
| Copy into the YAP shared memory (3 MB) | 4–5 ms |
|
||||
| The card's blit with the nearest-neighbour fit scale | 8 ms scaled, 2–4 ms at 1:1 |
|
||||
| LunaSysMgr's composite (GPU) | ~4 ms |
|
||||
|
||||
Frame gaps while painting: 35% under 16 ms, 1% 16–23, 6% 24–31, 16% 32–39, 17% 40–55, 24% over 56.
|
||||
The caps in `BrowserPageGoanna.cpp`: one dirty repaint per 150 ms, one pan repaint per 250 ms.
|
||||
|
||||
## The GPU (`tools/device/gpu-bench.c`, vendor EGL 1.4 / OpenGL ES 2.0, two runs)
|
||||
|
||||
| Stage | run 1 | run 2 |
|
||||
|---|---|---|
|
||||
| EGL + pbuffer + shader setup | 135 ms | 82 ms |
|
||||
| Upload a 1024×768 RGBA texture (3 MB) | 12.1 ms | 8.8 ms |
|
||||
| Composite one opaque full-screen layer | 3.8 ms | 3.7 ms |
|
||||
| Composite four layers, three blended | 13.5 ms | 12.9 ms |
|
||||
| Twelve 256×256 `glTexSubImage2D` updates (3 MB) + composite | 169 ms | 158 ms |
|
||||
| `glReadPixels` 1024×768 RGBA | 14.4 ms | 11.3 ms |
|
||||
| Whole readback frame (12 tile updates + 2 layers + read) | 45 ms | 64 ms |
|
||||
| CPU `memcpy` 3 MB | 5.0 ms | 4.2 ms |
|
||||
| CPU blend of one 1024×768 ARGB layer (plain C) | 22.5 ms | 19.7 ms |
|
||||
| CPU nearest-neighbour scale 1000→768 wide | 8.2 ms | 8.1 ms |
|
||||
|
||||
`GL_EXT_texture_format_BGRA8888` present, `GL_EXT_read_format_bgra` absent. The one pathological
|
||||
path is the sub-image tile update; whole-texture uploads run at 250–340 MB/s.
|
||||
|
||||
## What the other UXP browsers do (the platform source, `third_party/uxp`)
|
||||
|
||||
`gfxPlatform::AccelerateLayersByDefault()` (`gfx/thebes/gfxPlatform.cpp:2126`) is true on Windows,
|
||||
macOS and GTK: Pale Moon and Basilisk composite on the GPU by default (Direct3D 11 / OpenGL / OpenGL
|
||||
over GLX), rasterize content with Direct2D (Windows), CoreGraphics (macOS) or cairo (Linux), draw
|
||||
canvas with Direct2D, Skia-on-GL (`gfx.canvas.azure.accelerated`, macOS) or Skia on the CPU
|
||||
(Linux), and have WebGL on through WGL/ANGLE, CGL or GLX. The headless toolkit Jihad builds gets
|
||||
`gfxPlatformHeadless` (cairo, software layers) and the `Null` GL provider. The application pref
|
||||
files on repo.palemoon.org sit behind Anubis and were not read.
|
||||
|
||||
## The options (the report's table, in short)
|
||||
|
||||
1. Software tuning — NEON blend/scale, fewer copies: 40–45 ms frames, 8–12 fps scroll; days; all builds.
|
||||
2. WebGL through EGL with readback — 15–20 fps for a full-screen GLES 2.0 canvas; 1–2 weeks.
|
||||
3. Skia GPU canvas on 2's context — days more; readback per canvas.
|
||||
4. OMTC on the GPU with readback (Atlas's shape) — 25–30 fps scroll/animation, 4–8 weeks, high risk.
|
||||
5. Video through the MDP overlay (Atlas's `gst-mdpoverlay`) — decode-speed playback, 2–3 weeks.
|
||||
6. Zero-copy GPU frames to the card — 40–60 fps ceiling, LunaSysMgr change, webOS CE only, on top of 4.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
@@ -0,0 +1,43 @@
|
||||
viewport script ok
|
||||
=== 1. the clock hold (daemon 16)
|
||||
== daemon b99349bf; governor ondemandtcl; floor before 192000
|
||||
== running, process 9609;
|
||||
@@ ddg-H | governor=ondemandtcl | first-paint=15s load=53s worst-tick=10762ms | seconds at >=1 GHz: 51 of 60
|
||||
floor during: 1188000; after 4 s idle:
|
||||
floor now 1188000 (expected 192000 back)
|
||||
@@ gitlab-H | governor=ondemandtcl | first-paint=9s load=56s worst-tick=32219ms | seconds at >=1 GHz: 60 of 60
|
||||
@@ yt-home-H | governor=ondemandtcl | first-paint=1s load=42s worst-tick=33437ms | seconds at >=1 GHz: 50 of 50
|
||||
@@ yt-watch-H | governor=ondemandtcl | first-paint=1s load=50s worst-tick=23323ms | seconds at >=1 GHz: 60 of 60
|
||||
== floor after the round: 1188000; hold lines: 2
|
||||
=== 2. ffmpeg
|
||||
== libs: 1663636 592392
|
||||
== running, process 13438
|
||||
== codecs page title: jihad-codecs.html back=0 fwd=0
|
||||
ffmpeg lines:
|
||||
media errors:
|
||||
== youtube: load-done=1; media lines:
|
||||
99587 /tmp/shot-ff-codecs.png
|
||||
641782 /tmp/shot-ff-yt.png
|
||||
ff-codecs 99587
|
||||
ff-yt 641782
|
||||
=== 3. daemon 17: the layout viewport
|
||||
== daemon 67640a59
|
||||
== running, process 15330; clock hold: released
|
||||
@@ ddg | load-done=1 | viewport: layout viewport 980x1207 (card 768x946) | geometry: contentSize=1000x3323 win=768x946 mZoom=0.7680;contentSize=1000x3320 win=768x946 mZoom=0.7680; | worst-tick=15556ms
|
||||
@@ wiki | load-done=1 | viewport: | geometry: contentSize=768x18931 win=768x946 mZoom=1.0000;contentSize=768x16560 win=768x946 mZoom=1.0000; | worst-tick=5484ms
|
||||
@@ bbc | load-done=0 | viewport: | geometry: contentSize=768x946 win=768x946 mZoom=1.0000;contentSize=768x7863 win=768x946 mZoom=1.0000; | worst-tick=3273ms
|
||||
@@ ytm | load-done=1 | viewport: | geometry: contentSize=768x2420 win=768x946 mZoom=1.0000;contentSize=768x946 win=768x946 mZoom=1.0000; | worst-tick=6413ms
|
||||
@@ harness | load-done=0 | viewport: | geometry: | worst-tick=26672ms
|
||||
== floor after: 1188000; hold lines: 4; daemons 1
|
||||
204698 /tmp/shot-vp-bbc.png
|
||||
234587 /tmp/shot-vp-ddg.png
|
||||
115174 /tmp/shot-vp-harness.png
|
||||
326385 /tmp/shot-vp-wiki.png
|
||||
54894 /tmp/shot-vp-ytm.png
|
||||
vp-ddg 234587
|
||||
vp-wiki 326385
|
||||
vp-bbc 204698
|
||||
vp-ytm 54894
|
||||
vp-harness 115174
|
||||
|
||||
[exited with code 0]
|
||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,206 @@
|
||||
|
||||
[exited with code 0]
|
||||
daemon put ok
|
||||
== daemon 07cd73c4
|
||||
== console daemon: 1 process(es); [jihad-bs] js-console bridge on (ALL sources)
|
||||
== [jihad-bs] stall sampler on: an event longer than 1000 ms is sampled every 10 ms (the pc); samples to /tmp/jihad-stall.t
|
||||
@@ yt-home | 72s | load-done=1 | first-paint=12s load=31s worst-tick=8903ms | stalls: 6
|
||||
stall 1: an event ran 5142 ms, 396 samples: libxul.so 89%, libmemcpy.so 7%, jihad-browserserver 1%, libc.so.6 0%, libpthread.so.0 0%,
|
||||
stall 2: an event ran 8565 ms, 738 samples: libxul.so 86%, jihad-browserserver 4%, libpthread.so.0 3%, libmemcpy.so 2%, ld-2.23.so 0%, libc.so.6 0%, JIT code 1%
|
||||
stall 3: an event ran 1270 ms, 26 samples: libxul.so 57%, libc.so.6 19%, libpthread.so.0 19%, jihad-browserserver 3%,
|
||||
stall 4: an event ran 1331 ms, 32 samples: libxul.so 56%, libpthread.so.0 28%, jihad-browserserver 3%, libc.so.6 3%, JIT code 9%
|
||||
stall 5: an event ran 1418 ms, 40 samples: libxul.so 80%, jihad-browserserver 5%, libc.so.6 2%, libpthread.so.0 2%, JIT code 10%
|
||||
stall 6: an event ran 2902 ms, 184 samples: libxul.so 73%, libpthread.so.0 6%, jihad-browserserver 3%, libc.so.6 2%, libgcc_s.so.1 2%, libmemcpy.so 1%, JIT code 9%
|
||||
GC worst: | CC worst: | GC lines 3 CC lines 2
|
||||
@@ ddg | 75s | load-done=0 | first-paint=23s load=-s worst-tick=34318ms | stalls: 9
|
||||
stall 7: an event ran 32374 ms, 2958 samples: libxul.so 65%, libpthread.so.0 5%, jihad-browserserver 1%, libc.so.6 1%, libmemcpy.so 0%, ld-2.23.so 0%, JIT code 24%
|
||||
stall 8: an event ran 1591 ms, 58 samples: libxul.so 94%, libmemcpy.so 3%, jihad-browserserver 1%,
|
||||
stall 9: an event ran 1294 ms, 29 samples: libxul.so 72%, jihad-browserserver 3%, libpthread.so.0 3%, ld-2.23.so 3%, libmemcpy.so 3%, JIT code 13%
|
||||
stall 10: an event ran 1601 ms, 58 samples: libxul.so 81%, libc.so.6 3%, libpthread.so.0 1%, libmemcpy.so 1%, JIT code 12%
|
||||
stall 11: an event ran 4501 ms, 335 samples: libxul.so 85%, jihad-browserserver 3%, libpthread.so.0 2%, libmemcpy.so 2%, libgcc_s.so.1 0%, libc.so.6 0%, JIT code 3%
|
||||
stall 12: an event ran 2347 ms, 133 samples: libxul.so 75%, libpthread.so.0 9%, jihad-browserserver 1%, ld-2.23.so 0%, libgcc_s.so.1 0%, libmemcpy.so 0%, JIT code 11%
|
||||
stall 13: an event ran 4684 ms, 349 samples: libxul.so 97%, libmemcpy.so 1%, jihad-browserserver 0%, libc.so.6 0%,
|
||||
stall 14: an event ran 8647 ms, 731 samples: libxul.so 76%, libpthread.so.0 6%, jihad-browserserver 2%, libc.so.6 2%, libmemcpy.so 0%, ld-2.23.so 0%, JIT code 8%
|
||||
stall 15: an event ran 1092 ms, 9 samples: libxul.so 44%, libpthread.so.0 11%, JIT code 44%
|
||||
GC worst: | CC worst: | GC lines 4 CC lines 2
|
||||
@@ gitlab | 92s | load-done=1 | first-paint=11s load=-s worst-tick=46435ms | stalls: 9
|
||||
stall 16: an event ran 8799 ms, 750 samples: libxul.so 75%, libpthread.so.0 6%, jihad-browserserver 3%, libc.so.6 1%, libmemcpy.so 0%, ld-2.23.so 0%, JIT code 11%
|
||||
stall 17: an event ran 5239 ms, 397 samples: libxul.so 91%, libpthread.so.0 4%, libc.so.6 1%, libmemcpy.so 1%, libgcc_s.so.1 0%, jihad-browserserver 0%,
|
||||
stall 18: an event ran 1580 ms, 57 samples: libxul.so 89%, libc.so.6 5%, jihad-browserserver 3%, libmemcpy.so 1%,
|
||||
stall 19: an event ran 1085 ms, 8 samples: libxul.so 100%,
|
||||
stall 20: an event ran 1071 ms, 7 samples: libxul.so 71%, jihad-browserserver 14%, ld-2.23.so 14%,
|
||||
stall 21: an event ran 44441 ms, 4167 samples: libxul.so 76%, libpthread.so.0 5%, jihad-browserserver 2%, libmemcpy.so 1%, libc.so.6 0%, ld-2.23.so 0%, JIT code 12%
|
||||
stall 22: an event ran 1107 ms, 10 samples: libxul.so 100%,
|
||||
stall 23: an event ran 3617 ms, 248 samples: libxul.so 98%, libpthread.so.0 1%, libmemcpy.so 0%,
|
||||
stall 24: an event ran 1361 ms, 34 samples: libxul.so 76%, jihad-browserserver 8%, libpthread.so.0 5%, ld-2.23.so 2%, JIT code 5%
|
||||
GC worst: | CC worst: | GC lines 5 CC lines 4
|
||||
@@ oldreddit | 73s | load-done=0 | first-paint=1s load=-s worst-tick=50833ms | stalls: 2
|
||||
stall 25: an event ran 46124 ms, 4263 samples: libxul.so 71%, libpthread.so.0 4%, jihad-browserserver 1%, libmemcpy.so 0%, libc.so.6 0%, libgcc_s.so.1 0%, JIT code 19%
|
||||
stall 26: an event ran 1018 ms, 1 samples: libfontconfig.so.1 100%,
|
||||
GC worst: | CC worst: | GC lines 3 CC lines 1
|
||||
@@ yt-watch | 74s | load-done=1 | first-paint=12s load=35s worst-tick=7688ms | stalls: 7
|
||||
stall 27: an event ran 2082 ms, 94 samples: libxul.so 77%, libpthread.so.0 11%, jihad-browserserver 2%, libmemcpy.so 2%, libc.so.6 2%, JIT code 4%
|
||||
stall 28: an event ran 4952 ms, 376 samples: libxul.so 91%, libmemcpy.so 5%, jihad-browserserver 1%, libpthread.so.0 0%, libc.so.6 0%, ld-2.23.so 0%,
|
||||
stall 29: an event ran 1054 ms, 5 samples: libxul.so 100%,
|
||||
stall 30: an event ran 3327 ms, 229 samples: libxul.so 91%, jihad-browserserver 3%, libmemcpy.so 1%, libpthread.so.0 1%, JIT code 1%
|
||||
stall 31: an event ran 2572 ms, 153 samples: libxul.so 86%, libpthread.so.0 4%, jihad-browserserver 3%, ld-2.23.so 1%, libc.so.6 1%, libmemcpy.so 0%, JIT code 1%
|
||||
stall 32: an event ran 7617 ms, 646 samples: libxul.so 84%, libmemcpy.so 4%, jihad-browserserver 4%, libpthread.so.0 3%, libgcc_s.so.1 0%, ld-2.23.so 0%, JIT code 2%
|
||||
stall 33: an event ran 1345 ms, 34 samples: libxul.so 73%, libc.so.6 5%, libmemcpy.so 5%, libpthread.so.0 5%, jihad-browserserver 2%, JIT code 5%
|
||||
GC worst: | CC worst: | GC lines 4 CC lines 2
|
||||
== samples file: 17588 lines, 343089 bytes
|
||||
== restored: (103783/365115456) jihad-mochi (start) running, process 3899; daemons: 1
|
||||
tmpfs 40960 1608 39352 4% /tmp
|
||||
=== exit 0
|
||||
samples: 17698 lines
|
||||
=== stall 1: 5142 ms, 396 samples (10 ms each)
|
||||
--- by bucket:
|
||||
38.9% JS interpreter / runtime
|
||||
27.0% JIT compile (Ion/Baseline)
|
||||
20.2% other
|
||||
7.6% other module: libmemcpy.so
|
||||
2.5% GC
|
||||
1.3% other module: jihad-browserserver
|
||||
0.8% other module: libc.so.6
|
||||
0.8% other module: libpthread.so.0
|
||||
0.8% Memory (malloc, strings)
|
||||
0.3% Style (selectors, restyle)
|
||||
--- top 25 functions:
|
||||
7.6% libmemcpy.so
|
||||
4.3% js::LifoAlloc::allocImpl(unsigned int)
|
||||
2.8% js::jit::MacroAssemblerARM::ma_alu(js::jit::Register, js::jit::Imm32, js::jit::Register, js::jit::AutoGenericRegisterScope<js::jit::Register
|
||||
2.5% js::detail::BumpChunk::tryAlloc(unsigned int)
|
||||
2.3% js::irregexp::RegExpBuilder::AddAtom(js::irregexp::RegExpTree*)
|
||||
2.3% js::jit::AssemblerBuffer<1024, js::jit::Instruction>::nextOffset() const
|
||||
2.0% js::jit::AssemblerBufferWithConstantPools<1024u, 4u, js::jit::Instruction, js::jit::Assembler, 0u>::putInt(unsigned int, bool)
|
||||
1.8% js::jit::AssemblerBufferWithConstantPools<1024u, 4u, js::jit::Instruction, js::jit::Assembler, 0u>::allocEntry(unsigned int, unsigned int, u
|
||||
1.8% js::irregexp::TextNode::FilterASCII(int, bool, bool)
|
||||
1.5% js::irregexp::BufferedVector<js::irregexp::RegExpTree, 2>::Add(js::LifoAlloc*, js::irregexp::RegExpTree*)
|
||||
1.5% js::jit::AssemblerBufferWithConstantPools<1024u, 4u, js::jit::Instruction, js::jit::Assembler, 0u>::insertEntryForwards(unsigned int, unsign
|
||||
1.5% EmitSimpleCharacter(js::irregexp::RegExpCompiler*, char16_t, js::jit::Label*, int, bool, bool)
|
||||
1.3% js::jit::BufferSlice<1024>::putBytes(unsigned int, void const*)
|
||||
1.3% js::jit::BaselineCompiler::emit_JSOP_RETRVAL()
|
||||
1.3% js::jit::Assembler::as_b(js::jit::Label*, js::jit::Assembler::Condition)
|
||||
1.3% js::irregexp::RegExpAlternative::RegExpAlternative(js::irregexp::InfallibleVector<js::irregexp::RegExpTree*, 1u>*)
|
||||
1.3% ??
|
||||
1.3% js::jit::AssemblerBuffer<1024, js::jit::Instruction>::putBytes(unsigned int, void const*)
|
||||
1.3% jihad-browserserver
|
||||
1.3% js::irregexp::TextNode::TextEmitPass(js::irregexp::RegExpCompiler*, js::irregexp::TextNode::TextEmitPassType, bool, js::irregexp::Trace*, bo
|
||||
1.0% js::irregexp::TextNode::Emit(js::irregexp::RegExpCompiler*, js::irregexp::Trace*)
|
||||
1.0% js::jit::LabelBase::bound() const
|
||||
1.0% js::irregexp::RegExpParser<unsigned char>::Advance()
|
||||
1.0% void mozilla::detail::VectorImpl<js::irregexp::RegExpTree*, 1u, js::LifoAllocPolicy<(js::Fallibility)1>, true>::copyConstruct<js::irregexp::
|
||||
1.0% unsigned int mozilla::RotateLeft<unsigned int>(unsigned int, unsigned char)
|
||||
|
||||
=== stall 2: 8565 ms, 738 samples (10 ms each)
|
||||
--- by bucket:
|
||||
32.1% other
|
||||
20.7% JS parse / bytecode
|
||||
15.3% JIT compile (Ion/Baseline)
|
||||
9.8% JS interpreter / runtime
|
||||
8.1% GC
|
||||
4.1% other module: jihad-browserserver
|
||||
3.1% other module: libpthread.so.0
|
||||
2.3% other module: libmemcpy.so
|
||||
1.9% JIT code (the page's compiled JavaScript)
|
||||
0.9% other module: ld-2.23.so
|
||||
0.5% other module: libc.so.6
|
||||
0.5% other module: libgcc_s.so.1
|
||||
0.4% Memory (malloc, strings)
|
||||
0.1% other module: libnspr4.so
|
||||
--- top 25 functions:
|
||||
4.1% jihad-browserserver
|
||||
3.1% libpthread.so.0
|
||||
2.3% js::frontend::TokenStream::getTokenInternal(js::frontend::TokenKind*, js::frontend::Token::Modifier)
|
||||
2.3% libmemcpy.so
|
||||
1.9% js::frontend::EmitterScope::searchInEnclosingScope(JSAtom*, js::Scope*, unsigned char)
|
||||
1.9% <JIT code>
|
||||
1.6% js::jit::BaselineCompiler::emit_JSOP_RETRVAL()
|
||||
0.9% js::detail::HashTable<js::AtomStateEntry const, js::HashSet<js::AtomStateEntry, js::AtomHasher, js::SystemAllocPolicy>::SetOps, js::SystemAl
|
||||
0.9% js::frontend::TokenStream::getCharIgnoreEOL()
|
||||
0.9% js::TraceGenericPointerRoot(JSTracer*, js::gc::Cell**, char const*)
|
||||
0.9% mozilla::psm::OCSPVerificationTrustDomain::~OCSPVerificationTrustDomain()
|
||||
0.9% ld-2.23.so
|
||||
0.9% js::BindingIter::settle()
|
||||
0.8% js::gc::Cell::getTraceKind() const
|
||||
0.8% js::jit::AssemblerBuffer<1024, js::jit::Instruction>::nextOffset() const
|
||||
0.8% js::jit::ICStubCompiler::ICStubCompiler(JSContext*, js::jit::ICStub::Kind, js::jit::ICStubCompiler::Engine)
|
||||
0.8% js::jit::AssemblerBufferWithConstantPools<1024u, 4u, js::jit::Instruction, js::jit::Assembler, 0u>::allocEntry(unsigned int, unsigned int, u
|
||||
0.8% js::BindingIter::increment()
|
||||
0.7% js::frontend::Parser<js::frontend::SyntaxParseHandler>::assignExpr(js::frontend::InHandling, js::frontend::YieldHandling, js::frontend::Trip
|
||||
0.7% ReservedWordInfo const* FindReservedWord<char16_t>(char16_t const*, unsigned int)
|
||||
0.7% Fold(js::ExclusiveContext*, js::frontend::ParseNode**, js::frontend::Parser<js::frontend::FullParseHandler>&, bool)
|
||||
0.7% js::TaggedProto::TaggedProto(js::TaggedProto const&)
|
||||
0.5% libc.so.6
|
||||
0.5% js::frontend::TokenStream::getToken(js::frontend::TokenKind*, js::frontend::Token::Modifier)
|
||||
0.5% (anonymous namespace)::NameResolver::resolve(js::frontend::ParseNode*, JS::Handle<JSAtom*>)
|
||||
|
||||
=== stall 3: 1270 ms, 26 samples (10 ms each)
|
||||
--- by bucket:
|
||||
46.2% other
|
||||
19.2% other module: libc.so.6
|
||||
19.2% other module: libpthread.so.0
|
||||
7.7% Memory (malloc, strings)
|
||||
3.8% other module: jihad-browserserver
|
||||
3.8% JIT compile (Ion/Baseline)
|
||||
--- top 25 functions:
|
||||
19.2% libc.so.6
|
||||
19.2% libpthread.so.0
|
||||
7.7% std::mutex::lock()
|
||||
7.7% mozilla::layers::DebugGLData::~DebugGLData()
|
||||
3.8% jihad-browserserver
|
||||
3.8% icu_78::internal::LocalOpenPointer<UResourceBundle, &ures_close_78>::~LocalOpenPointer()
|
||||
3.8% icu_78::Mutex::Mutex(icu_78::UMutex*)
|
||||
3.8% (anonymous namespace)::init_resb_result(UResourceDataEntry*, unsigned int, char const*, int, UResourceDataEntry*, char const*, int, UResourc
|
||||
3.8% makeResourceFrom16(ResourceData const*, int)
|
||||
3.8% ures_getNextResource_78
|
||||
3.8% icu_78::copyInvariantChars(icu_78::UnicodeString const&, icu_78::FixedString&, UErrorCode&)
|
||||
3.8% (anonymous namespace)::initFromResourceBundle(UErrorCode&)
|
||||
3.8% ??
|
||||
3.8% uprv_compareInvAscii_78
|
||||
3.8% res_getStringNoTrace_78
|
||||
3.8% js::jit::MacroAssemblerARM::ma_ldrb(js::jit::DTRAddr, js::jit::Register, js::jit::Index, js::jit::Assembler::Condition)
|
||||
|
||||
=== stall 4: 1331 ms, 32 samples (10 ms each)
|
||||
--- by bucket:
|
||||
31.2% other
|
||||
28.1% other module: libpthread.so.0
|
||||
15.6% JS interpreter / runtime
|
||||
9.4% JIT code (the page's compiled JavaScript)
|
||||
6.2% JIT compile (Ion/Baseline)
|
||||
3.1% GC
|
||||
3.1% other module: jihad-browserserver
|
||||
3.1% other module: libc.so.6
|
||||
--- top 25 functions:
|
||||
28.1% libpthread.so.0
|
||||
9.4% <JIT code>
|
||||
3.1% js::detail::HashTable<js::HashMapEntry<js::gc::Cell*, unsigned long long>, js::HashMap<js::gc::Cell*, unsigned long long, js::PointerHasher<
|
||||
3.1% js::jit::ICEntry::pc(JSScript*) const
|
||||
3.1% js::NativeObject::addPropertyInternal(js::ExclusiveContext*, JS::Handle<js::NativeObject*>, JS::Handle<jsid>, bool (*)(JSContext*, JS::Handl
|
||||
3.1% JS::Heap<JS::Value>::set(JS::Value const&)
|
||||
3.1% JSContext::runningWithTrustedPrincipals() const
|
||||
3.1% js::TaggedProto::isObject() const
|
||||
3.1% js::BindingIter::increment()
|
||||
3.1% jihad-browserserver
|
||||
3.1% js::jit::AssemblerBufferWithConstantPools<1024u, 4u, js::jit::Instruction, js::jit::Assembler, 0u>::allocEntry(unsigned int, unsigned int, u
|
||||
3.1% js::MutableWrappedPtrOperations<JS::Value, JS::MutableHandle<JS::Value> >::set(JS::Value const&)
|
||||
3.1% js::TypeSet::ObjectKey::unknownProperties()
|
||||
3.1% js::HeapTypeSetKey::constant(js::CompilerConstraintList*, JS::Value*)
|
||||
3.1% libc.so.6
|
||||
3.1% js::ShapeTable::Entry& js::ShapeTable::searchUnchecked<(js::MaybeAdding)0>(jsid)
|
||||
3.1% js::jit::MLoadFixedSlot::op() const
|
||||
3.1% js::GCPtr<js::Shape*>::init(js::Shape* const&)
|
||||
3.1% js::TypeSet::baseObjectCount() const
|
||||
3.1% js::jit::BaselineCompiler::emit_JSOP_RETRVAL()
|
||||
3.1% JS::Rooted<JSScript*>::~Rooted()
|
||||
3.1% js::jit::ScriptFromCalleeToken(void*)
|
||||
|
||||
=== stall 5: 1418 ms, 40 samples (10 ms each)
|
||||
--- by bucket:
|
||||
45.0% other
|
||||
27.5% JS parse / bytecode
|
||||
10.0% JIT code (the page's compiled JavaScript)
|
||||
5.0% other module: jihad-browserserver
|
||||
2.5% other module: libc.so.6
|
||||
2.5% GC
|
||||
|
||||
[exited with code 0]
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -240,6 +240,19 @@ pref("jihad.compat.trace", false); // a diagnosis mode: logs which engin
|
||||
// pages (the round script's worst tick) if a page ever gets slower for it. ---
|
||||
pref("layout.css.has-pseudo.enabled", true);
|
||||
|
||||
// --- The clock hold (T-500, 2026-10-16; the owner: "fix the stall") — the TouchPad's governor
|
||||
// (Palm's ondemandtcl) ramps the clock on a touch, not on load: a page loading ran at 192-384 MHz
|
||||
// of the 1188 the chip does. While the engine has work the daemon holds the frequency floor at the
|
||||
// top and lets it go after two seconds of idle (JihadBrowserServer.cpp). ---
|
||||
pref("jihad.cpu.hold", true);
|
||||
|
||||
// --- The layout viewport follows a page's overflow (T-499, 2026-10-16) — a page laid out wider
|
||||
// than the card (DuckDuckGo: 1000 px in a 768 window) got innerWidth = 768 while the card showed
|
||||
// all 1000, so what the page parked just past its right edge (its slide-out menu) was on screen.
|
||||
// Now such a page, once, gets an engine window as wide as its content (up to 1100 px, no meta
|
||||
// viewport); the card's fit-zoom is unchanged. BrowserPageGoanna.cpp emitGeometry. ---
|
||||
pref("jihad.viewport.followOverflow", true);
|
||||
|
||||
// --- jemalloc (T-493, 2026-10-16): the allocator is in the daemon now; every memory-pressure
|
||||
// notification (the last card's, the low-memory guardrail's, the lists' unload) also returns
|
||||
// jemalloc's dirty pages to the kernel (AvailableMemoryTracker's watcher; off upstream, on for
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace jihad { void JihadNotifyClients(bool connected, int clients); }
|
||||
#include "JihadLunaService.h" // LunaServiceReadWebosProxy (a card opened)
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
@@ -500,6 +501,89 @@ void JihadBrowserServer::processInjectFile() {
|
||||
}
|
||||
#endif // !JIHAD_NO_INJECT
|
||||
|
||||
// ── The clock hold (T-500, 2026-10-16: "fix the stall") ──────────────────────────────────────
|
||||
//
|
||||
// The TouchPad's governor is Palm's "ondemandtcl": it ramps the clock on a TOUCH (a tickle from the
|
||||
// system UI), not on load. Measured 2026-10-16 while a DuckDuckGo page loaded with the load average
|
||||
// at 4: the clock sat at 192 or 384 MHz for most of the seconds, the chip doing 1188 — the engine
|
||||
// ran the page's script at a third to a sixth of its speed, and every stall (one long event in the
|
||||
// pump: the page's JavaScript, its compile, the collections) was that much longer. So: while the
|
||||
// engine has work, hold the frequency FLOOR (scaling_min_freq, both cores) at the top; after two
|
||||
// seconds of idle ticks let it go. The job runs as root, so sysfs takes the write; anywhere it
|
||||
// does not, this is silent. $JIHAD_CLOCK_HOLD=0 or the pref jihad.cpu.hold=false turns it off.
|
||||
bool gJihadClockHoldEnabled = true; // EngineHost reads jihad.cpu.hold into this at start
|
||||
namespace {
|
||||
class JihadClockHold {
|
||||
public:
|
||||
void busy(int64_t nowMs) {
|
||||
if (!enabled()) return;
|
||||
mLastBusyMs = nowMs;
|
||||
if (!mHeld) set(true);
|
||||
}
|
||||
void idle(int64_t nowMs) {
|
||||
if (!mHeld) return;
|
||||
if (nowMs - mLastBusyMs >= 2000) set(false);
|
||||
}
|
||||
~JihadClockHold() { if (mHeld) set(false); }
|
||||
private:
|
||||
bool enabled() {
|
||||
if (mDisabled) return false;
|
||||
if (!gJihadClockHoldEnabled) return false;
|
||||
if (!mProbed) probe();
|
||||
return !mDisabled;
|
||||
}
|
||||
static bool readFile(const char* path, std::string* out) {
|
||||
FILE* f = fopen(path, "r"); if (!f) return false;
|
||||
char b[64] = {0}; size_t n = fread(b, 1, sizeof(b) - 1, f); fclose(f);
|
||||
while (n && (b[n-1] == '\n' || b[n-1] == ' ')) b[--n] = 0;
|
||||
*out = b; return n > 0;
|
||||
}
|
||||
static bool writeFile(const std::string& path, const std::string& v) {
|
||||
FILE* f = fopen(path.c_str(), "w"); if (!f) return false;
|
||||
bool ok = fputs(v.c_str(), f) >= 0; fclose(f); return ok;
|
||||
}
|
||||
void probe() {
|
||||
mProbed = true;
|
||||
const char* e = getenv("JIHAD_CLOCK_HOLD");
|
||||
if (e && !strcmp(e, "0")) { mDisabled = true; return; }
|
||||
std::string max;
|
||||
if (!readFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", &max)) { mDisabled = true; return; }
|
||||
mTop = max;
|
||||
for (int c = 0; c < 2; c++) {
|
||||
char p[96]; snprintf(p, sizeof(p), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", c);
|
||||
std::string orig;
|
||||
if (readFile(p, &orig)) { mMinPath[c] = p; mOrigMin[c] = orig; }
|
||||
}
|
||||
if (mMinPath[0].empty()) { mDisabled = true; return; }
|
||||
// the write is tried once here: a daemon that may not write sysfs stays quiet about it
|
||||
if (!writeFile(mMinPath[0], mOrigMin[0])) { mDisabled = true; fprintf(stderr, "[jihad-bs] clock hold: sysfs not writable (%s), off\n", strerror(errno)); return; }
|
||||
std::string gov; readFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", &gov);
|
||||
fprintf(stderr, "[jihad-bs] clock hold on: the floor goes to %s kHz while the engine has work (governor %s, floor %s kHz otherwise)\n",
|
||||
mTop.c_str(), gov.c_str(), mOrigMin[0].c_str());
|
||||
}
|
||||
void set(bool hold) {
|
||||
// cpu1 is hot-plugged by this kernel (its cpufreq directory comes and goes with it), so its
|
||||
// path is tried at every change rather than once at the probe; a missing file is not an error
|
||||
for (int c = 0; c < 2; c++) {
|
||||
if (c == 1 && mMinPath[1].empty()) {
|
||||
std::string orig;
|
||||
if (readFile("/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq", &orig)) { mMinPath[1] = "/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq"; mOrigMin[1] = orig; }
|
||||
}
|
||||
if (mMinPath[c].empty()) continue;
|
||||
if (!writeFile(mMinPath[c], hold ? mTop : mOrigMin[c])) { if (c == 0) { mDisabled = true; return; } mMinPath[1].clear(); }
|
||||
}
|
||||
mHeld = hold;
|
||||
if (hold) mHolds++;
|
||||
if (mHolds <= 1) fprintf(stderr, "[jihad-bs] clock hold: %s\n", hold ? "held" : "released");
|
||||
}
|
||||
bool mProbed = false, mDisabled = false, mHeld = false;
|
||||
int mHolds = 0;
|
||||
int64_t mLastBusyMs = 0;
|
||||
std::string mTop, mMinPath[2], mOrigMin[2];
|
||||
};
|
||||
JihadClockHold gClockHold;
|
||||
} // namespace
|
||||
|
||||
static const uint64_t kTickStatsMinPaintMs = 10;
|
||||
static const uint32_t kTickStatsLongTickMs = 40;
|
||||
|
||||
@@ -553,6 +637,7 @@ void JihadBrowserServer::tick() {
|
||||
// is dozens of such hops) would otherwise advance one hop per second — so main() ticks at the
|
||||
// quiet period until a tick finds nothing (mIdleHadWork).
|
||||
if (snap.empty()) {
|
||||
gClockHold.idle(jihadTickNowMs()); // T-500: no page — the hold lets the clock go (this return came before its check, and the floor stayed up after the last card, 2026-10-16)
|
||||
mIdleHadWork = jihad::PumpReadyMainThread(4) > 0;
|
||||
mTickStatsReset = true;
|
||||
mTickBusy = false; mActivity = false; mTickPlugin = false;
|
||||
@@ -595,6 +680,13 @@ void JihadBrowserServer::tick() {
|
||||
mTickBusy = mActivity;
|
||||
mActivity = false;
|
||||
for (auto* pg : snap) if (pg->isBusy()) { mTickBusy = true; break; }
|
||||
// The clock hold: a busy page, or a tick that did real work (a stall is one long tick), holds
|
||||
// the clock; two seconds of quiet ticks release it. A stall's first busy tick precedes it, so
|
||||
// the hold is in place when the long event starts.
|
||||
{
|
||||
int64_t t = jihadTickNowMs();
|
||||
if (mTickBusy || (t - tickT0) >= 8) gClockHold.busy(t); else gClockHold.idle(t);
|
||||
}
|
||||
mTickPlugin = false;
|
||||
for (auto* pg : snap) if (pg->hasPlugin()) { mTickPlugin = true; break; }
|
||||
int64_t tickT1 = jihadTickNowMs();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* server->adapter messages via IPageMessageSink.
|
||||
*/
|
||||
#include "BrowserPageGoanna.h"
|
||||
extern bool gJihadViewportFollow; // EngineHost.cpp: jihad.viewport.followOverflow (T-499)
|
||||
#include "EngineCompat.h"
|
||||
#include "EngineHost.h"
|
||||
#include "GoannaRenderPage.h"
|
||||
@@ -609,6 +610,7 @@ bool BrowserPageGoanna::init(uint32_t width, uint32_t height,
|
||||
}
|
||||
|
||||
void BrowserPageGoanna::setWindowSize(uint32_t width, uint32_t height) {
|
||||
mLayoutFollowed = false; // T-499: a new card size (rotation) starts from a layout viewport equal to it
|
||||
// Same reasoning as the navigation reset: a resize replaces the whole frame, and both
|
||||
// buffers still hold the old one.
|
||||
mBufFull[0].valid = mBufFull[1].valid = mBufFull[2].valid = false;
|
||||
@@ -782,6 +784,7 @@ void BrowserPageGoanna::returnBuffer(int sharedBufferKey) {
|
||||
// scroll on back/forward either (restoreFrameStateRequested is an empty stub), so calling this
|
||||
// from all five nav commands reproduces stock exactly.
|
||||
void BrowserPageGoanna::resetScrollForNavigation() {
|
||||
mLayoutFollowed = false; // T-499: the next page decides its own layout viewport
|
||||
// A new document invalidates both buffers wholesale. The damage-only path proves its
|
||||
// premise from geometry (size, zoom, scroll), and a navigation can leave every one of
|
||||
// those identical while replacing all the pixels — so geometry alone cannot catch it and
|
||||
@@ -2193,6 +2196,27 @@ bool BrowserPageGoanna::emitGeometry() {
|
||||
// already fits the layout viewport should composite at exactly 1.0 (identity blit). The
|
||||
// reported height is unchanged — only the width is floored at the window width.
|
||||
if (got && cw > 0 && cw < mPage->Width()) cw = mPage->Width();
|
||||
// T-499: a page laid out wider than the card gets a layout viewport as wide as its content, ONCE
|
||||
// per page and size (a reflow at the new width can change the content size; nothing follows that
|
||||
// a second time, so there is no loop). Only a page without a meta viewport (one that declares a
|
||||
// width is the card's business through initial-scale), only up to 1100 px (a desktop page's
|
||||
// width; one overflowing element must not turn a phone layout into a desktop one), and only when
|
||||
// the pref says so. Then innerWidth is the page's own width and what it parks just past that
|
||||
// edge stays off screen while the card fit-zooms the whole width as before.
|
||||
if (got && cw > mPage->Width() + 8 && cw <= 1100 && ch > 0 && ::gJihadViewportFollow && !mLayoutFollowed) {
|
||||
double is = 1.0, mn = 1.0, mx = 1.0; int vw = 0, vh = 0; bool us = true;
|
||||
bool declares = mPage->GetViewport(&is, &mn, &mx, &vw, &vh, &us);
|
||||
mLayoutFollowed = true;
|
||||
if (!declares) {
|
||||
int lw = cw, lh = (int)((int64_t)mPage->Height() * lw / mPage->Width());
|
||||
if (mPage->SetLayoutSize(lw, lh)) {
|
||||
int cw2 = 0, ch2 = 0;
|
||||
if (mPage->GetContentSize(&cw2, &ch2) && cw2 > 0 && ch2 > 0) { cw = cw2; ch = ch2; }
|
||||
if (cw < lw) cw = lw;
|
||||
mNeedsPaint = true; mDamageForceFull = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (got && cw > 0 && ch > 0 && (cw != mLastContentW || ch != mLastContentH)) {
|
||||
// Log only on a REAL change. This used to log unconditionally, and emitGeometry is retried
|
||||
// from the pump on EVERY tick while mGeometryDirty is set — which made this line the single
|
||||
|
||||
@@ -478,6 +478,7 @@ private:
|
||||
// last meta-viewport sent to the card (emitGeometry sends it only on change)
|
||||
bool mPopupWasOpen = false; // a XUL popup was open at the last pump (repaint on change)
|
||||
bool mViewportSent = false;
|
||||
bool mLayoutFollowed = false; // T-499: this page already got its layout viewport
|
||||
double mVpIs = 0, mVpMn = 0, mVpMx = 0; int mVpW = 0, mVpH = 0; bool mVpUs = true;
|
||||
int mLastScrollX, mLastScrollY; // last emitted scroll offset
|
||||
double mZoom; // current full-page zoom (for input coord mapping)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "DialogService.h" // InstallDialogService (dialog interception)
|
||||
#include "DownloadService.h" // InstallDownloadService (download handoff)
|
||||
#include "JihadUserAgent.h" // JIHAD_USER_AGENT (shared UA string)
|
||||
extern bool gJihadClockHoldEnabled; // JihadBrowserServer.cpp (global scope): the clock hold (T-500), jihad.cpu.hold
|
||||
bool gJihadViewportFollow = true; // T-499: the layout viewport follows a page's overflow (jihad.viewport.followOverflow); BrowserPageGoanna.cpp reads it
|
||||
#include "JihadRuntimePaths.h" // the ONE runtime-state dir (T-057 / R8)
|
||||
#include <sys/stat.h> // chmod (menu highlight sheet)
|
||||
#include <dirent.h> // bundle fingerprint (stale startup cache)
|
||||
@@ -787,6 +789,8 @@ private:
|
||||
bool JihadConsoleListener::sAll = false;
|
||||
NS_IMPL_ISUPPORTS(JihadConsoleListener, nsIConsoleListener)
|
||||
|
||||
void JihadStallSamplerInit(); // GoannaRenderPage.cpp: $JIHAD_STALL_SAMPLER=<ms>, the stall sampler (diagnosis)
|
||||
|
||||
static void InstallConsoleListener()
|
||||
{
|
||||
const char* e = getenv("JIHAD_JS_CONSOLE");
|
||||
@@ -1142,6 +1146,15 @@ EngineHost::Init(const char* greDir)
|
||||
ProbeAppInfo("late-backstop");
|
||||
ProbeWidgetComponents(); // T-067/R6 — see the comment on the function
|
||||
InstallConsoleListener(); // T-067/R6 — chrome JS errors were being discarded entirely
|
||||
JihadStallSamplerInit(); // off unless $JIHAD_STALL_SAMPLER is set (the stall rounds)
|
||||
{ // the clock hold (T-500): jihad.cpu.hold, default on — JihadBrowserServer.cpp's tick loop
|
||||
nsCOMPtr<nsIPrefBranch> p = do_GetService("@mozilla.org/preferences-service;1");
|
||||
bool on = true;
|
||||
if (p && NS_SUCCEEDED(p->GetBoolPref("jihad.cpu.hold", &on))) ::gJihadClockHoldEnabled = on;
|
||||
bool follow = true;
|
||||
if (p && NS_SUCCEEDED(p->GetBoolPref("jihad.viewport.followOverflow", &follow))) ::gJihadViewportFollow = follow;
|
||||
if (!::gJihadClockHoldEnabled) fprintf(stderr, "[jihad-bs] clock hold off by pref (jihad.cpu.hold)\n");
|
||||
}
|
||||
InstallClipboardHelper(); // T-067/R6 — without it chrome/global/content/config.js aborts
|
||||
// ── announce the profile ────────────────────────────────────────────────
|
||||
// XRE_InitEmbedding2 takes the directory provider but does NOT arm it:
|
||||
|
||||
@@ -1041,11 +1041,38 @@ std::string DebugElementRect(const char* elementId) {
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// T-499 (2026-10-16): the layout viewport apart from the card. The card stays mWidth x mHeight (the
|
||||
// buffer, the pan arithmetic, the fit-zoom); only the engine's window — the thing innerWidth, vw
|
||||
// and position:fixed are measured against — is set to the page's overflow width, and the height
|
||||
// to what the card shows at that fit (card height / zoom). Measured need: DuckDuckGo lays out
|
||||
// 1000 px wide in a 768 window and parks its slide-out menu at innerWidth + 4, which the card's
|
||||
// fit-zoom of the whole 1000 px then shows; at a 1000-px layout viewport the menu parks at 1004.
|
||||
bool GoannaRenderPage::SetLayoutSize(int width, int height) {
|
||||
if (!mChrome) return false;
|
||||
if (width <= 0 || height <= 0 || width > 8192 || height > 8192) return false;
|
||||
if (width == mLayoutW && height == mLayoutH) return true;
|
||||
mLayoutW = width; mLayoutH = height;
|
||||
if (mOffscreen) jihad_offscreen_resize(mWidget, width, height);
|
||||
nsCOMPtr<nsIBaseWindow> bw = do_QueryInterface(mChrome->mBrowser);
|
||||
if (bw) bw->SetPositionAndSize(0, 0, width, height, nsIBaseWindow::eRepaint);
|
||||
// the reflow at the new size, as Resize() does it (the headless widget's resize does not reach
|
||||
// the presShell by itself; engine_calls::ResizeReflow works in device px = CSS px here)
|
||||
nsCOMPtr<nsIDocShell> ds = GetDocShell(mChrome->mBrowser);
|
||||
if (ds) {
|
||||
nsCOMPtr<nsIPresShell> ps = ds->GetPresShell();
|
||||
if (ps) engine_calls::ResizeReflow(ps.get(), width, height);
|
||||
}
|
||||
JIHAD_GTK_PUMP();
|
||||
fprintf(stderr, "[jihad-bs] layout viewport %dx%d (card %dx%d)\n", width, height, mWidth, mHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GoannaRenderPage::Resize(int width, int height) {
|
||||
if (!mChrome) return false;
|
||||
if ((mOffscreen && !mWidget) || (!mOffscreen && !mWindow)) return false;
|
||||
if (width <= 0 || height <= 0 || width > 8192 || height > 8192) return false;
|
||||
mWidth = width; mHeight = height;
|
||||
mLayoutW = mLayoutH = 0; // T-499: a new card size starts from a layout viewport equal to it
|
||||
mChrome->mW = width; mChrome->mH = height; // keep GetDimensions() consistent
|
||||
if (mOffscreen) {
|
||||
jihad_offscreen_resize(mWidget, width, height);
|
||||
@@ -2133,6 +2160,109 @@ bool GoannaRenderPage::LoadUrl(const char* url, const char* referrer, uint32_t r
|
||||
trigger));
|
||||
}
|
||||
|
||||
// ── The stall sampler (diagnosis only; $JIHAD_STALL_SAMPLER=<ms>, 2026-10-16) ───────────────────
|
||||
//
|
||||
// The tick log says a stall is in the pump (work max=40891 pump avg=40614 paint avg=277 on
|
||||
// GitLab), and the pump is one NS_ProcessNextEvent at a time — a single event (a script, a
|
||||
// reflow, a collection) can hold the thread for forty seconds. This engine build has no Gecko
|
||||
// profiler (tools/profiler: no objects), so: a thread that, once the current event has run
|
||||
// longer than the threshold, sends SIGPROF to the main thread every 10 ms; the handler records
|
||||
// the program counter (the leaf, no unwinding — robust on ARM, and the leaf names the work:
|
||||
// parser, JIT compiler, GC, cycle collector, style, reflow, paint, interpreter, JIT code).
|
||||
// After the event: one summary line in the log (per module), and every sample as
|
||||
// module+offset appended to /tmp/jihad-stall.txt, which tools/host/stall-resolve.py turns into
|
||||
// function names and buckets with the unstripped libxul and the toolchain's addr2line.
|
||||
// Signal-safe by construction: the handler touches an array and two atomics; dladdr runs on the
|
||||
// main thread afterwards.
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <ucontext.h>
|
||||
#include <dlfcn.h>
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
namespace {
|
||||
const int kStallSamplesMax = 8192; // 82 s of a stall at 10 ms; a longer one is truncated
|
||||
static std::atomic<long> gStallEventStartMs{0}; // 0 = no event running
|
||||
static std::atomic<int> gStallSampleCount{0};
|
||||
static uintptr_t gStallSamples[kStallSamplesMax];
|
||||
static pthread_t gStallMainThread;
|
||||
static int gStallThresholdMs = 0;
|
||||
static int gStallCount = 0;
|
||||
static long StallNowMs() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000L + ts.tv_nsec / 1000000; }
|
||||
static void StallOnProf(int, siginfo_t*, void* aCtx) {
|
||||
ucontext_t* uc = static_cast<ucontext_t*>(aCtx);
|
||||
uintptr_t pc = static_cast<uintptr_t>(uc->uc_mcontext.arm_pc);
|
||||
int n = gStallSampleCount.fetch_add(1);
|
||||
if (n < kStallSamplesMax) gStallSamples[n] = pc;
|
||||
}
|
||||
static void* StallSamplerThread(void*) {
|
||||
for (;;) {
|
||||
usleep(10000);
|
||||
long s = gStallEventStartMs.load();
|
||||
if (s && StallNowMs() - s >= gStallThresholdMs) pthread_kill(gStallMainThread, SIGPROF);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void JihadStallSamplerInit() {
|
||||
const char* e = getenv("JIHAD_STALL_SAMPLER");
|
||||
if (!e || !*e) return;
|
||||
gStallThresholdMs = atoi(e);
|
||||
if (gStallThresholdMs <= 0) return;
|
||||
gStallMainThread = pthread_self();
|
||||
struct sigaction sa; memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = StallOnProf; sa.sa_flags = SA_SIGINFO | SA_RESTART; sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGPROF, &sa, nullptr);
|
||||
pthread_t t;
|
||||
if (pthread_create(&t, nullptr, StallSamplerThread, nullptr) == 0) pthread_detach(t);
|
||||
fprintf(stderr, "[jihad-bs] stall sampler on: an event longer than %d ms is sampled every 10 ms (the pc); samples to /tmp/jihad-stall.txt\n", gStallThresholdMs);
|
||||
}
|
||||
static inline void JihadStallEventBegin() {
|
||||
if (!gStallThresholdMs) return;
|
||||
gStallSampleCount.store(0);
|
||||
gStallEventStartMs.store(StallNowMs());
|
||||
}
|
||||
static void JihadStallEventEnd() {
|
||||
if (!gStallThresholdMs) return;
|
||||
long s = gStallEventStartMs.exchange(0);
|
||||
if (!s) return;
|
||||
int n = gStallSampleCount.load();
|
||||
if (n <= 0) return;
|
||||
long ms = StallNowMs() - s;
|
||||
if (n > kStallSamplesMax) n = kStallSamplesMax;
|
||||
++gStallCount;
|
||||
// per-module histogram for the log; every sample to the file
|
||||
struct Mod { const char* name; uintptr_t base; int count; };
|
||||
std::vector<Mod> mods;
|
||||
FILE* f = fopen("/tmp/jihad-stall.txt", "a");
|
||||
if (f) fprintf(f, "stall %d: %ld ms, %d samples\n", gStallCount, ms, n);
|
||||
int jit = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
Dl_info di; memset(&di, 0, sizeof(di));
|
||||
if (dladdr(reinterpret_cast<void*>(gStallSamples[i]), &di) && di.dli_fname) {
|
||||
const char* nm = strrchr(di.dli_fname, '/'); nm = nm ? nm + 1 : di.dli_fname;
|
||||
uintptr_t off = gStallSamples[i] - reinterpret_cast<uintptr_t>(di.dli_fbase);
|
||||
if (f) fprintf(f, "%s+0x%lx\n", nm, static_cast<unsigned long>(off));
|
||||
bool found = false;
|
||||
for (auto& m : mods) if (m.base == reinterpret_cast<uintptr_t>(di.dli_fbase)) { m.count++; found = true; break; }
|
||||
if (!found) mods.push_back({nm, reinterpret_cast<uintptr_t>(di.dli_fbase), 1});
|
||||
} else {
|
||||
++jit;
|
||||
if (f) fprintf(f, "jit+0x%lx\n", static_cast<unsigned long>(gStallSamples[i]));
|
||||
}
|
||||
}
|
||||
if (f) fclose(f);
|
||||
std::sort(mods.begin(), mods.end(), [](const Mod& a, const Mod& b) { return a.count > b.count; });
|
||||
std::string line;
|
||||
for (size_t i = 0; i < mods.size() && i < 6; i++) {
|
||||
char b[160]; snprintf(b, sizeof(b), "%s %d%%, ", mods[i].name, mods[i].count * 100 / n); line += b;
|
||||
}
|
||||
if (jit) { char b[64]; snprintf(b, sizeof(b), "JIT code %d%%", jit * 100 / n); line += b; }
|
||||
fprintf(stderr, "[jihad-bs] stall %d: an event ran %ld ms, %d samples: %s\n", gStallCount, ms, n, line.c_str());
|
||||
}
|
||||
|
||||
// Drain the engine's ready work and RETURN. This is the tick's pump, and it exists because
|
||||
// PumpFor's contract is the opposite one: PumpFor SPENDS its budget, g_usleep(1000)ing between
|
||||
// events so that a test waiting on a load keeps the engine running for the full duration.
|
||||
@@ -2155,7 +2285,10 @@ int PumpReadyMainThread(int msBudget) {
|
||||
struct timespec ts0; clock_gettime(CLOCK_MONOTONIC, &ts0);
|
||||
int ran = 0;
|
||||
for (;;) {
|
||||
if (!NS_ProcessNextEvent(thread, false)) break; // nothing ready: do not wait for more
|
||||
JihadStallEventBegin();
|
||||
bool ran1 = NS_ProcessNextEvent(thread, false);
|
||||
JihadStallEventEnd();
|
||||
if (!ran1) break; // nothing ready: do not wait for more
|
||||
++ran;
|
||||
JIHAD_GTK_PUMP();
|
||||
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
@@ -2188,7 +2321,9 @@ void GoannaRenderPage::PumpFor(int msBudget) {
|
||||
NS_GetCurrentThread(getter_AddRefs(thread));
|
||||
struct timespec ts0; clock_gettime(CLOCK_MONOTONIC, &ts0);
|
||||
for (;;) {
|
||||
JihadStallEventBegin();
|
||||
NS_ProcessNextEvent(thread, false);
|
||||
JihadStallEventEnd();
|
||||
JIHAD_GTK_PUMP();
|
||||
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
long ms = (ts.tv_sec - ts0.tv_sec) * 1000 + (ts.tv_nsec - ts0.tv_nsec) / 1000000;
|
||||
@@ -2203,7 +2338,9 @@ bool GoannaRenderPage::LoadUrlAndWait(const char* url, int timeoutSec) {
|
||||
NS_GetCurrentThread(getter_AddRefs(thread));
|
||||
time_t start = time(nullptr);
|
||||
while (!mChrome->mDone && (time(nullptr) - start) < timeoutSec) {
|
||||
JihadStallEventBegin();
|
||||
NS_ProcessNextEvent(thread, false);
|
||||
JihadStallEventEnd();
|
||||
JIHAD_GTK_PUMP();
|
||||
g_usleep(1000);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ void SetUserAgentOverride(const char* ua);
|
||||
void ClearCache();
|
||||
void ClearCookies();
|
||||
void SetMinFontSize(int px); // YAP: setMinFontSize
|
||||
void JihadStallSamplerInit(); // $JIHAD_STALL_SAMPLER=<ms>: sample the pc of an event-loop event that runs longer (diagnosis; GoannaRenderPage.cpp)
|
||||
void SetBlockPopups(bool block); // YAP: setBlockPopups
|
||||
void SetAcceptCookies(bool accept); // YAP: setAcceptCookies
|
||||
|
||||
@@ -157,6 +158,11 @@ public:
|
||||
// Resize the offscreen surface + content viewport (YAP: setWindowSize). The
|
||||
// page reflows to the new size; the next ReadPixels returns width*height*4.
|
||||
bool Resize(int width, int height);
|
||||
// T-499: the engine's layout viewport set WIDER than the card (an offscreen window as wide as the
|
||||
// page's overflow), so innerWidth and position:fixed follow the page's own width while the card
|
||||
// fit-zooms the whole of it; Width()/Height() stay the card's. Resize() puts it back.
|
||||
bool SetLayoutSize(int width, int height);
|
||||
int LayoutWidth() const { return mLayoutW ? mLayoutW : mWidth; }
|
||||
|
||||
// Scroll the content to an absolute CSS-pixel offset (YAP: setScrollPosition).
|
||||
void ScrollTo(int x, int y);
|
||||
@@ -479,6 +485,7 @@ private:
|
||||
bool mOffscreen; // true when rendering via the offscreen widget
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
int mLayoutW = 0, mLayoutH = 0; // T-499: the layout viewport when it differs from the card (0 = the card)
|
||||
double mRenderZoom = 1.0; // pinch/fit zoom applied as a gfxContext scale in the offscreen capture
|
||||
double mPanX = 0.0; // visual-viewport pan (CSS px) used by the render when zoomed (>1)
|
||||
double mPanY = 0.0;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,158 @@
|
||||
/* Copyright 2026 NotAlexNoyle. Apache-2.0.
|
||||
*
|
||||
* gpu-bench — what the TouchPad's Adreno 220 costs per 1024x768 frame through the vendor EGL/GLES2
|
||||
* (dlopen'd from /usr/lib, no headers, no link libs: the same libs LunaSysMgr and Atlas use), and
|
||||
* what the CPU costs for the same work in software. The numbers feed the rendering-options report
|
||||
* (docs/RENDERING-OPTIONS.md): every GPU route for Jihad is "draw/composite on the GPU, then get
|
||||
* the pixels back for the card" (until LunaSysMgr takes GPU buffers), so the readback is the
|
||||
* number that decides.
|
||||
*
|
||||
* build: $TC/arm-webos-linux-gnueabi-gcc -O2 -march=armv7-a -mfpu=neon -mfloat-abi=softfp -o gpu-bench gpu-bench.c -ldl -lrt
|
||||
* run (device): LD_LIBRARY_PATH=/usr/lib ./gpu-bench [frames]
|
||||
*/
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef void* EGLDisplay; typedef void* EGLConfig; typedef void* EGLSurface; typedef void* EGLContext;
|
||||
typedef int EGLint; typedef unsigned EGLBoolean; typedef unsigned GLenum; typedef unsigned GLuint; typedef int GLint; typedef int GLsizei; typedef float GLfloat; typedef unsigned char GLboolean; typedef unsigned GLbitfield; typedef char GLchar;
|
||||
#define EGL_DEFAULT_DISPLAY ((void*)0)
|
||||
#define EGL_NONE 0x3038
|
||||
#define EGL_SURFACE_TYPE 0x3033
|
||||
#define EGL_PBUFFER_BIT 0x0001
|
||||
#define EGL_RENDERABLE_TYPE 0x3040
|
||||
#define EGL_OPENGL_ES2_BIT 0x0004
|
||||
#define EGL_RED_SIZE 0x3024
|
||||
#define EGL_GREEN_SIZE 0x3023
|
||||
#define EGL_BLUE_SIZE 0x3022
|
||||
#define EGL_ALPHA_SIZE 0x3021
|
||||
#define EGL_WIDTH 0x3057
|
||||
#define EGL_HEIGHT 0x3056
|
||||
#define EGL_CONTEXT_CLIENT_VERSION 0x3098
|
||||
#define EGL_NO_CONTEXT ((void*)0)
|
||||
#define EGL_NO_SURFACE ((void*)0)
|
||||
#define EGL_VENDOR 0x3053
|
||||
#define EGL_VERSION 0x3054
|
||||
#define EGL_EXTENSIONS 0x3055
|
||||
#define GL_VERSION 0x1F02
|
||||
#define GL_RENDERER 0x1F01
|
||||
#define GL_EXTENSIONS 0x1F03
|
||||
#define GL_COLOR_BUFFER_BIT 0x00004000
|
||||
#define GL_TEXTURE_2D 0x0DE1
|
||||
#define GL_RGBA 0x1908
|
||||
#define GL_BGRA_EXT 0x80E1
|
||||
#define GL_UNSIGNED_BYTE 0x1401
|
||||
#define GL_TEXTURE_MIN_FILTER 0x2801
|
||||
#define GL_TEXTURE_MAG_FILTER 0x2800
|
||||
#define GL_LINEAR 0x2601
|
||||
#define GL_NEAREST 0x2600
|
||||
#define GL_VERTEX_SHADER 0x8B31
|
||||
#define GL_FRAGMENT_SHADER 0x8B30
|
||||
#define GL_COMPILE_STATUS 0x8B81
|
||||
#define GL_LINK_STATUS 0x8B82
|
||||
#define GL_FLOAT 0x1406
|
||||
#define GL_FALSE 0
|
||||
#define GL_TRIANGLE_STRIP 0x0005
|
||||
#define GL_BLEND 0x0BE2
|
||||
#define GL_SRC_ALPHA 0x0302
|
||||
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
|
||||
#define GL_NO_ERROR 0
|
||||
#define GL_TEXTURE0 0x84C0
|
||||
|
||||
static void* E; static void* G;
|
||||
#define F(ret, name, args) static ret (*name) args
|
||||
F(EGLDisplay, eglGetDisplay, (void*)); F(EGLBoolean, eglInitialize, (EGLDisplay, EGLint*, EGLint*)); F(EGLBoolean, eglChooseConfig, (EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*));
|
||||
F(EGLSurface, eglCreatePbufferSurface, (EGLDisplay, EGLConfig, const EGLint*)); F(EGLContext, eglCreateContext, (EGLDisplay, EGLConfig, EGLContext, const EGLint*)); F(EGLBoolean, eglMakeCurrent, (EGLDisplay, EGLSurface, EGLSurface, EGLContext));
|
||||
F(const char*, eglQueryString, (EGLDisplay, EGLint)); F(EGLint, eglGetError, (void)); F(EGLBoolean, eglBindAPI, (unsigned));
|
||||
F(void, glClearColor, (float, float, float, float)); F(void, glClear, (GLbitfield)); F(void, glFinish, (void)); F(void, glReadPixels, (GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*));
|
||||
F(void, glGenTextures, (GLsizei, GLuint*)); F(void, glBindTexture, (GLenum, GLuint)); F(void, glTexImage2D, (GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*)); F(void, glTexSubImage2D, (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*));
|
||||
F(void, glTexParameteri, (GLenum, GLenum, GLint)); F(GLuint, glCreateShader, (GLenum)); F(void, glShaderSource, (GLuint, GLsizei, const GLchar**, const GLint*)); F(void, glCompileShader, (GLuint)); F(void, glGetShaderiv, (GLuint, GLenum, GLint*));
|
||||
F(GLuint, glCreateProgram, (void)); F(void, glAttachShader, (GLuint, GLuint)); F(void, glLinkProgram, (GLuint)); F(void, glGetProgramiv, (GLuint, GLenum, GLint*)); F(void, glUseProgram, (GLuint)); F(GLint, glGetAttribLocation, (GLuint, const GLchar*)); F(GLint, glGetUniformLocation, (GLuint, const GLchar*));
|
||||
F(void, glVertexAttribPointer, (GLuint, GLint, GLenum, GLboolean, GLsizei, const void*)); F(void, glEnableVertexAttribArray, (GLuint)); F(void, glDrawArrays, (GLenum, GLint, GLsizei)); F(void, glUniform1i, (GLint, GLint)); F(void, glUniform4f, (GLint, float, float, float, float));
|
||||
F(void, glEnable, (GLenum)); F(void, glDisable, (GLenum)); F(void, glBlendFunc, (GLenum, GLenum)); F(void, glViewport, (GLint, GLint, GLsizei, GLsizei)); F(GLenum, glGetError, (void)); F(const unsigned char*, glGetString, (GLenum)); F(void, glActiveTexture, (GLenum));
|
||||
|
||||
#define LOAD(lib, name) do { name = dlsym(lib, #name); if (!name) { fprintf(stderr, "no %s\n", #name); return 2; } } while (0)
|
||||
static double now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6; }
|
||||
static GLuint shader(GLenum type, const char* src) { GLuint s = glCreateShader(type); glShaderSource(s, 1, &src, NULL); glCompileShader(s); GLint ok = 0; glGetShaderiv(s, GL_COMPILE_STATUS, &ok); if (!ok) { fprintf(stderr, "shader compile failed\n"); exit(3); } return s; }
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int frames = argc > 1 ? atoi(argv[1]) : 30; const int W = 1024, H = 768;
|
||||
E = dlopen("/usr/lib/libEGL.so", RTLD_NOW | RTLD_GLOBAL); if (!E) E = dlopen("libEGL.so.1", RTLD_NOW | RTLD_GLOBAL);
|
||||
G = dlopen("/usr/lib/libGLESv2.so", RTLD_NOW | RTLD_GLOBAL); if (!G) G = dlopen("libGLESv2.so.2", RTLD_NOW | RTLD_GLOBAL);
|
||||
if (!E || !G) { fprintf(stderr, "dlopen: %s\n", dlerror()); return 1; }
|
||||
LOAD(E, eglGetDisplay); LOAD(E, eglInitialize); LOAD(E, eglChooseConfig); LOAD(E, eglCreatePbufferSurface); LOAD(E, eglCreateContext); LOAD(E, eglMakeCurrent); LOAD(E, eglQueryString); LOAD(E, eglGetError); LOAD(E, eglBindAPI);
|
||||
LOAD(G, glClearColor); LOAD(G, glClear); LOAD(G, glFinish); LOAD(G, glReadPixels); LOAD(G, glGenTextures); LOAD(G, glBindTexture); LOAD(G, glTexImage2D); LOAD(G, glTexSubImage2D); LOAD(G, glTexParameteri);
|
||||
LOAD(G, glCreateShader); LOAD(G, glShaderSource); LOAD(G, glCompileShader); LOAD(G, glGetShaderiv); LOAD(G, glCreateProgram); LOAD(G, glAttachShader); LOAD(G, glLinkProgram); LOAD(G, glGetProgramiv); LOAD(G, glUseProgram);
|
||||
LOAD(G, glGetAttribLocation); LOAD(G, glGetUniformLocation); LOAD(G, glVertexAttribPointer); LOAD(G, glEnableVertexAttribArray); LOAD(G, glDrawArrays); LOAD(G, glUniform1i); LOAD(G, glUniform4f); LOAD(G, glEnable); LOAD(G, glDisable); LOAD(G, glBlendFunc); LOAD(G, glViewport); LOAD(G, glGetError); LOAD(G, glGetString); LOAD(G, glActiveTexture);
|
||||
|
||||
double t0 = now_ms();
|
||||
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); EGLint maj = 0, mn = 0;
|
||||
if (!eglInitialize(dpy, &maj, &mn)) { fprintf(stderr, "eglInitialize failed 0x%x\n", eglGetError()); return 4; }
|
||||
printf("EGL %d.%d vendor=%s version=%s\n", maj, mn, eglQueryString(dpy, EGL_VENDOR), eglQueryString(dpy, EGL_VERSION));
|
||||
printf("EGL extensions: %s\n", eglQueryString(dpy, EGL_EXTENSIONS));
|
||||
eglBindAPI(0x30A0 /* EGL_OPENGL_ES_API */);
|
||||
const EGLint cfgAttr[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE };
|
||||
EGLConfig cfg; EGLint n = 0;
|
||||
if (!eglChooseConfig(dpy, cfgAttr, &cfg, 1, &n) || n < 1) { fprintf(stderr, "no pbuffer config 0x%x\n", eglGetError()); return 5; }
|
||||
const EGLint pbAttr[] = { EGL_WIDTH, W, EGL_HEIGHT, H, EGL_NONE };
|
||||
EGLSurface surf = eglCreatePbufferSurface(dpy, cfg, pbAttr); if (!surf) { fprintf(stderr, "pbuffer failed 0x%x\n", eglGetError()); return 6; }
|
||||
const EGLint ctxAttr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
||||
EGLContext ctx = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT, ctxAttr); if (!ctx) { fprintf(stderr, "context failed 0x%x\n", eglGetError()); return 7; }
|
||||
if (!eglMakeCurrent(dpy, surf, surf, ctx)) { fprintf(stderr, "makeCurrent failed 0x%x\n", eglGetError()); return 8; }
|
||||
printf("GL renderer=%s version=%s\n", glGetString(GL_RENDERER), glGetString(GL_VERSION));
|
||||
const char* ext = (const char*)glGetString(GL_EXTENSIONS); int bgra = ext && strstr(ext, "GL_EXT_texture_format_BGRA8888") != NULL; int readbgra = ext && strstr(ext, "GL_EXT_read_format_bgra") != NULL;
|
||||
printf("GL_EXT_texture_format_BGRA8888=%d GL_EXT_read_format_bgra=%d\n", bgra, readbgra);
|
||||
printf("setup: %.1f ms\n", now_ms() - t0);
|
||||
glViewport(0, 0, W, H);
|
||||
|
||||
const char* vs = "attribute vec2 p; attribute vec2 t; varying vec2 v; void main(){ v = t; gl_Position = vec4(p, 0.0, 1.0); }";
|
||||
const char* fs = "precision mediump float; varying vec2 v; uniform sampler2D s; uniform vec4 m; void main(){ gl_FragColor = texture2D(s, v) * m; }";
|
||||
GLuint prog = glCreateProgram(); glAttachShader(prog, shader(GL_VERTEX_SHADER, vs)); glAttachShader(prog, shader(GL_FRAGMENT_SHADER, fs)); glLinkProgram(prog);
|
||||
GLint linked = 0; glGetProgramiv(prog, GL_LINK_STATUS, &linked); if (!linked) { fprintf(stderr, "link failed\n"); return 9; }
|
||||
glUseProgram(prog);
|
||||
const GLfloat quad[] = { -1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1 };
|
||||
GLint ap = glGetAttribLocation(prog, "p"), at = glGetAttribLocation(prog, "t"), um = glGetUniformLocation(prog, "m"), us = glGetUniformLocation(prog, "s");
|
||||
glVertexAttribPointer(ap, 2, GL_FLOAT, GL_FALSE, 16, quad); glEnableVertexAttribArray(ap);
|
||||
glVertexAttribPointer(at, 2, GL_FLOAT, GL_FALSE, 16, quad + 2); glEnableVertexAttribArray(at);
|
||||
glUniform1i(us, 0); glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
uint8_t* pix = malloc((size_t)W * H * 4); for (size_t i = 0; i < (size_t)W * H * 4; i++) pix[i] = (uint8_t)(i * 7);
|
||||
uint8_t* tile = malloc(256 * 256 * 4); memset(tile, 0x80, 256 * 256 * 4);
|
||||
uint8_t* back = malloc((size_t)W * H * 4);
|
||||
GLuint tex[4]; glGenTextures(4, tex);
|
||||
// 1. one full-frame texture upload (RGBA 1024x768 = 3 MB)
|
||||
double t = now_ms();
|
||||
for (int i = 0; i < 4; i++) { glBindTexture(GL_TEXTURE_2D, tex[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, H, 0, GL_RGBA, GL_UNSIGNED_BYTE, pix); }
|
||||
glFinish(); printf("upload 4 x 1024x768 RGBA textures: %.1f ms (%.1f ms each)\n", now_ms() - t, (now_ms() - t) / 4);
|
||||
// 2. composite: one opaque layer per frame
|
||||
glDisable(GL_BLEND); glUniform4f(um, 1, 1, 1, 1);
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) { glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, tex[0]); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } glFinish();
|
||||
printf("composite 1 opaque full-screen layer: %.2f ms/frame\n", (now_ms() - t) / frames);
|
||||
// 3. composite: 4 full-screen layers, 3 blended (a page: content + fixed header + overlay + scrollbar-ish)
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) { glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, tex[0]); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUniform4f(um, 1, 1, 1, 0.5f); for (int l = 1; l < 4; l++) { glBindTexture(GL_TEXTURE_2D, tex[l]); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } glUniform4f(um, 1, 1, 1, 1); } glFinish();
|
||||
printf("composite 4 full-screen layers (3 blended): %.2f ms/frame\n", (now_ms() - t) / frames);
|
||||
// 4. tile upload per frame: 12 x 256x256 (what a scroll repaints under OMTC), then one draw
|
||||
glDisable(GL_BLEND);
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) { glBindTexture(GL_TEXTURE_2D, tex[0]); for (int k = 0; k < 12; k++) glTexSubImage2D(GL_TEXTURE_2D, 0, (k % 4) * 256, (k / 4) * 256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, tile); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } glFinish();
|
||||
printf("12 tile uploads (256x256, 3 MB total) + composite: %.2f ms/frame\n", (now_ms() - t) / frames);
|
||||
// 5. readback: the cost every GPU route pays until LunaSysMgr takes GPU buffers
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) { glReadPixels(0, 0, W, H, GL_RGBA, GL_UNSIGNED_BYTE, back); }
|
||||
printf("glReadPixels 1024x768 RGBA: %.2f ms/frame\n", (now_ms() - t) / frames);
|
||||
if (readbgra) { t = now_ms(); for (int f = 0; f < frames; f++) { glReadPixels(0, 0, W, H, GL_BGRA_EXT, GL_UNSIGNED_BYTE, back); } printf("glReadPixels 1024x768 BGRA_EXT: %.2f ms/frame\n", (now_ms() - t) / frames); }
|
||||
// 6. a whole GPU frame as the readback routes would run it: tiles up, composite, read back
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) { glBindTexture(GL_TEXTURE_2D, tex[0]); for (int k = 0; k < 12; k++) glTexSubImage2D(GL_TEXTURE_2D, 0, (k % 4) * 256, (k / 4) * 256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, tile); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glEnable(GL_BLEND); glUniform4f(um, 1, 1, 1, 0.5f); glBindTexture(GL_TEXTURE_2D, tex[1]); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisable(GL_BLEND); glUniform4f(um, 1, 1, 1, 1); glReadPixels(0, 0, W, H, GL_RGBA, GL_UNSIGNED_BYTE, back); }
|
||||
printf("GPU frame with readback (12 tiles up + 2 layers + read): %.2f ms/frame\n", (now_ms() - t) / frames);
|
||||
printf("GL error state: 0x%x\n", glGetError());
|
||||
|
||||
// CPU side, for the software route
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) memcpy(back, pix, (size_t)W * H * 4); printf("CPU memcpy 3 MB: %.2f ms\n", (now_ms() - t) / frames);
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) { uint32_t* d = (uint32_t*)back; const uint32_t* a = (const uint32_t*)pix; for (size_t i = 0; i < (size_t)W * H; i++) { uint32_t s = a[i], b = d[i]; uint32_t al = (s >> 24) & 0xff, ia = 255 - al; d[i] = ((((s & 0xff00ff) * al + (b & 0xff00ff) * ia) >> 8) & 0xff00ff) | ((((s & 0xff00) * al + (b & 0xff00) * ia) >> 8) & 0xff00) | 0xff000000; } }
|
||||
printf("CPU blend one 1024x768 ARGB layer over another (plain C): %.2f ms\n", (now_ms() - t) / frames);
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) { const int SW = 1000; uint32_t* d = (uint32_t*)back; const uint32_t* a = (const uint32_t*)pix; for (int y = 0; y < H; y++) { const uint32_t* row = a + (size_t)(y * SW / 768 % H) * W; for (int x = 0; x < 768; x++) d[(size_t)y * W + x] = row[x * SW / 768]; } }
|
||||
printf("CPU nearest-neighbour scale 1000->768 wide, 768 rows: %.2f ms\n", (now_ms() - t) / frames);
|
||||
t = now_ms(); for (int f = 0; f < frames; f++) memset(back, f, (size_t)W * H * 4); printf("CPU memset 3 MB: %.2f ms\n", (now_ms() - t) / frames);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2026 NotAlexNoyle. Apache-2.0.
|
||||
#
|
||||
# stall-resolve.py — the daemon's stall samples (/tmp/jihad-stall.txt on the device, written by the
|
||||
# sampler in render/goanna/GoannaRenderPage.cpp when $JIHAD_STALL_SAMPLER is set) turned into
|
||||
# function names and buckets: which part of the engine held the main thread.
|
||||
#
|
||||
# python3 -I tools/host/stall-resolve.py <jihad-stall.txt> [--xul <unstripped libxul.so>] [--top N]
|
||||
#
|
||||
# Each "stall N: <ms> ms, <n> samples" block is resolved on its own. libxul offsets go through the
|
||||
# toolchain's addr2line against the UNSTRIPPED libxul of the same build
|
||||
# (build/webos-oe/out-arm/obj-jihad-goanna-arm/toolkit/library/libxul.so); other modules are
|
||||
# counted by name; "jit" is code the JIT emitted (no module).
|
||||
import sys, os, re, subprocess, collections
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
REPO = os.path.dirname(os.path.dirname(HERE))
|
||||
XUL = os.path.join(REPO, "build/webos-oe/out-arm/obj-jihad-goanna-arm/toolkit/library/libxul.so")
|
||||
A2L = os.path.join(REPO, "build/webos-oe/toolchain/out-toolchain/x-tools/arm-webos-linux-gnueabi/bin/arm-webos-linux-gnueabi-addr2line")
|
||||
|
||||
BUCKETS = [
|
||||
("JS parse / bytecode", r"js::frontend::|BytecodeEmitter|TokenStream|js::Parser|FullParseHandler|SyntaxParseHandler|CompileScript|CompileModule"),
|
||||
("JIT compile (Ion/Baseline)", r"js::jit::(Ion|MIR|LIR|Lowering|Codegen|Baseline|Range|Scalar|Alias|Valu|Licm|GVN|Linear|Backtracking|Register|Macro|Assembler|JitCompiler|OptimizeMIR|CompileBackEnd|IonBuilder|BaselineCompiler|ICStub|CacheIR)|js::jit::Compile|jit::Assembler"),
|
||||
("GC", r"js::gc::|GCRuntime|js::Nursery|TenuringTracer|GCMarker|js::TraceEdge|TraceChildren|js::MarkingTracer|Arena::|Chunk::|Zone::sweep|SweepArena|FinalizeTypedArenas|IncrementalCollectSlice"),
|
||||
("Cycle collector", r"nsCycleCollector|CCGraph|nsPurpleBuffer|CycleCollect|Traverse\(|Unlink\(|NS_CYCLE_COLLECTION"),
|
||||
("Style (selectors, restyle)", r"nsCSSRuleProcessor|SelectorMatches|RuleProcessorData|RestyleManager|ElementRestyler|nsStyleSet::|nsRuleNode|ResolveStyleFor|nsStyleContext|StyleSetHandle|nsCSSSelector|RuleHash|ComputeStyle|GetStyleContext|nsTransitionManager|nsAnimationManager"),
|
||||
("Layout (reflow, frames)", r"::Reflow|nsBlockFrame|nsInlineFrame|nsFlexContainerFrame|nsGridContainerFrame|nsTableFrame|nsLineLayout|nsCSSFrameConstructor|nsFrameManager|ReflowInput|nsHTMLReflowState|PresShell::DoReflow|ProcessReflowCommands|FlushPendingNotifications|nsTextFrame|nsContainerFrame|nsLayoutUtils|BlockReflowInput|nsFloatManager|nsBidi"),
|
||||
("Paint / display list", r"nsDisplayList|nsDisplayItem|PaintRoot|BuildDisplayList|gfxContext|gfxPlatform|mozilla::gfx::|DrawTarget|cairo_|pixman_|PaintFrame|nsLayoutUtils::Paint|ImageLayer|BasicLayer|nsImageRenderer|gfxTextRun|gfxFont|FrameLayerBuilder|JihadRenderDocument|RenderDocument"),
|
||||
("HTML parse / DOM build", r"nsHtml5|nsContentSink|nsXMLContentSink|nsGenericHTMLElement::|Element::Set|nsINode::|nsDocument::|nsContentUtils|mozilla::dom::Element|nsAttrValue|nsContentList|nsDOMMutationObserver"),
|
||||
("JS interpreter / runtime", r"js::Interpret|js::RunScript|js::Call\(|js::InternalCallOrConstruct|js::jit::DoCallFallback|js::NativeObject|js::NativeGetProperty|js::GetProperty|js::SetProperty|js::ObjectGroup|js::Shape|js::TypeSet|js::jit::ICStubCompiler|js::StringBuffer|js::ConcatStrings|js::Invoke|JSObject::|js::NewObject|js::ArrayObject|js::str_|js::array_|js::RegExp|js::irregexp|js::ToString|js::ValueToSource|js::jit::VMFunction|js::jit::Invalidate|js::DeepCloneObject|js::Proxy|js::Wrapper|js::CrossCompartmentWrapper|XPCWrappedNative|xpc::|XrayWrapper|DOMProxyHandler|JSAutoCompartment"),
|
||||
("DOM bindings / XPCOM glue", r"mozilla::dom::.*Binding|GenericBindingMethod|GenericBindingGetter|binding_detail|nsXPCWrappedJS|XPCCallContext|XPCConvert|nsCOMPtr|NS_Addref|NS_Release|AddRef|Release\(|QueryInterface"),
|
||||
("Events / timers / tasks", r"nsTimer|TimerThread|nsRefreshDriver|EventDispatcher|nsEventDispatcher|EventListenerManager|nsThread::|nsRunnable|mozilla::Runnable|TaskQueue|MessageLoop|nsAppShell|ProcessNextEvent"),
|
||||
("Network / cache / sqlite", r"sqlite3|mozStorage|nsHttp|nsSocketTransport|nsCacheEntry|CacheFile|CacheIndex|nsInputStream|nsOutputStream|nsStreamLoader|nsCookieService|nsPermissionManager|nsDiskCache|nsUrlClassifier|nsStandardURL|nsIOService|nsLoadGroup|nsChannel"),
|
||||
("Images / media", r"imgLoader|imgRequest|RasterImage|Decoder|nsJPEG|nsPNG|nsGIF|SurfaceCache|VectorImage|MediaDecoder|MediaCache|MediaStream|FFmpeg|VPX|Opus|AudioStream|cubeb"),
|
||||
("Memory (malloc, strings)", r"jemalloc|arena_|malloc|free\b|moz_x|nsTArray|nsTString|nsAString|nsACString|nsString|memcpy|memmove|memset|strlen|std::"),
|
||||
]
|
||||
|
||||
def resolve_xul(offsets, xul, a2l):
|
||||
"""offsets: set of ints → dict offset → 'function' (the innermost inlined name)"""
|
||||
out = {}
|
||||
if not offsets or not os.path.exists(xul) or not os.path.exists(a2l): return out
|
||||
offs = sorted(offsets)
|
||||
for i in range(0, len(offs), 2000):
|
||||
chunk = offs[i:i+2000]
|
||||
p = subprocess.run([a2l, "-f", "-C", "-e", xul] + ["0x%x" % o for o in chunk], capture_output=True, text=True)
|
||||
lines = p.stdout.splitlines()
|
||||
for j, o in enumerate(chunk):
|
||||
fn = lines[2*j] if 2*j < len(lines) else "??"
|
||||
out[o] = fn
|
||||
return out
|
||||
|
||||
def bucket(fn):
|
||||
for name, rx in BUCKETS:
|
||||
if re.search(rx, fn): return name
|
||||
return "other"
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
if not args: print(__doc__); sys.exit(2)
|
||||
path = args[0]; xul = XUL; top = 30
|
||||
if "--xul" in args: xul = args[args.index("--xul") + 1]
|
||||
if "--top" in args: top = int(args[args.index("--top") + 1])
|
||||
stalls = []; cur = None
|
||||
for line in open(path, encoding="utf8", errors="replace"):
|
||||
line = line.strip()
|
||||
m = re.match(r"stall (\d+): (\d+) ms, (\d+) samples", line)
|
||||
if m: cur = {"n": int(m.group(1)), "ms": int(m.group(2)), "count": int(m.group(3)), "samples": []}; stalls.append(cur); continue
|
||||
if cur is not None and "+0x" in line:
|
||||
mod, off = line.rsplit("+0x", 1); cur["samples"].append((mod, int(off, 16)))
|
||||
xul_offsets = set(off for st in stalls for mod, off in st["samples"] if mod.startswith("libxul"))
|
||||
names = resolve_xul(xul_offsets, xul, A2L)
|
||||
for st in stalls:
|
||||
n = len(st["samples"]) or 1
|
||||
byfn = collections.Counter(); bybk = collections.Counter(); bymod = collections.Counter()
|
||||
for mod, off in st["samples"]:
|
||||
bymod[mod] += 1
|
||||
if mod.startswith("libxul"):
|
||||
fn = names.get(off, "libxul+0x%x" % off); byfn[fn] += 1; bybk[bucket(fn)] += 1
|
||||
elif mod == "jit": byfn["<JIT code>"] += 1; bybk["JIT code (the page's compiled JavaScript)"] += 1
|
||||
else: byfn[mod] += 1; bybk["other module: " + mod] += 1
|
||||
print("=== stall %d: %d ms, %d samples (10 ms each)" % (st["n"], st["ms"], n))
|
||||
print("--- by bucket:")
|
||||
for k, v in bybk.most_common(): print(" %5.1f%% %s" % (100.0 * v / n, k))
|
||||
print("--- top %d functions:" % top)
|
||||
for k, v in byfn.most_common(top): print(" %5.1f%% %s" % (100.0 * v / n, k[:140]))
|
||||
print()
|
||||
|
||||
if __name__ == "__main__": main()
|
||||
Reference in new issue
Block a user