diff --git a/.github/workflows/build-austin-dist.yml b/.github/workflows/build-austin-dist.yml new file mode 100644 index 0000000000..98c80d0284 --- /dev/null +++ b/.github/workflows/build-austin-dist.yml @@ -0,0 +1,250 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on: https://github.com/P403n1x87/austin/blob/v4.0.0/.github/workflows/release_arch.yml +# and https://github.com/P403n1x87/austin/blob/v4.0.0/.github/workflows/tests.yml +name: Build austin-dist wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/austin-dist.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-austin-dist.yml' + - 'docs/packages/austin-dist.yaml' + - 'patches/austin-dist/**' + push: + branches: [main] + paths: + - '.github/workflows/build-austin-dist.yml' + - 'docs/packages/austin-dist.yaml' + - 'patches/austin-dist/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + UV_EXTRA_INDEX_URL: https://pypi.riseproject.dev/simple/ + # austin-python pins protobuf~=3.12 and our registry only carries 7.x, which + # first-index resolution stops at. + UV_INDEX_STRATEGY: unsafe-best-match + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: austin-dist + version: ${{ inputs.version }} + + build_wheel: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + name: Build austin-dist ${{ matrix.version }} py3-none-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 60 + + env: + AUSTIN_DIST_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout austin v${{ matrix.version }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: P403n1x87/austin + ref: v${{ env.AUSTIN_DIST_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Apply patches + run: git apply -v python-wheels/patches/austin-dist/${{ env.AUSTIN_DIST_VERSION }}/*.patch + + # The wheel carries austin alone, the way upstream's musl wheels do: + # austinp's native unwinding segfaults on riscv64, in austin's own MOJO + # emitter and inside libunwind's riscv64 symbol lookup. + - name: Build austin and package the wheel + run: | + docker run --rm \ + -v "$(pwd)":/workspace \ + --workdir /workspace \ + -e AUSTIN_DIST_VERSION \ + "${{ env.MANYLINUX_RISCV64_IMAGE }}" \ + bash -c ' + set -euxo pipefail + dnf -y --disablerepo=extras install autoconf automake + autoreconf --install + ./configure + make -j"$(nproc)" + /opt/python/cp312-cp312/bin/python -m pip install -q wheel + /opt/python/cp312-cp312/bin/python scripts/build-wheel.py \ + --version "$AUSTIN_DIST_VERSION" \ + --platform manylinux_2_39_riscv64 \ + --files austin:src/austin + ' + + - name: Verify the wheel ships the riscv64 binary + run: | + python3 - dist/*.whl <<'EOF' + import sys, zipfile + with zipfile.ZipFile(sys.argv[1]) as zf: + names = zf.namelist() + print("\n".join(names)) + scripts = sorted(n.rsplit("/", 1)[-1] for n in names if ".data/scripts/" in n) + assert scripts == ["austin"], scripts + licenses = sorted(n.rsplit("/", 1)[-1] for n in names if ".dist-info/licenses/" in n) + assert licenses == ["LICENSE.md"], licenses + header = zf.read(next(n for n in names if ".data/scripts/" in n))[:20] + assert header[:4] == b"\x7fELF", header + assert header[18] == 0xF3, header + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: austin-dist-${{ env.AUSTIN_DIST_VERSION }}-py3-none-manylinux_riscv64 + path: dist/*.whl + if-no-files-found: error + + test_wheel: + name: Test austin-dist ${{ matrix.version }} on Python ${{ matrix.python-version }} + needs: [setup, build_wheel] + if: needs.setup.outputs.versions != '[]' + runs-on: ubuntu-24.04-riscv + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + # Upstream tests 3.9 to 3.14; trimmed to the interpreters this registry targets. + python-version: ['3.12', '3.13', '3.14'] + + env: + AUSTIN_DIST_VERSION: ${{ matrix.version }} + AUSTIN_TESTS_PYTHON_VERSIONS: ${{ matrix.python-version }} + + steps: + - name: Checkout austin v${{ matrix.version }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: P403n1x87/austin + ref: v${{ env.AUSTIN_DIST_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Apply patches + run: git apply -v python-wheels/patches/austin-dist/${{ env.AUSTIN_DIST_VERSION }}/*.patch + + - name: Download wheel + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: austin-dist-${{ env.AUSTIN_DIST_VERSION }}-py3-none-manylinux_riscv64 + path: wheelhouse + + - name: Install Python + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + python-version: ${{ matrix.python-version }} + activate-environment: true + enable-cache: false + + - name: Install the wheel and upstream's test dependencies + run: uv pip install -r test/requirements.txt wheelhouse/*.whl + + # The suite runs the binary from src/, which is where upstream's own test + # jobs download it to; here it comes out of the installed wheel. + - name: Stage the wheel's binary + run: cp "$(command -v austin)" src/ + + # Upstream's own test jobs do this so that the suite can print a + # backtrace of a crashed austin process. + - name: Enable crash diagnostics + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq --no-install-recommends gdb + echo "core.%p" | sudo tee /proc/sys/kernel/core_pattern + + - name: Run upstream's tests + run: | + ulimit -c unlimited + python -m pytest -svr a test/functional test/integrity + + # test_where_multiprocess asserts that a single -w snapshot catches both + # children inside fact(); upstream marks it flaky and these runners are + # slow enough to lose that race on every attempt. + - name: Run upstream's tests as root + run: | + ulimit -c unlimited + sudo -E env PATH="$PATH" python -m pytest -svr a test/functional test/integrity \ + --deselect test/functional/test_attach.py::test_where_multiprocess + + gpl_sources: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + name: Collect GPL sources for austin-dist ${{ matrix.version }} + runs-on: ubuntu-24.04-riscv + + env: + AUSTIN_DIST_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: ./actions/collect-gpl-sources + with: + image: ${{ env.MANYLINUX_RISCV64_IMAGE }} + packages: gcc + output: gpl-sources.tar + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: austin-dist-${{ env.AUSTIN_DIST_VERSION }}-gpl-sources + path: gpl-sources.tar + if-no-files-found: error + + publish: + name: Publish austin-dist ${{ matrix.version }} + needs: [setup, build_wheel, test_wheel, gpl_sources] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: austin-dist-${{ matrix.version }}-py3-none-manylinux_riscv64 + gpl-sources-artifact: austin-dist-${{ matrix.version }}-gpl-sources + gpl-sources-description: gcc diff --git a/docs/packages/austin-dist.yaml b/docs/packages/austin-dist.yaml new file mode 100644 index 0000000000..72d678db7d --- /dev/null +++ b/docs/packages/austin-dist.yaml @@ -0,0 +1,11 @@ +package-name: austin-dist +source-code: https://github.com/P403n1x87/austin +license: GPL-3.0-or-later +versions: +- version: 4.0.0 + patched: true + warning: >- + The riscv64 wheel ships the austin binary only, as upstream's musllinux wheels do. + austinp, the variant that also samples native stacks, segfaults on riscv64 while + sampling: once in austin's own MOJO emitter, and once inside libunwind's riscv64 + ELF symbol lookup. diff --git a/patches/austin-dist/4.0.0/0001-build-wheel-add-a-riscv64-Linux-wheel-platform.patch b/patches/austin-dist/4.0.0/0001-build-wheel-add-a-riscv64-Linux-wheel-platform.patch new file mode 100644 index 0000000000..d1f7054cba --- /dev/null +++ b/patches/austin-dist/4.0.0/0001-build-wheel-add-a-riscv64-Linux-wheel-platform.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Mon, 21 Sep 2026 00:00:00 +0000 +Subject: [PATCH] build-wheel: add a riscv64 Linux wheel platform + +scripts/build-wheel.py drives --platform off the AUSTIN_WHEELS table, so a +platform missing from it cannot be built at all: + + build-wheel.py: error: argument --platform: invalid choice: 'manylinux_2_39_riscv64' + +Add the riscv64 entry. manylinux_2_39 is the oldest manylinux profile that +exists for riscv64 (glibc 2.39, Rocky 10). The wheel carries austin alone, as +the musl wheels do: austinp needs libunwind to walk native stacks, and on +riscv64 both austin's MOJO emitter and libunwind's own ELF symbol lookup +segfault while it samples. + +Upstream-Status: To upstream [the release asset this entry names, austin--gnu-linux-riscv64.tar.xz, is not produced by upstream yet, so the table entry is only useful there together with a riscv64 leg in release_arch.yml; the port builds the binary from source and passes it with --files] + +Signed-off-by: Ludovic Henry +--- +diff --git a/scripts/build-wheel.py b/scripts/build-wheel.py +--- a/scripts/build-wheel.py ++++ b/scripts/build-wheel.py +@@ -53,6 +53,7 @@ AUSTIN_WHEELS = { + "gnu-linux-ppc64le.tar.xz", + ("austin", "austinp"), + ), ++ "manylinux_2_39_riscv64": ("gnu-linux-riscv64.tar.xz", ("austin",)), + "macosx_11_0_x86_64": ("mac64.zip", ("austin",)), + "macosx_11_0_arm64": ("mac-arm64.zip", ("austin",)), + "musllinux_1_1_aarch64": ("musl-linux-aarch64.tar.xz", ("austin",)), diff --git a/patches/austin-dist/4.0.0/0002-build-wheel-ship-the-licence-files-in-the-wheel.patch b/patches/austin-dist/4.0.0/0002-build-wheel-ship-the-licence-files-in-the-wheel.patch new file mode 100644 index 0000000000..a29cd974f8 --- /dev/null +++ b/patches/austin-dist/4.0.0/0002-build-wheel-ship-the-licence-files-in-the-wheel.patch @@ -0,0 +1,78 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Mon, 21 Sep 2026 00:00:00 +0000 +Subject: [PATCH] build-wheel: ship the licence files in the wheel + +The wheels ship the austin and austinp binaries, and austin is GPL-3.0, but +no copy of the licence travels with them: the only trace of it is the +"License: GPLv3+" metadata field. Section 4 of the GPL asks whoever +distributes the program to hand out the licence text with it, and the +binaries also carry statically linked third-party code (libunwind, and +binutils' libbfd, libiberty and libsframe for austinp) whose licences belong +in the wheel too. + +Write every file named by the new --licenses option into +/licenses/ and declare it with License-File, which needs +Metadata-Version 2.4 (PEP 639). The default keeps the project's own +LICENSE.md in every wheel. + +Upstream-Status: To upstream [not yet submitted] + +Signed-off-by: Ludovic Henry +--- +diff --git a/scripts/build-wheel.py b/scripts/build-wheel.py +--- a/scripts/build-wheel.py ++++ b/scripts/build-wheel.py +@@ -80,7 +80,7 @@ def make_message(headers, payload=None): + return message.getvalue().encode("utf-8") + + +-def write_austin_wheel(out_dir, *, version, platform, austin_bin_data): ++def write_austin_wheel(out_dir, *, version, platform, austin_bin_data, license_files): + package_name = "austin-dist" + python = "py3" + dist_name = package_name.replace("-", "_") +@@ -99,10 +99,11 @@ def write_austin_wheel(out_dir, *, version, platform, austin_bin_data): + + contents[f"{dist_info}/METADATA"] = make_message( + { +- "Metadata-Version": "2.1", ++ "Metadata-Version": "2.4", + "Name": package_name, + "Version": version, + **METADATA, ++ "License-File": [_.name for _ in license_files], + }, + Path("README.md") + .read_text("utf-8") +@@ -111,6 +112,9 @@ def write_austin_wheel(out_dir, *, version, platform, austin_bin_data): + 'img src="https://raw.githubusercontent.com/P403n1x87/austin/refs/heads/master/art', + ), + ) ++ for license_file in license_files: ++ contents[f"{dist_info}/licenses/{license_file.name}"] = license_file.read_bytes() ++ + contents[f"{dist_info}/WHEEL"] = make_message( + { + "Wheel-Version": "1.0", +@@ -179,6 +183,14 @@ if __name__ == "__main__": + default=None, + ) + ++ argp.add_argument( ++ "--licenses", ++ help="Licence files to ship in the wheel", ++ nargs="+", ++ type=Path, ++ default=[Path("LICENSE.md")], ++ ) ++ + argp.add_argument( + "--files", + help="The variant to binary file mapping (e.g. austin:src/austin)", +@@ -215,4 +227,5 @@ if __name__ == "__main__": + version=version, + platform=platform, + austin_bin_data=bin_data, ++ license_files=args.licenses, + ) diff --git a/patches/austin-dist/4.0.0/0003-test-tolerate-a-host-with-no-var-log-syslog.patch b/patches/austin-dist/4.0.0/0003-test-tolerate-a-host-with-no-var-log-syslog.patch new file mode 100644 index 0000000000..d1c6556948 --- /dev/null +++ b/patches/austin-dist/4.0.0/0003-test-tolerate-a-host-with-no-var-log-syslog.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Mon, 21 Sep 2026 00:00:00 +0000 +Subject: [PATCH] test: tolerate a host with no /var/log/syslog + +collect_logs() is called from Variant.prepare_result() after every single +austin invocation, and on Linux it opens /var/log/syslog unconditionally. That +file is an rsyslog artefact: a journald-only host (Fedora, RHEL, Rocky, Arch, +or a minimal Ubuntu image like the self-hosted riscv64 runners) has none, so +the helper raises before the test under it can assert anything: + + test/utils.py:154: in collect_logs + with Path("/var/log/syslog").open(errors="replace") as logfile: + E FileNotFoundError: [Errno 2] No such file or directory: '/var/log/syslog' + + ================== 40 failed, 27 skipped in 512.75s (0:08:32) ================== + +Every failure in that run was this line; nothing about austin was exercised. +Return no logs when the file is absent, which is what the macOS branch below +already does, and leave the diagnostics untouched where rsyslog is installed. + +Upstream-Status: To upstream [not yet submitted] + +Signed-off-by: Ludovic Henry +--- +diff --git a/test/utils.py b/test/utils.py +--- a/test/utils.py ++++ b/test/utils.py +@@ -151,7 +151,12 @@ def collect_logs(variant: str, pid: int) -> List[str]: + needles: tuple[str, ...] + match platform.system(): + case "Linux": +- with Path("/var/log/syslog").open(errors="replace") as logfile: ++ syslog = Path("/var/log/syslog") ++ if not syslog.is_file(): ++ # Not every Linux host has rsyslog; on a journald-only system ++ # there is no file to grep for austin's messages. ++ return [] ++ with syslog.open(errors="replace") as logfile: + needles = (f"{variant}[{pid}]", f"systemd-coredump[{pid}]") + return [ + f" logs for {variant}[{pid}] ".center(80, "="),