kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

addr2line.sh (7462B)


      1 #!/usr/bin/env bash
      2 # test/rt/addr2line.sh — L3a backtrace round-trip (addr2line + symbolize).
      3 #
      4 # Compiles test/rt/addr2line_prog.c (which prints its own backtrace via
      5 # __kit_print_backtrace), links a static non-PIE ELF, runs it to capture the
      6 # raw "#N 0xADDR" lines, then symbolizes those addresses two ways, asserting
      7 # the expected function names (bt_leaf / bt_mid / bt_root / test_main) appear:
      8 #
      9 #   * `kit addr2line -f`  — the GNU-style "addresses in, file:line out" lane,
     10 #     fed the bare addresses grepped from the stream.
     11 #   * `kit symbolize`     — fed the raw "#N 0xADDR" stream verbatim; it must
     12 #     keep the "#N" framing and append "<func> at file:line" in place (the
     13 #     "#0 0x401136 bt_leaf at addr2line_prog.c:51:3" shape).
     14 #
     15 # Together they prove the freestanding capture/print path emits addresses the
     16 # hosted DWARF tools resolve. See doc/plan/BACKTRACE.md (L3a / WS5).
     17 #
     18 # The per-arch wiring mirrors test/rt/run.sh's lane R: each arch maps to an
     19 # <arch>-linux exec tuple, the matching build/rt/<triple>/libkit_rt.a, a clang
     20 # freestanding start.o, a link via link-exe-runner, and an exec via
     21 # exec_target. Because the image is a static non-PIE ELF, the return addresses
     22 # printed at run time equal its link-time addresses, so they pipe straight to
     23 # addr2line. Anything missing (rt archive, exec runner, clang start.o) is a
     24 # SKIP, not a failure. Set KIT_TEST_ALLOW_SKIP=1 to exit 0 with skips.
     25 
     26 set -u
     27 
     28 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
     29 PROG_SRC="$ROOT/test/rt/addr2line_prog.c"
     30 BUILD_DIR="$ROOT/build/test/rt-addr2line"
     31 KIT="$ROOT/build/kit"
     32 LINK_EXE_RUNNER="$ROOT/build/test/link-exe-runner"
     33 START_SRC="$ROOT/test/link/harness/start.c"
     34 
     35 export KIT_KIT_DIR="$ROOT/test/lib"
     36 # shellcheck source=../lib/kit_sh_kit.sh
     37 . "$ROOT/test/lib/kit_sh_kit.sh"
     38 kit_report_init
     39 [ "${KIT_TEST_ALLOW_SKIP:-0}" = 1 ] || KIT_SKIP_IS_FAILURE=1
     40 
     41 mkdir -p "$BUILD_DIR"
     42 
     43 if [ ! -x "$KIT" ]; then
     44   skip_test "kit" "kit driver missing at $KIT -- run \`make bin\` first"
     45   kit_summary test-rt-addr2line
     46   kit_exit
     47 fi
     48 if [ ! -x "$LINK_EXE_RUNNER" ]; then
     49   skip_test "link-exe-runner" "missing at $LINK_EXE_RUNNER -- run \`make test-rt-runtime\`"
     50   kit_summary test-rt-addr2line
     51   kit_exit
     52 fi
     53 
     54 # exec_target wiring (same host-detection knobs test/rt/run.sh exports).
     55 have_qemu=0
     56 QEMU_BIN="$(command -v qemu-aarch64-static 2>/dev/null || command -v qemu-aarch64 2>/dev/null || true)"
     57 [ -n "$QEMU_BIN" ] && have_qemu=1
     58 have_podman=0
     59 command -v podman >/dev/null 2>&1 && have_podman=1
     60 arch_raw="$(uname -m 2>/dev/null || true)"
     61 is_aarch64=0
     62 if [ "$(uname -s 2>/dev/null)" = "Linux" ]; then
     63   { [ "$arch_raw" = "aarch64" ] || [ "$arch_raw" = "arm64" ]; } && is_aarch64=1
     64 fi
     65 export have_qemu QEMU_BIN have_podman is_aarch64
     66 EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR"
     67 export EXEC_TARGET_MOUNT_ROOT
     68 # shellcheck source=../lib/exec_target.sh
     69 . "$ROOT/test/lib/exec_target.sh"
     70 
     71 arch_triple() {
     72   case "$1" in
     73     aa64) echo "aarch64-linux-gnu" ;;
     74     x64)  echo "x86_64-linux-gnu" ;;
     75     rv64) echo "riscv64-linux-gnu" ;;
     76     *) return 1 ;;
     77   esac
     78 }
     79 rt_archive() {
     80   case "$1" in
     81     aa64) echo "$ROOT/build/rt/aarch64-linux/libkit_rt.a" ;;
     82     x64)  echo "$ROOT/build/rt/x86_64-linux/libkit_rt.a" ;;
     83     rv64) echo "$ROOT/build/rt/riscv64-linux/libkit_rt.a" ;;
     84     *) return 1 ;;
     85   esac
     86 }
     87 clang_extra_flags() {
     88   case "$1" in
     89     rv64) echo "-march=rv64gc" ;;
     90     *) echo "" ;;
     91   esac
     92 }
     93 
     94 # Functions the backtrace must symbolize to, innermost first. _start may show
     95 # as ?? (start.o is built without -g), so it is not required.
     96 WANT_FUNCS="bt_leaf bt_mid bt_root test_main"
     97 
     98 run_one() { # <arch> <opt>
     99   local arch="$1" opt="$2" name="$1/O$2 round-trip" sym_name="$1/O$2 symbolize"
    100   local triple rtlib extra work obj exe start_obj
    101   triple="$(arch_triple "$arch")" || { not_ok "$name" "unknown arch"; return; }
    102   rtlib="$(rt_archive "$arch")"
    103   extra="$(clang_extra_flags "$arch")"
    104   work="$BUILD_DIR/$arch/O$opt"
    105   mkdir -p "$work"
    106   obj="$work/prog.o"; exe="$work/prog.exe"; start_obj="$work/start.o"
    107 
    108   if [ ! -f "$rtlib" ]; then
    109     skip_test "$name" "runtime archive missing at $rtlib"; return
    110   fi
    111   if ! exec_target_supported "$arch"; then
    112     skip_test "$name" "no execution runner"; return
    113   fi
    114   if ! clang --target="$triple" $extra -O1 -ffreestanding -fno-stack-protector \
    115       -fno-PIC -fno-pie -c "$START_SRC" -o "$start_obj" \
    116       >"$work/start.out" 2>"$work/start.err"; then
    117     skip_test "$name" "clang cannot build start.o for $triple"; return
    118   fi
    119 
    120   # -g so addr2line has DWARF; static non-PIE so runtime addr == link addr.
    121   # The chain is @[.noinline] + non-tail, so the frames survive at O1 too.
    122   if ! "$KIT" cc -target "$triple" -g -O"$opt" -Werror -c "$PROG_SRC" -o "$obj" \
    123       >"$work/cc.out" 2>"$work/cc.err"; then
    124     not_ok "$name" "$work/cc.err"; return
    125   fi
    126   if ! KIT_TEST_ARCH="$arch" "$LINK_EXE_RUNNER" -o "$exe" "$obj" "$start_obj" \
    127       --archive "$rtlib" >"$work/link.out" 2>"$work/link.err"; then
    128     not_ok "$name" "$work/link.err"; return
    129   fi
    130 
    131   exec_target_run "$arch" "$exe" "$work/run.out" "$work/run.err"
    132   if [ "$RUN_RC" -ne 42 ]; then
    133     printf 'expected exit 42, got %s\n' "$RUN_RC" > "$work/run.diag"
    134     cat "$work/run.err" >> "$work/run.diag" 2>/dev/null
    135     not_ok "$name" "$work/run.diag"; return
    136   fi
    137 
    138   # --- Lane 1: kit addr2line -f over the bare captured addresses. ---
    139   local addrs
    140   addrs="$(grep -oE '0x[0-9a-fA-F]+' "$work/run.out" 2>/dev/null | tr '\n' ' ')"
    141   if [ -z "$addrs" ]; then
    142     printf 'no "#N 0xADDR" lines captured; run.out was:\n' > "$work/sym.diag"
    143     cat "$work/run.out" >> "$work/sym.diag" 2>/dev/null
    144     not_ok "$name" "$work/sym.diag"; return
    145   fi
    146   # shellcheck disable=SC2086
    147   "$KIT" addr2line -f -e "$exe" $addrs >"$work/sym.out" 2>"$work/sym.err"
    148 
    149   local fn missing=""
    150   for fn in $WANT_FUNCS; do
    151     grep -qw "$fn" "$work/sym.out" || missing="$missing $fn"
    152   done
    153   if [ -n "$missing" ]; then
    154     printf 'addr2line missing function(s):%s\naddresses: %s\nsymbolized:\n' \
    155       "$missing" "$addrs" > "$work/sym.diag"
    156     cat "$work/sym.out" >> "$work/sym.diag" 2>/dev/null
    157     not_ok "$name" "$work/sym.diag"
    158   else
    159     ok "$name"
    160   fi
    161 
    162   # --- Lane 2: kit symbolize annotates the raw "#N 0xADDR" stream in place. ---
    163   # The whole captured stream is piped in verbatim; symbolize must keep the
    164   # "#N 0x<hex>" framing and append "<func> at file:line[:col]" on each frame
    165   # line (lines without an address pass through untouched).
    166   "$KIT" symbolize -e "$exe" <"$work/run.out" >"$work/symz.out" 2>"$work/symz.err"
    167 
    168   local sym_missing=""
    169   for fn in $WANT_FUNCS; do
    170     # Require BOTH the preserved framing and the appended symbolization:
    171     #   #<n> 0x<hex> <fn> at <file>:<line>
    172     grep -qE "^#[0-9]+ 0x[0-9a-fA-F]+ ${fn} at .+:[0-9]+" "$work/symz.out" \
    173       || sym_missing="$sym_missing $fn"
    174   done
    175   if [ -n "$sym_missing" ]; then
    176     printf 'symbolize missing annotated frame(s):%s\nstream:\n' \
    177       "$sym_missing" > "$work/symz.diag"
    178     cat "$work/run.out" >> "$work/symz.diag" 2>/dev/null
    179     printf '\nsymbolized:\n' >> "$work/symz.diag"
    180     cat "$work/symz.out" >> "$work/symz.diag" 2>/dev/null
    181     not_ok "$sym_name" "$work/symz.diag"
    182   else
    183     ok "$sym_name"
    184   fi
    185 }
    186 
    187 for arch in ${KIT_RT_RUNTIME_ARCHES:-aa64 x64 rv64}; do
    188   case "$arch" in
    189     aa64|x64|rv64)
    190       for opt in ${KIT_RT_OPT_LEVELS:-0 1}; do run_one "$arch" "$opt"; done ;;
    191     *) not_ok "$arch" "unknown arch" ;;
    192   esac
    193 done
    194 
    195 kit_summary test-rt-addr2line
    196 kit_exit