diff --git a/apps/backtrace/EXPECTED.md b/apps/backtrace/EXPECTED.md new file mode 100644 index 0000000000..d11b4cb88f --- /dev/null +++ b/apps/backtrace/EXPECTED.md @@ -0,0 +1,101 @@ +# Expected Backtrace Demo Markers + +Use this file as a quick checklist while reviewing logs. The commands are listed +in [README.md](README.md). + +## Demo 1: Raw Backtrace + +Must contain: + +```text +Running backtrace tests... +BACKTRACE_BEGIN +BT 0 ip=0x... fp=0x... +BT 1 ip=0x... fp=0x... +BACKTRACE_END +test pass +``` + +Must not require: + +```text +=== host backtrace symbolize === +``` + +`--no-symbolize` intentionally disables host symbolization. + +## Demo 2: Auto Host Symbolize + +Must contain: + +```text +Running backtrace tests... + +test pass + + +=== host backtrace symbolize === +BACKTRACE_BLOCK +BT 0 ip=0x... fp=0x... [function name] +BT 1 ip=0x... fp=0x... [function name] +ok: backtrace +``` + +The exact function names can vary with optimization and toolchain details, but +the host-side block should not be empty. + +## Demo 3: DWARF Auto Symbolize + +Must contain: + +```text +emitting raw backtrace report (normal fp chain)... + +test pass + +=== host backtrace symbolize === +BACKTRACE_BLOCK 0 kind=raw +``` + +When DWARF/debug info is available, the symbolized block should include the +synthetic call chain from `test-suit/arceos/rust/backtrace-raw-normal/src/main.rs`: + +```text +c +b +a +``` + +## Demo 4: StarryOS Memtrack Backtrace + +Must contain: + +```text +Memory allocation sample recorded +Hard memory allocation sample recorded +BACKTRACE_BEGIN kind=alloc +BT 0 ip=0x... +BACKTRACE_END +STARRY_MEMTRACK_BACKTRACE_OK + +=== host backtrace symbolize === +BACKTRACE_BLOCK ... kind=alloc +``` + +The `sample_hard` allocation path may show extra frames such as: + +```text +starry_memtrack_sample_hard_leaf +starry_memtrack_sample_hard_mid +``` + +Those frames are useful for inspection, but the stable guest-side assertion is +the deterministic `BACKTRACE_BEGIN kind=alloc` block. + +The helper script automatically host-symbolizes the captured app output. When +symbols are available, the symbolized output may also include: + +```text +BACKTRACE_BLOCK +starry_memtrack_symbolize_probe +``` diff --git a/apps/backtrace/README.md b/apps/backtrace/README.md new file mode 100644 index 0000000000..feb680a123 --- /dev/null +++ b/apps/backtrace/README.md @@ -0,0 +1,354 @@ +# Backtrace Showcase + +This directory is a runnable guide for the TGOSKits backtrace demos. It is +intended for contributors and reviewers: start here when you need to configure +the environment, build StarryOS/ArceOS, run the demos, enable backtrace support +in another program, and check what each backtrace mode proves. + +The demos cover four paths: + +| Demo | Command target | What it shows | +| ---- | -------------------------------------------- | ----------------------------------------------------------------- | +| 1 | ArceOS `backtrace` with `--no-symbolize` | Target-side raw `BACKTRACE_BEGIN` / `BT` / `BACKTRACE_END` output | +| 2 | ArceOS `backtrace` with auto symbolize | Host-side auto-symbolization from the same build's ELF | +| 3 | ArceOS `backtrace-raw-normal` with `DWARF=y` | Deeper frame-pointer chain plus DWARF-enabled host symbolize | +| 4 | StarryOS `qemu/memtrack-backtrace` app | Allocation tracking raw backtraces plus host symbolize | + +For expected log markers, see [EXPECTED.md](EXPECTED.md). + +## Architecture Coverage + +The showcase is configured for all four TGOSKits QEMU architectures: + +| Demo | x86_64 | aarch64 | riscv64 | loongarch64 | +| ---- | ------ | ------- | ------- | ----------- | +| 1: ArceOS raw, no symbolize | yes | yes | yes | yes | +| 2: ArceOS auto host symbolize | yes | yes | yes | yes | +| 3: ArceOS DWARF raw-normal | yes | yes | yes | yes | +| 4: StarryOS memtrack backtrace | yes | yes | yes | yes | + +Architecture names map to these Rust target triples: + +| Arch | Target triple | +| ---- | ------------- | +| `x86_64` | `x86_64-unknown-none` | +| `aarch64` | `aarch64-unknown-none-softfloat` | +| `riscv64` | `riscv64gc-unknown-none-elf` | +| `loongarch64` | `loongarch64-unknown-none-softfloat` | + +Use any supported architecture as the optional second argument: + +```bash +bash apps/backtrace/run_demo.sh demo3 riscv64 +bash apps/backtrace/run_demo.sh demo4 aarch64 +``` + +Run one demo across the whole matrix: + +```bash +bash apps/backtrace/run_demo.sh demo3-all +bash apps/backtrace/run_demo.sh demo4-all +``` + +Run the complete matrix: + +```bash +bash apps/backtrace/run_demo.sh all-arch +``` + +Coverage means each demo has a per-architecture build config, QEMU config, and +helper-script entry point. In local Docker validation, Demo 3 prints symbolized +`c -> b -> a -> main` frame chains on all four architectures. Demo 4 emits +allocation raw blocks and the helper script host-symbolizes `kind=alloc` blocks +on all four architectures. + +## Use Backtrace In Your Own Program + +There are three pieces to enable backtrace in another ArceOS or StarryOS +program: dependency/features, a raw-block print point, and build-time flags. + +For an ArceOS Rust app that wants to print an explicit raw block, add +`axbacktrace` and keep the usual `ax-std` feature path enabled: + +```toml +[features] +ax-std = ["dep:ax-std"] + +[dependencies] +ax-std = { workspace = true, optional = true, features = ["backtrace"] } +axbacktrace = { workspace = true } +``` + +Print a machine-readable raw block from the place you want to inspect: + +```rust +use axbacktrace::Backtrace; + +fn show_backtrace() { + println!("{}", Backtrace::capture().kind("raw")); +} +``` + +For a trap/exception path, use the architecture trap context helper when one is +available, or call `Backtrace::capture_trap(fp, ip, ra)` only when the caller +already has a trustworthy frame pointer, instruction pointer, and return +address. + +In the app's `build-.toml`, enable one of these environment flags: + +```toml +[env] +BACKTRACE = "y" +``` + +`BACKTRACE=y` tells axbuild to keep frame pointers, which is required for the +target-side frame-pointer unwind. If you also need function/file/line +symbolization from the host, use `DWARF=y`: + +```toml +[env] +DWARF = "y" +``` + +`DWARF=y` implies backtrace support, keeps frame pointers, and preserves debug +information in the ELF. ArceOS QEMU tests with `BACKTRACE=y` or `DWARF=y` +automatically run the host symbolizer after QEMU unless `--no-symbolize` is +passed. + +For StarryOS allocation tracking, enable the memtrack feature and backtrace +build flag in the StarryOS app config: + +```toml +env = { BACKTRACE = "y" } +features = [ + "starry-kernel/memtrack", +] +``` + +Then trigger `/dev/memtrack` from the guest shell. Demo 4 writes `start`, +`sample`, `sample_hard`, `symbolize`, and `end` to show allocation backtrace +capture and reporting. + +## Prerequisites + +Run commands from the repository root. + +Recommended environment: + +- Rust toolchain configured for this repository. +- QEMU for the architecture you run, such as `qemu-system-x86_64`, + `qemu-system-aarch64`, `qemu-system-riscv64`, or + `qemu-system-loongarch64`. +- `llvm-addr2line` or `addr2line` in `PATH`. +- For StarryOS rootfs manipulation, the tools documented by the TGOSKits + quick-start/container environment. + +The project container is the most reproducible setup: + +```bash +docker pull ghcr.io/rcore-os/tgoskits-container:latest +docker run -it --rm \ + -v "$(pwd)":/workspace \ + -w /workspace \ + ghcr.io/rcore-os/tgoskits-container:latest +``` + +If you are running on the host directly, first check: + +```bash +rustc --version +cargo --version +qemu-system-x86_64 --version +llvm-addr2line --version || addr2line --version +``` + +## Prepare StarryOS + +The ArceOS demos build self-contained QEMU test images. The StarryOS memtrack +demo uses a managed Alpine rootfs. `starry app qemu` can prepare it on demand, +but this command is useful when you want to download/patch the rootfs before +running Demo 4: + +```bash +bash apps/backtrace/run_demo.sh starry-rootfs-all +``` + +Optional smoke run: + +```bash +cargo xtask starry qemu --arch x86_64 +``` + +## Run All Demos + +The helper script wraps the canonical commands below and keeps the workflow easy +to reproduce in a PR discussion. Demo 4 additionally captures the StarryOS app +log and runs the host symbolizer for the captured allocation blocks. + +```bash +bash apps/backtrace/run_demo.sh all x86_64 +``` + +You can also run one demo at a time: + +```bash +bash apps/backtrace/run_demo.sh demo1 x86_64 +bash apps/backtrace/run_demo.sh demo2 x86_64 +bash apps/backtrace/run_demo.sh demo3 x86_64 +bash apps/backtrace/run_demo.sh demo4 x86_64 +``` + +Omit the architecture argument to use `x86_64`. + +## Demo 1: Raw Backtrace, No Host Symbolize + +```bash +cargo xtask arceos test qemu \ + --arch \ + --test-group rust \ + --test-case backtrace \ + --no-symbolize +``` + +Expected result: + +- QEMU prints a raw block with `BACKTRACE_BEGIN`. +- The block contains at least `BT 0` and `BT 1`. +- The test ends with `test pass`. +- No `=== host backtrace symbolize ===` section is expected because + `--no-symbolize` disables host symbolization. + +## Demo 2: Auto Host Symbolize + +```bash +cargo xtask arceos test qemu \ + --arch \ + --test-group rust \ + --test-case backtrace +``` + +Expected result: + +- The target still emits the raw backtrace block. +- The host prints `=== host backtrace symbolize ===` after QEMU. +- The symbolized block contains function names resolved from the same build's + ELF. + +## Demo 3: DWARF Auto Symbolize + +```bash +cargo xtask arceos test qemu \ + --arch \ + --test-group rust \ + --test-case backtrace-raw-normal +``` + +Expected result: + +- The target prints `emitting raw backtrace report (normal fp chain)...`. +- The raw block uses `kind=raw`. +- The host symbolizer runs automatically because the build config enables + `DWARF=y`. +- The symbolized output should include the synthetic call chain from the test + program, such as `c`, `b`, and `a`, when debug information is available. + +## Demo 4: StarryOS Memtrack Backtrace + +```bash +bash apps/backtrace/run_demo.sh demo4 +``` + +The helper script runs the StarryOS QEMU app, keeps the QEMU transcript in +`/tmp`, then host-symbolizes allocation raw blocks against +`target//release/starryos`. + +The underlying app command is: + +```bash +cargo xtask starry app qemu \ + -t qemu/memtrack-backtrace \ + --arch \ + --qemu-config qemu-.toml +``` + +Expected result: + +- StarryOS boots with `starry-kernel/memtrack` enabled. +- The QEMU shell command sequence writes to `/dev/memtrack`: + `start`, `sample`, `sample_hard`, `symbolize`, and `end`. +- The guest prints: + - `Memory allocation sample recorded` + - `Hard memory allocation sample recorded` + - at least one `BACKTRACE_BEGIN kind=alloc` raw block + - `STARRY_MEMTRACK_BACKTRACE_OK` +- The helper prints `=== host backtrace symbolize ===` after QEMU. +- The symbolized output contains `BACKTRACE_BLOCK ... kind=alloc`; when symbols + are available, it includes frames such as `starry_memtrack_symbolize_probe`. + +Demo 4 uses `--adjust-ip false` when host-symbolizing StarryOS memtrack +allocation blocks. The default host symbolizer adjustment is useful for normal +frame-pointer backtraces whose IPs are return addresses. Memtrack allocation +blocks record allocation/probe sample addresses that should be symbolized as +recorded; on aarch64, subtracting the default 4-byte call-site adjustment can +move the probe frame away from `starry_memtrack_symbolize_probe`. + +The raw `cargo xtask starry app qemu` command validates the guest markers. Use +the helper, or the manual command below, when you also want host symbolization +for the same allocation blocks. + +## Manual Host Symbolize + +If you keep or capture a QEMU log manually, you can symbolize it afterwards: + +```bash +cargo xtask backtrace symbolize \ + --elf target//release/arceos-backtrace-raw-normal \ + --log /tmp/arceos-backtrace.log \ + --kind raw +``` + +For the StarryOS memtrack app, capture the app output and symbolize `kind=alloc` +blocks against the StarryOS ELF. Keep `--adjust-ip false` for memtrack logs so +allocation/probe sample IPs are resolved at the recorded addresses: + +```bash +cargo xtask starry app qemu \ + -t qemu/memtrack-backtrace \ + --arch \ + --qemu-config qemu-.toml \ + 2>&1 | tee /tmp/starry-memtrack-backtrace.log + +cargo xtask backtrace symbolize \ + --elf target//release/starryos \ + --log /tmp/starry-memtrack-backtrace.log \ + --kind alloc \ + --adjust-ip false +``` + +For automatic QEMU log retention, set: + +```bash +TGOSKITS_KEEP_QEMU_LOG=1 cargo xtask arceos test qemu \ + --arch x86_64 \ + --test-group rust \ + --test-case backtrace-raw-normal +``` + +## Troubleshooting + +- If no host-symbolized section appears, confirm the build config enables + `BACKTRACE=y` or `DWARF=y`, and that `llvm-addr2line` or `addr2line` is in + `PATH`. +- If Demo 4 does not symbolize, check the raw log path printed by the helper and + confirm `target//release/starryos` exists for the selected + architecture. +- If the StarryOS demo fails before booting the shell, regenerate the rootfs: + `bash apps/backtrace/run_demo.sh starry-rootfs `. +- If frame chains are shallow, confirm the build keeps frame pointers. These + demos rely on the build flags inserted by TGOSKits when backtrace support is + enabled. +- If a StarryOS app appears to pause after build at `starry-kallsyms.sh`, wait + for the existing kallsyms padding step to finish; it can take a few minutes + on large release ELFs before QEMU starts. +- If behavior differs across machines, rerun inside + `ghcr.io/rcore-os/tgoskits-container:latest`. diff --git a/apps/backtrace/run_demo.sh b/apps/backtrace/run_demo.sh new file mode 100755 index 0000000000..603743a7ea --- /dev/null +++ b/apps/backtrace/run_demo.sh @@ -0,0 +1,237 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "${ROOT_DIR}" + +readonly DEFAULT_ARCH="x86_64" +readonly ARCHES=("x86_64" "aarch64" "riscv64" "loongarch64") + +usage() { + cat <<'USAGE' +Usage: + bash apps/backtrace/run_demo.sh demo1 [arch] + bash apps/backtrace/run_demo.sh demo2 [arch] + bash apps/backtrace/run_demo.sh demo3 [arch] + bash apps/backtrace/run_demo.sh demo4 [arch] + bash apps/backtrace/run_demo.sh demo1-all + bash apps/backtrace/run_demo.sh demo2-all + bash apps/backtrace/run_demo.sh demo3-all + bash apps/backtrace/run_demo.sh demo4-all + bash apps/backtrace/run_demo.sh starry-rootfs [arch] + bash apps/backtrace/run_demo.sh starry-rootfs-all + bash apps/backtrace/run_demo.sh all [arch] + bash apps/backtrace/run_demo.sh all-arch + +Supported arch values: + x86_64, aarch64, riscv64, loongarch64 + +Demos: + demo1 ArceOS raw backtrace without host symbolize. + demo2 ArceOS raw backtrace with automatic host symbolize. + demo3 ArceOS DWARF-enabled raw backtrace with automatic host symbolize. + demo4 StarryOS /dev/memtrack allocation backtrace with host symbolize. + starry-rootfs Prepare the StarryOS rootfs used by demo4. + all Prepare StarryOS rootfs, then run demo1 through demo4 for one arch. + all-arch Run the full workflow for all supported arch values. +USAGE +} + +require_supported_arch() { + local arch="${1}" + + for supported in "${ARCHES[@]}"; do + if [[ "${arch}" == "${supported}" ]]; then + return 0 + fi + done + + echo "unsupported arch: ${arch}" >&2 + echo "supported arch values: ${ARCHES[*]}" >&2 + exit 2 +} + +target_triple_for_arch() { + case "${1}" in + x86_64) + echo "x86_64-unknown-none" + ;; + aarch64) + echo "aarch64-unknown-none-softfloat" + ;; + riscv64) + echo "riscv64gc-unknown-none-elf" + ;; + loongarch64) + echo "loongarch64-unknown-none-softfloat" + ;; + *) + echo "unsupported arch: ${1}" >&2 + exit 2 + ;; + esac +} + +run_for_all_arches() { + local fn="${1}" + + for arch in "${ARCHES[@]}"; do + printf '\n==> %s (%s)\n' "${fn}" "${arch}" + "${fn}" "${arch}" + done +} + +run_starry_rootfs() { + local arch="${1:-${DEFAULT_ARCH}}" + require_supported_arch "${arch}" + + cargo xtask starry rootfs --arch "${arch}" +} + +run_demo1() { + local arch="${1:-${DEFAULT_ARCH}}" + require_supported_arch "${arch}" + + cargo xtask arceos test qemu \ + --arch "${arch}" \ + --test-group rust \ + --test-case backtrace \ + --no-symbolize +} + +run_demo2() { + local arch="${1:-${DEFAULT_ARCH}}" + require_supported_arch "${arch}" + + cargo xtask arceos test qemu \ + --arch "${arch}" \ + --test-group rust \ + --test-case backtrace +} + +run_demo3() { + local arch="${1:-${DEFAULT_ARCH}}" + require_supported_arch "${arch}" + + cargo xtask arceos test qemu \ + --arch "${arch}" \ + --test-group rust \ + --test-case backtrace-raw-normal +} + +run_demo4() { + local arch="${1:-${DEFAULT_ARCH}}" + require_supported_arch "${arch}" + + local target + target="$(target_triple_for_arch "${arch}")" + + local log + log="$(mktemp "${TMPDIR:-/tmp}/tgoskits-starry-memtrack-${arch}.XXXXXX.log")" + + set +e + cargo xtask starry app qemu \ + -t qemu/memtrack-backtrace \ + --arch "${arch}" \ + --qemu-config "qemu-${arch}.toml" \ + 2>&1 | tee "${log}" + local status="${PIPESTATUS[0]}" + set -e + + if [[ "${status}" -ne 0 ]]; then + echo "demo4 qemu failed; raw log kept at ${log}" >&2 + return "${status}" + fi + + if grep -q '^=== host backtrace symbolize ===$' "${log}"; then + echo "demo4 host symbolize already emitted by runner; raw log kept at ${log}" >&2 + return 0 + fi + + if ! grep -Eq '^BACKTRACE_BEGIN([[:space:]]|$)' "${log}"; then + echo "demo4 found no BACKTRACE_BEGIN blocks; raw log kept at ${log}" >&2 + return 1 + fi + + local symbolized + symbolized="$(mktemp "${TMPDIR:-/tmp}/tgoskits-starry-memtrack-${arch}.symbolized.XXXXXX.log")" + + if ! cargo xtask backtrace symbolize \ + --elf "target/${target}/release/starryos" \ + --log "${log}" \ + --kind alloc \ + --adjust-ip false > "${symbolized}"; then + echo "demo4 host symbolize failed; raw log kept at ${log}" >&2 + echo "demo4 partial symbolized output kept at ${symbolized}" >&2 + return 1 + fi + + if ! grep -Eq '^BACKTRACE_BLOCK[[:space:]].*kind=alloc([[:space:]]|$)' "${symbolized}"; then + echo "demo4 host symbolize produced no alloc blocks; raw log kept at ${log}" >&2 + echo "demo4 symbolized output kept at ${symbolized}" >&2 + return 1 + fi + + printf '\n=== host backtrace symbolize ===\n' + cat "${symbolized}" + rm -f "${symbolized}" + echo "demo4 raw log: ${log}" >&2 +} + +run_all_one_arch() { + local arch="${1:-${DEFAULT_ARCH}}" + require_supported_arch "${arch}" + + run_starry_rootfs "${arch}" + run_demo1 "${arch}" + run_demo2 "${arch}" + run_demo3 "${arch}" + run_demo4 "${arch}" +} + +case "${1:-}" in + starry-rootfs) + run_starry_rootfs "${2:-${DEFAULT_ARCH}}" + ;; + starry-rootfs-all) + run_for_all_arches run_starry_rootfs + ;; + demo1) + run_demo1 "${2:-${DEFAULT_ARCH}}" + ;; + demo1-all) + run_for_all_arches run_demo1 + ;; + demo2) + run_demo2 "${2:-${DEFAULT_ARCH}}" + ;; + demo2-all) + run_for_all_arches run_demo2 + ;; + demo3) + run_demo3 "${2:-${DEFAULT_ARCH}}" + ;; + demo3-all) + run_for_all_arches run_demo3 + ;; + demo4) + run_demo4 "${2:-${DEFAULT_ARCH}}" + ;; + demo4-all) + run_for_all_arches run_demo4 + ;; + all) + run_all_one_arch "${2:-${DEFAULT_ARCH}}" + ;; + all-arch) + run_for_all_arches run_all_one_arch + ;; + -h|--help|help|"") + usage + ;; + *) + echo "unknown demo: $1" >&2 + usage >&2 + exit 2 + ;; +esac diff --git a/apps/starry/qemu/memtrack-backtrace/build-aarch64-unknown-none-softfloat.toml b/apps/starry/qemu/memtrack-backtrace/build-aarch64-unknown-none-softfloat.toml new file mode 100644 index 0000000000..78cee2dcff --- /dev/null +++ b/apps/starry/qemu/memtrack-backtrace/build-aarch64-unknown-none-softfloat.toml @@ -0,0 +1,16 @@ +env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2", BACKTRACE = "y" } +features = [ + "ax-feat/display", + "ax-feat/rtc", + "ax-driver/virtio-blk", + "ax-driver/virtio-net", + "ax-driver/virtio-gpu", + "ax-driver/virtio-input", + "ax-driver/virtio-socket", + "starry-kernel/input", + "starry-kernel/vsock", + "starry-kernel/memtrack", +] +log = "Warn" +plat_dyn = true +target = "aarch64-unknown-none-softfloat" diff --git a/apps/starry/qemu/memtrack-backtrace/build-loongarch64-unknown-none-softfloat.toml b/apps/starry/qemu/memtrack-backtrace/build-loongarch64-unknown-none-softfloat.toml new file mode 100644 index 0000000000..491b54f0ec --- /dev/null +++ b/apps/starry/qemu/memtrack-backtrace/build-loongarch64-unknown-none-softfloat.toml @@ -0,0 +1,15 @@ +target = "loongarch64-unknown-none-softfloat" +env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2", BACKTRACE = "y" } +log = "Warn" +features = [ + "ax-hal/loongarch64-qemu-virt", + "qemu", + "ax-driver/plat-static", + "ax-driver/virtio-blk", + "ax-driver/virtio-net", + "ax-driver/virtio-gpu", + "ax-driver/virtio-input", + "ax-driver/virtio-socket", + "starry-kernel/memtrack", +] +plat_dyn = false diff --git a/apps/starry/qemu/memtrack-backtrace/build-riscv64gc-unknown-none-elf.toml b/apps/starry/qemu/memtrack-backtrace/build-riscv64gc-unknown-none-elf.toml new file mode 100644 index 0000000000..76b131dcd9 --- /dev/null +++ b/apps/starry/qemu/memtrack-backtrace/build-riscv64gc-unknown-none-elf.toml @@ -0,0 +1,15 @@ +env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2", BACKTRACE = "y" } +features = [ + "ax-feat/rtc", + "ax-driver/serial", + "ax-driver/virtio-blk", + "ax-driver/virtio-net", + "ax-driver/virtio-gpu", + "ax-driver/virtio-input", + "ax-driver/virtio-socket", + "starry-kernel/input", + "starry-kernel/memtrack", +] +log = "Warn" +plat_dyn = true +target = "riscv64gc-unknown-none-elf" diff --git a/apps/starry/qemu/memtrack-backtrace/qemu-aarch64.toml b/apps/starry/qemu/memtrack-backtrace/qemu-aarch64.toml new file mode 100644 index 0000000000..32c87e1a9b --- /dev/null +++ b/apps/starry/qemu/memtrack-backtrace/qemu-aarch64.toml @@ -0,0 +1,30 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "cortex-a53", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:/root #" +shell_init_cmd = "printf 'start\\n' > /dev/memtrack && printf 'sample\\n' > /dev/memtrack && printf 'sample_hard\\n' > /dev/memtrack && printf 'symbolize\\n' > /dev/memtrack && printf 'end\\n' > /dev/memtrack && echo STARRY_MEMTRACK_BACKTRACE_OK" +success_regex = [ + "(?m)^Memory allocation sample recorded\\s*$", + "(?m)^Hard memory allocation sample recorded\\s*$", + "(?ms)^BACKTRACE_BEGIN\\b.*\\bkind=alloc\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b", + "(?m)^STARRY_MEMTRACK_BACKTRACE_OK\\s*$", +] +host_symbolize_success_regex = [ + "(?ms)^=== host backtrace symbolize ===$.*^BACKTRACE_BLOCK\\s+\\d+\\s+kind=alloc\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x0\\s+starry_memtrack_symbolize_probe\\b", +] +fail_regex = ['(?i)\bpanic(?:ked)?\b'] +timeout = 180 diff --git a/apps/starry/qemu/memtrack-backtrace/qemu-loongarch64.toml b/apps/starry/qemu/memtrack-backtrace/qemu-loongarch64.toml new file mode 100644 index 0000000000..0ea20cbc6c --- /dev/null +++ b/apps/starry/qemu/memtrack-backtrace/qemu-loongarch64.toml @@ -0,0 +1,32 @@ +args = [ + "-machine", + "virt", + "-cpu", + "la464", + "-nographic", + "-m", + "128M", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-loongarch64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:/root #" +shell_init_cmd = "printf 'start\\n' > /dev/memtrack && printf 'sample\\n' > /dev/memtrack && printf 'sample_hard\\n' > /dev/memtrack && printf 'symbolize\\n' > /dev/memtrack && printf 'end\\n' > /dev/memtrack && echo STARRY_MEMTRACK_BACKTRACE_OK" +success_regex = [ + "(?m)^Memory allocation sample recorded\\s*$", + "(?m)^Hard memory allocation sample recorded\\s*$", + "(?ms)^BACKTRACE_BEGIN\\b.*\\bkind=alloc\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b", + "(?m)^STARRY_MEMTRACK_BACKTRACE_OK\\s*$", +] +host_symbolize_success_regex = [ + "(?ms)^=== host backtrace symbolize ===$.*^BACKTRACE_BLOCK\\s+\\d+\\s+kind=alloc\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x0\\s+starry_memtrack_symbolize_probe\\b", +] +fail_regex = ['(?i)\bpanic(?:ked)?\b'] +timeout = 180 diff --git a/apps/starry/qemu/memtrack-backtrace/qemu-riscv64.toml b/apps/starry/qemu/memtrack-backtrace/qemu-riscv64.toml new file mode 100644 index 0000000000..1417fc1d38 --- /dev/null +++ b/apps/starry/qemu/memtrack-backtrace/qemu-riscv64.toml @@ -0,0 +1,30 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:/root #" +shell_init_cmd = "printf 'start\\n' > /dev/memtrack && printf 'sample\\n' > /dev/memtrack && printf 'sample_hard\\n' > /dev/memtrack && printf 'symbolize\\n' > /dev/memtrack && printf 'end\\n' > /dev/memtrack && echo STARRY_MEMTRACK_BACKTRACE_OK" +success_regex = [ + "(?m)^Memory allocation sample recorded\\s*$", + "(?m)^Hard memory allocation sample recorded\\s*$", + "(?ms)^BACKTRACE_BEGIN\\b.*\\bkind=alloc\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b", + "(?m)^STARRY_MEMTRACK_BACKTRACE_OK\\s*$", +] +host_symbolize_success_regex = [ + "(?ms)^=== host backtrace symbolize ===$.*^BACKTRACE_BLOCK\\s+\\d+\\s+kind=alloc\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x0\\s+starry_memtrack_symbolize_probe\\b", +] +fail_regex = ['(?i)\bpanic(?:ked)?\b'] +timeout = 180 diff --git a/apps/starry/qemu/memtrack-backtrace/qemu-x86_64.toml b/apps/starry/qemu/memtrack-backtrace/qemu-x86_64.toml index de63cb1314..3c47cef308 100644 --- a/apps/starry/qemu/memtrack-backtrace/qemu-x86_64.toml +++ b/apps/starry/qemu/memtrack-backtrace/qemu-x86_64.toml @@ -13,7 +13,7 @@ args = [ ] uefi = false to_bin = false -shell_prefix = "root@starry:" +shell_prefix = "root@starry:/root #" shell_init_cmd = "printf 'start\\n' > /dev/memtrack && printf 'sample\\n' > /dev/memtrack && printf 'sample_hard\\n' > /dev/memtrack && printf 'symbolize\\n' > /dev/memtrack && printf 'end\\n' > /dev/memtrack && echo STARRY_MEMTRACK_BACKTRACE_OK" success_regex = [ "(?m)^Memory allocation sample recorded\\s*$", diff --git a/components/axbacktrace/src/lib.rs b/components/axbacktrace/src/lib.rs index 38222bb6da..a66dd4d60f 100644 --- a/components/axbacktrace/src/lib.rs +++ b/components/axbacktrace/src/lib.rs @@ -182,10 +182,17 @@ fn unwind_core(mut fp: usize, mut callback: impl FnMut(Frame) -> bool) { // IP does not necessarily mean the FP chain is broken. // Skipped frames still count against the depth budget to prevent // infinite loops on corrupted FP chains with bad IPs. + let next_fp = frame.fp; + // Check FP progress before IP filtering: a bad IP can be skipped, but + // a non-advancing FP would otherwise keep revisiting the same frame. + if next_fp != 0 && next_fp <= fp { + break; + } + if let Some(ip_range) = ip_range && !ip_range.contains(&frame.ip) { - fp = frame.fp; + fp = next_fp; depth += 1; continue; } @@ -195,12 +202,16 @@ fn unwind_core(mut fp: usize, mut callback: impl FnMut(Frame) -> bool) { } if let Some(large_stack_end) = fp.checked_add(8 * 1024 * 1024) - && frame.fp >= large_stack_end + && next_fp >= large_stack_end { break; } - fp = frame.fp; + if next_fp == 0 { + break; + } + + fp = next_fp; depth += 1; } } @@ -536,6 +547,18 @@ mod tests { assert_eq!(count, 3); } + #[test] + fn unwind_stack_stops_on_non_advancing_frame_pointer() { + init_for_tests(); + let mut frames = [Frame { fp: 0, ip: 0x1111 }, Frame { fp: 0, ip: 0x2222 }]; + let base = frames.as_mut_ptr(); + frames[0].fp = unsafe { base.add(1) as usize }; + frames[1].fp = base as usize; + + let out = unwind_stack(base as usize); + assert_eq!(out, [frames[0]]); + } + #[test] fn frame_read_rejects_null_and_misaligned() { assert!(Frame::read(0).is_none()); diff --git a/os/StarryOS/kernel/src/kprobe.rs b/os/StarryOS/kernel/src/kprobe.rs index 94e0f83000..dd4fd5484a 100644 --- a/os/StarryOS/kernel/src/kprobe.rs +++ b/os/StarryOS/kernel/src/kprobe.rs @@ -462,24 +462,29 @@ pub fn handle_breakpoint(tf: &mut ax_runtime::hal::cpu::TrapFrame) -> bool { false } -#[allow(dead_code)] +#[cfg(not(target_arch = "loongarch64"))] #[inline(never)] fn kprobe_selftest_target() -> i32 { 42 } +#[cfg(not(target_arch = "loongarch64"))] static SELFTEST_HIT: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); +#[cfg(not(target_arch = "loongarch64"))] static SELFTEST_RET_HIT: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); +#[cfg(not(target_arch = "loongarch64"))] fn selftest_pre_handler(_data: &dyn kprobe::ProbeData, _pt: &mut kprobe::PtRegs) { SELFTEST_HIT.store(true, core::sync::atomic::Ordering::SeqCst); } +#[cfg(not(target_arch = "loongarch64"))] fn selftest_ret_handler(_data: &dyn kprobe::ProbeData, _pt: &mut kprobe::PtRegs) { SELFTEST_RET_HIT.store(true, core::sync::atomic::Ordering::SeqCst); } +#[cfg(not(target_arch = "loongarch64"))] pub fn run_selftest() -> bool { SELFTEST_HIT.store(false, core::sync::atomic::Ordering::SeqCst); SELFTEST_RET_HIT.store(false, core::sync::atomic::Ordering::SeqCst); diff --git a/os/arceos/modules/axruntime/src/lib.rs b/os/arceos/modules/axruntime/src/lib.rs index 3d8e562a9f..2f318d72a9 100644 --- a/os/arceos/modules/axruntime/src/lib.rs +++ b/os/arceos/modules/axruntime/src/lib.rs @@ -202,29 +202,30 @@ pub fn rust_main(cpu_id: usize, arg: usize) -> ! { init_allocator(); + let (kernel_space_start, kernel_space_size) = ax_hal::mem::kernel_aspace(); + { use core::ops::Range; unsafe extern "C" { safe static _stext: [u8; 0]; safe static _etext: [u8; 0]; - safe static _edata: [u8; 0]; } + let fp_range_start = kernel_space_start.as_usize(); + let fp_range_end = fp_range_start.saturating_add(kernel_space_size); axbacktrace::init( Range { start: _stext.as_ptr() as usize, end: _etext.as_ptr() as usize, }, Range { - start: _edata.as_ptr() as usize, - end: usize::MAX, + start: fp_range_start, + end: fp_range_end, }, ); } - let (kernel_space_start, kernel_space_size) = ax_hal::mem::kernel_aspace(); - info!( "kernel aspace: [{:#x?}, {:#x?})", kernel_space_start, diff --git a/scripts/axbuild/src/backtrace.rs b/scripts/axbuild/src/backtrace.rs index f7c2c23bc5..1e048d928c 100644 --- a/scripts/axbuild/src/backtrace.rs +++ b/scripts/axbuild/src/backtrace.rs @@ -11,6 +11,7 @@ use std::{ use anyhow::{Context, bail}; use clap::{Args, Subcommand}; +use object::{Object, ObjectSymbol, SymbolKind}; use regex::Regex; #[derive(Subcommand)] @@ -183,7 +184,7 @@ pub(crate) struct BacktraceSymbolizeSession { header_printed: AtomicBool, symbolized: AtomicBool, failed: AtomicBool, - loader: OnceLock>, + symbolizer: OnceLock>, } impl BacktraceSymbolizeSession { @@ -205,8 +206,8 @@ impl BacktraceSymbolizeSession { ); return None; } - let loader = match addr2line::Loader::new(elf) { - Ok(loader) => Some(loader), + let symbolizer = match HostSymbolizer::new(elf) { + Ok(symbolizer) => Some(symbolizer), Err(err) => { eprintln!( "warning: failed to load symbols from {} for stream backtrace symbolize: {err}", @@ -216,23 +217,23 @@ impl BacktraceSymbolizeSession { } }; let once = OnceLock::new(); - once.set(loader).ok(); // always succeeds (first set) + once.set(symbolizer).ok(); // always succeeds (first set) Some(Arc::new(Self { elf: elf.to_path_buf(), case_name: case_name.to_string(), header_printed: AtomicBool::new(false), symbolized: AtomicBool::new(false), failed: AtomicBool::new(false), - loader: once, + symbolizer: once, })) } - /// Get or lazily initialize the cached Loader. - fn loader(&self) -> Option<&addr2line::Loader> { + /// Get or lazily initialize the cached symbolizer. + fn symbolizer(&self) -> Option<&HostSymbolizer> { let opt = self - .loader - .get_or_init(|| match addr2line::Loader::new(&self.elf) { - Ok(loader) => Some(loader), + .symbolizer + .get_or_init(|| match HostSymbolizer::new(&self.elf) { + Ok(symbolizer) => Some(symbolizer), Err(err) => { eprintln!( "warning: failed to load symbols from {} for stream backtrace symbolize: \ @@ -271,7 +272,7 @@ impl BacktraceSymbolizeSession { } }; - let Some(loader) = self.loader() else { + let Some(symbolizer) = self.symbolizer() else { self.failed.store(true, Ordering::SeqCst); return; }; @@ -284,7 +285,7 @@ impl BacktraceSymbolizeSession { let mut stdout = std::io::stdout().lock(); if let Err(err) = write_symbolized_blocks( &mut stdout, - loader, + symbolizer, &blocks, kind_filter.as_deref(), true, @@ -331,13 +332,20 @@ pub(crate) fn symbolize_text_to_string( return Ok(None); } - let loader = addr2line::Loader::new(elf_path).map_err(|err| { + let symbolizer = HostSymbolizer::new(elf_path).map_err(|err| { anyhow::anyhow!("failed to load symbols from {}: {err}", elf_path.display()) })?; let mut out = Vec::new(); writeln!(&mut out, "{HOST_SYMBOLIZE_HEADER}")?; let kind_filter = infer_kind_filter(case_name, &blocks); - write_symbolized_blocks(&mut out, &loader, &blocks, kind_filter.as_deref(), true, 0)?; + write_symbolized_blocks( + &mut out, + &symbolizer, + &blocks, + kind_filter.as_deref(), + true, + 0, + )?; Ok(Some(String::from_utf8(out)?)) } @@ -451,8 +459,8 @@ pub(crate) fn maybe_symbolize_after_qemu( }; let kind_filter = infer_kind_filter(case_name, &blocks); - let loader = match addr2line::Loader::new(elf) { - Ok(loader) => loader, + let symbolizer = match HostSymbolizer::new(elf) { + Ok(symbolizer) => symbolizer, Err(err) => { eprintln!( "warning: failed to load symbols from {} for backtrace symbolize: {err}", @@ -468,7 +476,7 @@ pub(crate) fn maybe_symbolize_after_qemu( let mut stdout = std::io::stdout().lock(); if let Err(err) = write_symbolized_blocks( &mut stdout, - &loader, + &symbolizer, &blocks, kind_filter.as_deref(), true, @@ -780,7 +788,7 @@ fn symbolize_cli(args: SymbolizeArgs) -> anyhow::Result<()> { bail!("no backtrace blocks found"); } - let loader = addr2line::Loader::new(&args.elf).map_err(|err| { + let symbolizer = HostSymbolizer::new(&args.elf).map_err(|err| { anyhow::anyhow!( "failed to load dwarf/symbols from {}: {}", args.elf.display(), @@ -790,7 +798,7 @@ fn symbolize_cli(args: SymbolizeArgs) -> anyhow::Result<()> { write_symbolized_blocks( &mut std::io::stdout().lock(), - &loader, + &symbolizer, &blocks, args.kind.as_deref(), args.adjust_ip, @@ -812,7 +820,7 @@ fn ip_adjustment_for_arch(arch: Option<&str>) -> u64 { fn write_symbolized_blocks( out: &mut impl Write, - loader: &addr2line::Loader, + symbolizer: &HostSymbolizer, blocks: &[Block], kind_filter: Option<&str>, adjust_ip: bool, @@ -841,7 +849,7 @@ fn write_symbolized_blocks( frame.ip }; let ip = ip.wrapping_add_signed(ip_bias); - let symbolized = maybe_symbolize_with_loader(loader, ip); + let symbolized = symbolizer.maybe_symbolize(ip); match (&frame.fp, symbolized) { (Some(fp), Some(sym)) => { @@ -871,42 +879,124 @@ fn write_symbolized_blocks( Ok(()) } -fn maybe_symbolize_with_loader(loader: &addr2line::Loader, ip: u64) -> Option { - if ip == 0 { - return None; - } - symbolize_with_loader(loader, ip) +#[derive(Debug, Clone)] +struct TextSymbol { + address: u64, + size: u64, + name: String, } -fn symbolize_with_loader(loader: &addr2line::Loader, ip: u64) -> Option { - let mut frames = loader.find_frames(ip).ok()?; - let mut out = Vec::new(); - while let Some(frame) = frames.next().ok()? { - let name = frame - .function - .as_ref() - .and_then(|f| f.raw_name().ok()) - .map(|s| rustc_demangle::demangle(s.as_ref()).to_string()); - let loc = frame.location.as_ref().and_then(|l| { - let file = l.file?; - let line = l.line?; - Some(format!("{file}:{line}")) - }); - match (name, loc) { - (Some(name), Some(loc)) => out.push(format!("{name} ({loc})")), - (Some(name), None) => out.push(name), - (None, Some(loc)) => out.push(loc), - (None, None) => {} +struct HostSymbolizer { + loader: addr2line::Loader, + text_symbols: Vec, +} + +impl HostSymbolizer { + fn new(elf: &Path) -> anyhow::Result { + let loader = addr2line::Loader::new(elf).map_err(|err| anyhow::anyhow!("{err}"))?; + let text_symbols = load_text_symbols(elf)?; + Ok(Self { + loader, + text_symbols, + }) + } + + fn maybe_symbolize(&self, ip: u64) -> Option { + if ip == 0 { + return None; } + self.symbolize(ip) } - if out.is_empty() { - let sym = loader - .find_symbol(ip) - .map(|s| rustc_demangle::demangle(s).to_string()); - return sym; + + fn symbolize(&self, ip: u64) -> Option { + let mut frames = self.loader.find_frames(ip).ok()?; + let mut out = Vec::new(); + while let Some(frame) = frames.next().ok()? { + let name = frame.function.as_ref().and_then(|f| { + let raw = f.raw_name().ok()?; + self.display_symbol_name(raw.as_ref(), ip) + }); + let loc = frame.location.as_ref().and_then(|l| { + let file = l.file?; + let line = l.line?; + Some(format!("{file}:{line}")) + }); + match (name, loc) { + (Some(name), Some(loc)) => out.push(format!("{name} ({loc})")), + (Some(name), None) => out.push(name), + (None, Some(loc)) => out.push(loc), + (None, None) => {} + } + } + if out.is_empty() { + let sym = self + .loader + .find_symbol(ip) + .and_then(|name| self.display_symbol_name(name, ip)) + .or_else(|| self.nearest_text_symbol(ip)); + return sym; + } + + Some(out.join(" ; ")) } - Some(out.join(" ; ")) + fn display_symbol_name(&self, raw: &str, ip: u64) -> Option { + if is_compiler_local_symbol(raw) { + self.nearest_text_symbol(ip) + } else { + Some(rustc_demangle::demangle(raw).to_string()) + } + } + + fn nearest_text_symbol(&self, ip: u64) -> Option { + let idx = self.text_symbols.partition_point(|sym| sym.address <= ip); + for sym in self.text_symbols[..idx].iter().rev() { + if sym.size == 0 || ip < sym.address.saturating_add(sym.size) { + return Some(rustc_demangle::demangle(&sym.name).to_string()); + } + } + None + } +} + +fn load_text_symbols(elf: &Path) -> anyhow::Result> { + let bytes = fs::read(elf)?; + let file = object::File::parse(bytes.as_slice())?; + let mut symbols = Vec::new(); + + for sym in file.symbols() { + if sym.kind() != SymbolKind::Text || sym.address() == 0 { + continue; + } + let Ok(name) = sym.name() else { + continue; + }; + if is_compiler_local_symbol(name) { + continue; + } + symbols.push(TextSymbol { + address: sym.address(), + size: sym.size(), + name: name.to_string(), + }); + } + + symbols.sort_by(|a, b| { + a.address + .cmp(&b.address) + .then_with(|| a.name.len().cmp(&b.name.len())) + .then_with(|| a.name.cmp(&b.name)) + }); + symbols.dedup_by(|a, b| a.address == b.address && a.name == b.name); + Ok(symbols) +} + +fn is_compiler_local_symbol(name: &str) -> bool { + let name = name.trim(); + // Covered target-local labels include LLVM/GNU `.L*` labels observed on + // x86_64/riscv64/loongarch64 and ARM/AArch64 mapping symbols such as + // `$x`/`$d`; all are rendering noise for host backtrace output. + name.starts_with(".L") || name.starts_with('$') } #[cfg(test)] @@ -1066,8 +1156,8 @@ BACKTRACE_END let bias = file_ip as i64 - runtime_ip as i64; let ip_for_file = runtime_ip.wrapping_add_signed(bias); - let loader = addr2line::Loader::new(&exe).unwrap(); - let sym = symbolize_with_loader(&loader, ip_for_file).unwrap(); + let symbolizer = HostSymbolizer::new(&exe).unwrap(); + let sym = symbolizer.symbolize(ip_for_file).unwrap(); assert!(sym.contains("bt_symbolize_probe")); } @@ -1123,8 +1213,50 @@ BACKTRACE_END #[test] fn symbolize_skips_zero_ip() { let exe = std::env::current_exe().unwrap(); - let loader = addr2line::Loader::new(&exe).unwrap(); - assert!(maybe_symbolize_with_loader(&loader, 0).is_none()); + let symbolizer = HostSymbolizer::new(&exe).unwrap(); + assert!(symbolizer.maybe_symbolize(0).is_none()); + } + + #[test] + fn compiler_local_symbols_are_not_display_names() { + assert!(is_compiler_local_symbol(".Lpcrel_hi31487")); + assert!(is_compiler_local_symbol(".L0")); + assert!(is_compiler_local_symbol(".Ltmp142")); + assert!(is_compiler_local_symbol("$x")); + assert!(is_compiler_local_symbol("$d")); + assert!(!is_compiler_local_symbol( + "starry_memtrack_sample_hard_leaf" + )); + assert!(!is_compiler_local_symbol( + "_RNvNtNtNtCs66o47AdWPbf_13starry_kernel8pseudofs3dev8memtrack29record_hard_sample_allocation" + )); + } + + #[test] + fn local_symbol_names_fall_back_to_nearest_text_symbol() { + let exe = std::env::current_exe().unwrap(); + let mut symbolizer = HostSymbolizer::new(&exe).unwrap(); + symbolizer.text_symbols = vec![ + TextSymbol { + address: 0x1000, + size: 0x40, + name: "starry_memtrack_sample_hard_mid".to_string(), + }, + TextSymbol { + address: 0x1040, + size: 0x80, + name: "starry_memtrack_sample_hard_leaf".to_string(), + }, + ]; + + assert_eq!( + symbolizer.display_symbol_name(".Lpcrel_hi31487", 0x1060), + Some("starry_memtrack_sample_hard_leaf".to_string()) + ); + assert_eq!( + symbolizer.display_symbol_name(".Lpcrel_hi31487", 0x2000), + None + ); } #[test] diff --git a/test-suit/arceos/rust/backtrace-raw-normal/build-aarch64-unknown-none-softfloat.toml b/test-suit/arceos/rust/backtrace-raw-normal/build-aarch64-unknown-none-softfloat.toml new file mode 100644 index 0000000000..ae9b3e9ce3 --- /dev/null +++ b/test-suit/arceos/rust/backtrace-raw-normal/build-aarch64-unknown-none-softfloat.toml @@ -0,0 +1,6 @@ +features = ["ax-std"] +log = "Warn" +max_cpu_num = 4 + +[env] +DWARF = "y" diff --git a/test-suit/arceos/rust/backtrace-raw-normal/build-loongarch64-unknown-none-softfloat.toml b/test-suit/arceos/rust/backtrace-raw-normal/build-loongarch64-unknown-none-softfloat.toml new file mode 100644 index 0000000000..ae9b3e9ce3 --- /dev/null +++ b/test-suit/arceos/rust/backtrace-raw-normal/build-loongarch64-unknown-none-softfloat.toml @@ -0,0 +1,6 @@ +features = ["ax-std"] +log = "Warn" +max_cpu_num = 4 + +[env] +DWARF = "y" diff --git a/test-suit/arceos/rust/backtrace-raw-normal/build-riscv64gc-unknown-none-elf.toml b/test-suit/arceos/rust/backtrace-raw-normal/build-riscv64gc-unknown-none-elf.toml new file mode 100644 index 0000000000..ae9b3e9ce3 --- /dev/null +++ b/test-suit/arceos/rust/backtrace-raw-normal/build-riscv64gc-unknown-none-elf.toml @@ -0,0 +1,6 @@ +features = ["ax-std"] +log = "Warn" +max_cpu_num = 4 + +[env] +DWARF = "y" diff --git a/test-suit/arceos/rust/backtrace-raw-normal/qemu-aarch64.toml b/test-suit/arceos/rust/backtrace-raw-normal/qemu-aarch64.toml new file mode 100644 index 0000000000..ef8382e3e4 --- /dev/null +++ b/test-suit/arceos/rust/backtrace-raw-normal/qemu-aarch64.toml @@ -0,0 +1,19 @@ +args = [ + "-machine", + "virt", + "-cpu", + "cortex-a72", + "-m", + "512M", + "-smp", + "4", + "-nographic", + "-serial", + "mon:stdio", +] +uefi = false +to_bin = true +success_regex = [ + "(?ms)^emitting raw backtrace report \\(normal fp chain\\)\\.\\.\\.$.*^BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BT\\s+1\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b.*^test pass\\s*$", +] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b"] diff --git a/test-suit/arceos/rust/backtrace-raw-normal/qemu-loongarch64.toml b/test-suit/arceos/rust/backtrace-raw-normal/qemu-loongarch64.toml new file mode 100644 index 0000000000..32338d1045 --- /dev/null +++ b/test-suit/arceos/rust/backtrace-raw-normal/qemu-loongarch64.toml @@ -0,0 +1,19 @@ +args = [ + "-machine", + "virt", + "-cpu", + "la464", + "-m", + "128M", + "-smp", + "4", + "-nographic", + "-serial", + "mon:stdio", +] +uefi = false +to_bin = true +success_regex = [ + "(?ms)^emitting raw backtrace report \\(normal fp chain\\)\\.\\.\\.$.*^BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BT\\s+1\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b.*^test pass\\s*$", +] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b"] diff --git a/test-suit/arceos/rust/backtrace-raw-normal/qemu-riscv64.toml b/test-suit/arceos/rust/backtrace-raw-normal/qemu-riscv64.toml new file mode 100644 index 0000000000..3441fd4034 --- /dev/null +++ b/test-suit/arceos/rust/backtrace-raw-normal/qemu-riscv64.toml @@ -0,0 +1,19 @@ +args = [ + "-machine", + "virt", + "-cpu", + "rv64", + "-m", + "512M", + "-smp", + "4", + "-nographic", + "-serial", + "mon:stdio", +] +uefi = false +to_bin = true +success_regex = [ + "(?ms)^emitting raw backtrace report \\(normal fp chain\\)\\.\\.\\.$.*^BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BT\\s+1\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b.*^test pass\\s*$", +] +fail_regex = ["(?i)\\bpanic(?:ked)?\\b"]