Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 56 additions & 25 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,48 +234,38 @@ jobs:
fail-fast: false
matrix:
include:
# Every asset is a musl static build, from the toolchains on the
# repo's `toolchains` release. Static glibc could not reliably
# resolve names (getaddrinfo wants the host's NSS libraries at
# run time, which a foreign device does not have), and the two
# 32-bit assets must be soft-float: hard-float is SIGILL on
# boards without VFP, and soft-float runs on both.
- arch: x86_64
cross: ''
cross_pkg: ''
toolchain_asset: ''
cross: 'x86_64-linux-musl-'
toolchain_asset: 'x86_64-linux-musl-cross.tgz'
- arch: aarch64
cross: 'aarch64-linux-gnu-'
cross_pkg: 'gcc-aarch64-linux-gnu'
toolchain_asset: ''
cross: 'aarch64-linux-musl-'
toolchain_asset: 'aarch64-linux-musl-cross.tgz'
- arch: aarch64_be
cross: 'aarch64_be-linux-musl-'
cross_pkg: ''
toolchain_asset: 'aarch64_be-linux-musl-cross.tgz'
# 'armv7', not the 'armv7l' that uname -m reports: this is the
# asset name, and every consumer (install-agent.sh, push-agent,
# agent --update) normalizes the uname string to 'armv7' before
# building the URL. Publishing 'armv7l' 404s all three.
- arch: armv7
cross: 'arm-linux-gnueabihf-'
cross_pkg: 'gcc-arm-linux-gnueabihf'
toolchain_asset: ''
# Soft-float, not hard-float. Real big-endian ARM embedded silicon
# reports no `vfp` and runs a soft-float userland, so the
# hard-float asset was SIGILL on it — measured on hardware.
# Soft-float also runs on VFP hardware, so one asset covers both.
cross: 'arm-linux-musleabi-'
toolchain_asset: 'arm-linux-musleabi-cross.tgz'
# NOTE: needs armeb-linux-musleabi-cross.tgz on the `toolchains`
# release; the hard-float tarball will not build this target.
- arch: armeb
cross: 'armeb-linux-musleabi-'
cross_pkg: ''
toolchain_asset: 'armeb-linux-musleabi-cross.tgz'
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Install cross-compiler (apt)
if: matrix.cross_pkg != ''
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq ${{ matrix.cross_pkg }}

- name: Install cross-compiler (musl toolchain)
if: matrix.toolchain_asset != ''
env:
GH_TOKEN: ${{ github.token }}
run: |
Expand Down Expand Up @@ -306,6 +296,39 @@ jobs:
file "$BIN"
echo "bin=agent-c/${BIN#./}" >> "$GITHUB_OUTPUT"

# Assert what the asset claims, not just print it. Both traps below
# were measured on hardware: a hard-float 32-bit build is SIGILL on a
# board without VFP, and a BE-32 build is SIGILL on every ARMv6+.
- name: Check the binary's linking and ABI
shell: bash
run: |
BIN="${{ steps.agent.outputs.bin }}"
file "$BIN" | grep -Eq 'static(ally|-pie) linked' \
|| { echo "not statically linked"; exit 1; }
case "${{ matrix.arch }}" in
armv7|armeb)
readelf -h "$BIN" | grep -q 'soft-float ABI' \
|| { echo "expected a soft-float ABI"; readelf -h "$BIN"; exit 1; } ;;
esac
case "${{ matrix.arch }}" in
armeb)
readelf -h "$BIN" | grep -q 'BE8' \
|| { echo "expected BE8"; readelf -h "$BIN"; exit 1; } ;;
esac
readelf -h "$BIN" | grep -E 'Class|Data|Machine|Flags'

# The release asset is stripped; the unstripped binary stays a CI
# artifact for symbolizing a crash. Its name deliberately does not
# start with perflens-agent-, which is what the release job collects.
- name: Strip
shell: bash
run: |
BIN="${{ steps.agent.outputs.bin }}"
mkdir -p debug
cp "$BIN" "debug/agent-unstripped-${{ matrix.arch }}"
"${{ matrix.cross }}strip" "$BIN"
ls -la "$BIN" "debug/agent-unstripped-${{ matrix.arch }}"

- name: Package
shell: bash
run: |
Expand All @@ -318,9 +341,9 @@ jobs:
ASSET="perflens-agent-linux-${{ matrix.arch }}"
cp "$BIN" "dist/${ASSET}"
chmod +x "dist/${ASSET}"
# Checksum sidecar, mirroring the tools bundle. install-agent.sh and
# push-agent verify against this; the C agent does not (it has no
# SHA-256 of its own and will not grow one just for this).
# Checksum sidecar, mirroring the tools bundle. install-agent.sh,
# push-agent and the agent's own --update verify against it (the
# agent through the device's sha256sum).
(cd dist && sha256sum "${ASSET}" > "${ASSET}.sha256")
# Versioned tarball for manual downloads.
PKG_NAME="perflens-agent-c-${VERSION}-linux-${{ matrix.arch }}"
Expand All @@ -339,6 +362,14 @@ jobs:
dist/perflens-agent-linux-*
if-no-files-found: error

- name: Upload unstripped binary
uses: actions/upload-artifact@v5
with:
name: perflens-agent-debug-${{ matrix.arch }}
path: debug/agent-unstripped-*
if-no-files-found: error
retention-days: 30

# -------------------------------------------------------------------------
# Release on tag: agent binaries, tools bundles, wheel + sdist.
# -------------------------------------------------------------------------
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,72 @@ jobs:
- name: Run pytest
run: python -m pytest tests/ -v -ra

# -------------------------------------------------------------------------
# The C agent under AddressSanitizer+UBSan and under ThreadSanitizer,
# driving the same protocol tests; plus its native unit tests. A daemon
# that forks perf from several threads and answers a network peer is
# exactly what these catch, and nothing ran them before.
# -------------------------------------------------------------------------
agent-sanitizers:
name: agent (${{ matrix.sanitize }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitize: ['address,undefined', 'thread']
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'

- name: Build instrumented agent
run: make -C agent-c SANITIZE=${{ matrix.sanitize }}

# Same SANITIZE: the unit tests run instrumented too, and link
# against the objects the step above built.
- name: C unit tests
run: make -C agent-c check SANITIZE=${{ matrix.sanitize }}

- name: Install package + test deps
run: python -m pip install -e '.[dev]'

- name: Protocol tests under the sanitizer
env:
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
TSAN_OPTIONS: halt_on_error=1
run: python -m pytest tests/test_agent_protocol.py -q

# A report goes to the agent's stderr, which the harness captures
# into agent.log; halt_on_error makes the agent die and the test fail,
# but grep as well so a report on the exit path cannot slip through.
- name: No sanitizer reports in any agent log
if: always()
run: |
if grep -rl 'Sanitizer\|runtime error:' /tmp/pytest-of-*/pytest-*/*/agent.log 2>/dev/null; then
grep -rh -A15 'Sanitizer\|runtime error:' /tmp/pytest-of-*/pytest-*/*/agent.log | head -100
exit 1
fi

# -------------------------------------------------------------------------
# The shell scripts: the curl-able installer, the package builder and
# the docs-capture helpers were never linted or run by CI.
# -------------------------------------------------------------------------
shell:
name: shellcheck
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install shellcheck
run: sudo apt-get update -qq && sudo apt-get install -y -qq shellcheck
- name: Lint
run: shellcheck -S warning install-agent.sh build_package.sh tools/*.sh

# -------------------------------------------------------------------------
# Frontend: typecheck + vitest unit tests + Playwright browser E2E
# against a real server replaying a device-captured fixture session
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ tests/sample_workload
.DS_Store
*.o
agent-c/perflens-agent
agent-c/obj/
agent-c/perflens-agent-*
tests/matrixlab/build/
tests/matrixlab/bin/
Expand All @@ -20,3 +21,5 @@ src/perflens/ui/
frontend/test-results/
frontend/playwright-report/
*.tsbuildinfo
# Local review notes, not for the repo
/AGENT_FINDINGS.md
Loading
Loading