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
96 changes: 96 additions & 0 deletions .github/workflows/build-viztracer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# Based on upstream's CI:
# https://github.com/gaogaotiantian/viztracer/blob/1.1.1/.github/workflows/wheels.yml
name: Build viztracer wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'viztracer version to build (git tag, e.g. 1.1.1)'
required: true
default: '1.1.1'
pull_request:
paths:
- '.github/workflows/build-viztracer.yml'
- 'patches/viztracer/**'

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '1.1.1' }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read # to fetch code (actions/checkout)

env:
VIZTRACER_VERSION: ${{ inputs.version || '1.1.1' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

jobs:
setup:
uses: $/.github/workflows/_setup.yml

build_wheels:
needs: [setup]
name: Build viztracer ${{ inputs.version || '1.1.1' }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
python: ["cp312", "cp313", "cp314", "cp314t"]

steps:
- name: Checkout viztracer ${{ env.VIZTRACER_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: gaogaotiantian/viztracer
ref: ${{ env.VIZTRACER_VERSION }}
persist-credentials: false

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false

# Two tests hang forever on riscv64 and four fail (see the patches): self-tracing
# overhead, Perfetto's trace_processor_shell having no riscv64 prebuilt, and no
# riscv64 build of the cross-process attach .so.
- name: Patch viztracer source
run: |
git apply python-wheels/patches/viztracer/${{ env.VIZTRACER_VERSION }}/00*.patch

- name: Build and test wheel
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
output-dir: wheelhouse/
only: ${{ matrix.python }}-manylinux_riscv64
env:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# TestLoky (tests/test_multiprocess.py) imports loky unconditionally; every
# other optional dep upstream's suite touches (torch, jaxlib, ipywidgets)
# is self-skipping on ImportError or platform.
CIBW_TEST_REQUIRES: loky>=3.0.0,<3.5.0
# cibuildwheel runs the tests in an empty directory, so stage upstream's suite
# there; test_combine also reaches outside tests/ for its example data files.
CIBW_TEST_SOURCES: tests example/json
CIBW_TEST_COMMAND: python -m unittest -v

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: viztracer-${{ env.VIZTRACER_VERSION }}-${{ matrix.python }}-manylinux_riscv64
path: wheelhouse/*.whl
if-no-files-found: error

publish:
name: Publish viztracer ${{ inputs.version || '1.1.1' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: viztracer-${{ inputs.version || '1.1.1' }}-*-manylinux_riscv64
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Sun, 6 Sep 2026 00:00:00 +0000
Subject: [PATCH] tests: skip test_trace_self, which hangs indefinitely on
riscv64

Upstream-Status: Inappropriate [riscv64-only hang, never exercised by upstream's own x86/aarch64 CI hardware]

test_trace_self runs `viztracer --trace_self vizviewer --server_only
<json>` and waits for "Ctrl+C" to appear on the child's stdout before
sending SIGTERM (tests/cmdline_tmpl.py's `template()`, string-`wait`
branch): a plain `while True: line = p.stdout.readline()` loop with no
timeout at all.

On riscv64 this readline loop never returns: this exact test hung
identically across all four interpreters (cp312/cp313/cp314/cp314t) until
GitHub's own 60-minute job timeout killed it, producing zero output after
"test_trace_self start".

A likely contributing mechanism: get_system_ts()/get_system_ns() in
src/viztracer/modules/quicktime.h use __rdtsc() on x86 (a single
non-syscall instruction) but fall back to clock_gettime(CLOCK_MONOTONIC)
everywhere else, including riscv64, and --trace_self hooks every
Python-level call. A bounded self-trace smoke test (`viztracer --trace_self
-c "print(1)"`) still completes in ~1s on this runner, so self-tracing
alone is not universally catastrophic here -- something specific to
self-tracing `vizviewer --server_only`'s heavier import graph (argparse,
socketserver, http.server, ...) and/or its long-running server loop is
what never reaches the "Press Ctrl+C to quit" print. Regardless of the
exact mechanism, the test's own wait has no timeout, so any slowdown here
becomes a permanent hang instead of a failure.

Signed-off-by: Ludovic Henry <git@ludovic.dev>
---
tests/test_cmdline.py | 1 +
1 file changed, 1 insertion(+)

diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py
index c37d4a2..98efd4d 100644
--- a/tests/test_cmdline.py
+++ b/tests/test_cmdline.py
@@ -435,6 +435,7 @@ class TestCommandLineBasic(CmdlineTmpl):
self.template([sys.executable, "-m", "viztracer", "--tracer_entries", "1000", "cmdline_test.py"])
self.template([sys.executable, "-m", "viztracer", "--tracer_entries", "50", "cmdline_test.py"])

+ @skipIf(True, "riscv64: hangs forever, see build-viztracer.yml")
def test_trace_self(self):
def check_func(data):
self.assertGreater(len(data["traceEvents"]), 1000)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Sun, 6 Sep 2026 00:00:00 +0000
Subject: [PATCH] tests: skip test_use_external_processor, which hangs
forever on riscv64

Upstream-Status: Inappropriate [riscv64 has no Perfetto trace_processor_shell prebuilt; upstream never exercises this combination]

--use_external_processor shells out to src/viztracer/web_dist/trace_processor,
Perfetto's own prebuilt-binary launcher. Its TRACE_PROCESSOR_SHELL_MANIFEST
lists prebuilts only for x86_64 and aarch64 machines; on riscv64 no entry
matches and it raises "No prebuilts available for linux-riscv64" and exits
immediately.

viztracer's own ExternalProcessorProcess._wait_start() does not handle that:

while True:
line = self._process.stderr.readline().decode("utf-8")
if "This server can be used" in line:
break

Once the child has exited, readline() on its now-closed stderr pipe returns
"" forever instead of blocking, so this becomes a tight busy-loop that spins
until the CI job's own timeout kills it rather than raising or returning.
This is a real bug in viztracer (any crashed/fast-exiting trace_processor
child triggers it, not just a missing riscv64 prebuilt), but the practical
blocker for us is Perfetto shipping no riscv64 binary at all, which is
outside what a build/test patch here can fix.

Signed-off-by: Ludovic Henry <git@ludovic.dev>
---
tests/test_viewer.py | 1 +
1 file changed, 1 insertion(+)

diff --git a/tests/test_viewer.py b/tests/test_viewer.py
index 0000000..0000000 100644
--- a/tests/test_viewer.py
+++ b/tests/test_viewer.py
@@ -225,6 +225,7 @@ class TestViewer(CmdlineTmpl):
os.remove(f.name)

@unittest.skipIf(sys.platform == "win32", "Can't send Ctrl+C reliably on Windows")
+ @unittest.skip("riscv64: hangs forever, see build-viztracer.yml")
def test_use_external_processor(self):
json_script = '{"file_info": {}, "traceEvents": []}'
try:
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Sun, 6 Sep 2026 00:00:00 +0000
Subject: [PATCH] tests: skip attach tests riscv64 has no attach .so for

Upstream-Status: Inappropriate [riscv64 has no prebuilt attach_riscv64.so; there is no source to build one from]

viztracer ships prebuilt cross-process "attach to a running interpreter"
shared libraries under src/viztracer/attach_process/ (attach_linux_amd64.so,
attach_linux_x86.so, attach_x86_64.dylib, ...) vendored from a third-party
project (see attach_process/LICENSE); there is no C/C++ source for them in
this repository and no build step compiles them - setup.py's package_data
only lists whichever prebuilt file matches platform.machine(). There is no
riscv64 entry at all, so add_code_to_python_process.py's
run_python_code_linux() always raises "Could not find .so for attach to
process." on this architecture. This is a real, upstream, no-source-available
gap, not something a build/test patch here can fix.

tests/test_remote.py's own `attach_unavailable` skip guard only excludes
win32 and some macOS/Python combinations, so it never anticipated "Linux
but no attach binary for this CPU architecture" and these tests run and
fail on riscv64:

FAIL: test_basic (tests.test_remote.TestAttachSanity.test_basic)
FAIL: test_attach (tests.test_remote.TestRemote.test_attach)
FAIL: test_attach_installed (tests.test_remote.TestRemote.test_attach_installed)
FAIL: test_uninstall (tests.test_remote.TestRemote.test_uninstall)

test_remote.py's own test_install and test_attach_script are unaffected -
both drive viztracer's in-process attach API (SIGUSR-based install/
viztracer.attach.start_attach), not the cross-process .so injection path.

Signed-off-by: Ludovic Henry <git@ludovic.dev>
---
tests/test_remote.py | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/tests/test_remote.py b/tests/test_remote.py
index bbeb27c..f78db47 100644
--- a/tests/test_remote.py
+++ b/tests/test_remote.py
@@ -39,6 +39,7 @@ class TestRemote(CmdlineTmpl):
self.assertFileExists("remote.json")
os.remove("remote.json")

+ @unittest.skip("riscv64: no attach_riscv64.so shipped, see build-viztracer.yml")
def test_attach_installed(self):
file_to_attach = textwrap.dedent("""
from viztracer import VizTracer
@@ -57,6 +58,7 @@ class TestRemote(CmdlineTmpl):
self.attach_check(file_to_attach, attach_cmd, output_file)
self.attach_check(file_to_attach, attach_installed_cmd, output_file, use_installed=True)

+ @unittest.skip("riscv64: no attach_riscv64.so shipped, see build-viztracer.yml")
def test_attach(self):
file_to_attach = textwrap.dedent("""
import time
@@ -142,6 +144,7 @@ class TestRemote(CmdlineTmpl):
p_attach_invalid.wait()
self.assertTrue(p_attach_invalid.returncode != 0)

+ @unittest.skip("riscv64: no attach_riscv64.so shipped, see build-viztracer.yml")
def test_uninstall(self):
file_to_attach = textwrap.dedent("""
import time
@@ -228,6 +231,7 @@ class TestRemoteFail(CmdlineTmpl):

@unittest.skipIf(attach_unavailable, "Does not support this platform")
class TestAttachSanity(CmdlineTmpl):
+ @unittest.skip("riscv64: no attach_riscv64.so shipped, see build-viztracer.yml")
def test_basic(self):
file_to_attach = textwrap.dedent("""
import time