hitHuang opened a new pull request, #19528: URL: https://github.com/apache/nuttx/pull/19528
## Summary risc-v's `up_backtrace()` currently has the following problems: 1. Under `CONFIG_BUILD_KERNEL`, `dumpstack` on a user task only dumps its user stack, never its kernel stack. 2. Under `CONFIG_BUILD_FLAT`/`CONFIG_BUILD_PROTECTED`, backtrace output contains duplicate frames. 3. The existing implementation piles up branches, macros, and conditionals to handle the various combinations, which makes the code hard to follow and makes it increasingly difficult to keep fixing bugs or adding support for new scenarios on top of the existing structure. Problem 1 was already noted in https://github.com/apache/nuttx/pull/19439, but never actually fixed in code. Problem 2 is caused by an implementation bug that dumps the same range of the user stack twice. This change reworks `up_backtrace()` around a table of stack ranges (interrupt stack / kernel stack / user stack) that a given fp chain may legitimately cross into, walked by one shared procedure that switches ranges as needed, instead of hand-writing a separate branch with one or two hardcoded ranges per scenario. ### Problem 1 repro `rv-virt:knsh`, `dumpstack` on a user task only shows frames on the user stack (tid 2/3/4 partially only show kernel-stack addresses `0xc0...`, missing the corresponding user-stack frames): ``` NuttShell (NSH) NuttX-13.0.0 nsh> dumpstack 0 10 [3951369912320000.001000] sched_dumpstack: backtrace| 0: 0x8020cf92 0x80608730 0x802011d6 0x8020004a [3977139716096000.001000] sched_dumpstack: backtrace| 1: 0x8020cafc 0x80218ffa 0x80208cfc 0x80208d20 0x8020312c 0x80202a94 [3990024617984000.001000] sched_dumpstack: backtrace| 3: 0xc000c478 0xc000561e 0xc0002d4e 0xc0003cbc 0xc0003d5c 0xc0002722 0xc0002542 0xc0000b4a [4002909519872000.001000] sched_dumpstack: backtrace| 3: 0xc0000b08 [4011499454464000.001000] sched_dumpstack: backtrace| 4: 0xc000289a 0xc0000b44 0xc0000aca 0xc0000a8c ``` ### Problem 2 repro `rv-virt:nsh`, `mb 0xdeadbeef 1` faults on an invalid address, and the backtrace for nsh_main (tid 2) itself contains duplicate frames. This happens any time `up_backtrace()` tries to backtrace self from `up_interrupt_context()`. ``` NuttShell (NSH) NuttX-13.0.0 nsh> ps TID PID PPID PRI POLICY TYPE NPX STATE EVENT SIGMASK STACK USED FILLED COMMAND 0 0 0 0 FIFO Kthread - Ready 0000000000000000 0002016 0000668 33.1% Idle_Task 2 2 0 100 RR Task - Running 0000000000000000 0001984 0001984 100.0%! nsh_main nsh> mb 0xdeadbeef 1 riscv_exception: EXCEPTION: Load access fault. MCAUSE: 00000005, EPC: 8000b450, MTVAL: deadbeef riscv_fault_handler: PANIC!!! Exception = 00000005 dump_assert_info: Current Version: NuttX 13.0.0 2f73fe2267-dirty Jul 20 2026 09:26:46 risc-v dump_assert_info: Assertion failed panic: at file: :0 task: nsh_main process: nsh_main 0x80008ce2 up_dump_register: EPC: 8000b450 ...... stack_dump: 0x80039160: 00000000 00000000 80039180 80002288 00000000 00000000 00000000 00000000 stack_dump: 0x80039180: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 sched_dumpstack: backtrace| 2: 0x80025234 0x80018dc8 0x80015590 0x80008566 0x80001918 0x8000156a 0x8000150c 0x80000186 sched_dumpstack: backtrace| 2: 0x8000b450 0x8000b3b6 0x8000973a 0x8000a490 0x8000a530 0x80008f42 0x80008d62 0x80008d1e sched_dumpstack: backtrace| 2: 0x80006248 0x80002288 0x8000b450 0x8000b3b6 0x8000973a 0x8000a490 0x8000a530 0x80008f42 sched_dumpstack: backtrace| 2: 0x80008d62 0x80008d1e 0x80006248 0x80002288 dump_tasks: PID GROUP PRI POLICY TYPE NPX STATE EVENT SIGMASK STACKBASE STACKSIZE USED FILLED COMMAND dump_tasks: ---- --- --- -------- ------- --- ------- ---------- ---------------- 0x80034380 2048 1100 53.7% irq dump_task: 0 0 0 FIFO Kthread - Ready 0000000000000000 0x80037180 2016 668 33.1% Idle_Task dump_task: 2 2 100 RR Task - Running 0000000000000000 0x800389c0 1984 1984 100.0%! nsh_main sched_dumpstack: backtrace| 0: 0x80008bc0 0x80037950 0x800014a0 0x80000048 sched_dumpstack: backtrace| 2: 0x80025234 0x80018dc8 0x800151d8 0x80015960 0x80015624 0x80008566 0x80001918 0x8000156a sched_dumpstack: backtrace| 2: 0x8000150c 0x80000186 0x8000b450 0x8000b3b6 0x8000973a 0x8000a490 0x8000a530 0x80008f42 sched_dumpstack: backtrace| 2: 0x80008d62 0x80008d1e 0x80006248 0x80002288 0x8000b450 0x8000b3b6 0x8000973a 0x8000a490 sched_dumpstack: backtrace| 2: 0x8000a530 0x80008f42 0x80008d62 0x80008d1e 0x80006248 0x80002288 ``` The duplicated part (`0x8000b450 0x8000b3b6 0x8000973a 0x8000a490 0x8000a530 0x80008f42 0x80008d62 0x80008d1e 0x80006248 0x80002288`) resolved via addr2line: ``` huang@ubuntu:~/workspace/nuttx-fork/nuttx$ riscv64-unknown-elf-addr2line -f -i -e nuttx 0x8000b450 0x8000b3b6 0x8000973a 0x8000a490 0x8000a530 0x80008f42 0x80008d62 0x80008d1e 0x80006248 0x80002288 cmd_mb /home/huang/workspace/nuttx-fork/apps/nshlib/nsh_dbgcmds.c:150 nsh_command /home/huang/workspace/nuttx-fork/apps/nshlib/nsh_command.c:1321 nsh_execute /home/huang/workspace/nuttx-fork/apps/nshlib/nsh_parse.c:759 nsh_parse_command /home/huang/workspace/nuttx-fork/apps/nshlib/nsh_parse.c:2911 nsh_parse /home/huang/workspace/nuttx-fork/apps/nshlib/nsh_parse.c:3116 nsh_session /home/huang/workspace/nuttx-fork/apps/nshlib/nsh_session.c:249 nsh_consolemain /home/huang/workspace/nuttx-fork/apps/nshlib/nsh_consolemain.c:81 nsh_main /home/huang/workspace/nuttx-fork/apps/system/nsh/nsh_main.c:82 nxtask_startup /home/huang/workspace/nuttx-fork/nuttx/libs/libc/sched/task_startup.c:72 (discriminator 1) nxtask_start /home/huang/workspace/nuttx-fork/nuttx/sched/task/task_start.c:72 ``` The same duplication reproduces under `rv-virt:pnsh` as well. Along the way, several related but independent issues were found and fixed and are tracked as separate PRs: https://github.com/apache/nuttx/pull/19439, https://github.com/apache/nuttx/pull/19468, https://github.com/apache/nuttx/pull/19471, https://github.com/apache/nuttx/pull/19479, https://github.com/apache/nuttx/pull/19487. This PR also picks up two small, related fixes/improvements along the way: 1. `up_idle()` is a leaf function that doesn't spill `ra`, which produced garbage frames at the tail of a backtrace that unwound through it. 2. Under `CONFIG_BUILD_KERNEL`, when a user app is killed due to a fault, its backtrace is now dumped proactively to help with debugging. Since these two are small in scope, the rest of this description focuses on the `up_backtrace()` fix/rework described above. ## Impact Only affects `up_backtrace()` in `arch/risc-v/src/common/riscv_backtrace.c`. Fixes the missing-kernel-stack-frames bug under `CONFIG_BUILD_KERNEL` and the duplicate-frame bug under `CONFIG_BUILD_FLAT`/`CONFIG_BUILD_PROTECTED` described above, and replaces the previous scenario-specific hardcoded branches with a general multi-range registration + relay walk. Behavior for already-covered scenarios should stay equivalent or become more complete (recovering previously missing frames); no new Kconfig options or API changes. ## Testing Only one SiFive U74 risc-v SoC is available as real hardware, so most of the verification below is on QEMU. Config dimensions covered: 1. rv32 / rv64 2. BUILD_FLAT / BUILD_PROTECTED / BUILD_KERNEL 3. no-smp / smp 4. CONFIG_ARCH_INTERRUPTSTACK > 15 / < 15 The full cross product is 2 * 3 * 2 * 2 = 24 combinations, too many to cover individually. Testing mainly relied on the existing nsh/pnsh/knsh/smp/ksmp64 configs, with `CONFIG_ARCH_INTERRUPTSTACK` toggled manually to cover the remaining combination. These configs cover the combinations of interest: | Config | Arch | Build mode | SMP | INTERRUPTSTACK > 15 | KERNEL_STACK + ADDRENV | LIBC_SYSCALL | |------------------|------|------------|-----|----------------------|------------------------|--------------| | nsh | RV32 | FLAT | N | Y | N | N | | pnsh | RV32 | PROTECTED | N | Y | N | Y | | knsh | RV32 | KERNEL | N | Y | Y | Y | | smp | RV64 | FLAT | Y | Y | N | N | | ksmp64 | RV64 | KERNEL | Y | Y | Y | Y | | nsh (no intstack) | RV32 | FLAT | N | N | N | N | | pnsh (no intstack) | RV32 | PROTECTED | N | N | N | Y | | knsh (no intstack) | RV32 | KERNEL | N | N | Y | Y | Scenarios covered (the most complex case involves interrupt stack + kernel stack + user stack together; there's also an independent dimension of backtracing self vs. a different task, where cross-task backtracing under BUILD_KERNEL additionally has to cross address spaces): 1. ostest 2. dumpstack (application) on self / another task 3. `mb` on an invalid address triggering a panic with backtrace output 4. backtrace from an interrupt handler that preempted a kernel thread 5. backtrace from an interrupt handler that preempted a user thread running in user mode 6. backtrace from an interrupt handler that preempted a user thread that had trapped into the kernel via a syscall 7. NULL pointer access inside an interrupt handler that preempted a kernel thread, triggering a panic with backtrace output 8. NULL pointer access inside an interrupt handler that preempted a user thread running in user mode, triggering a panic with backtrace output 9. NULL pointer access inside an interrupt handler that preempted a user thread that had trapped into the kernel via a syscall, triggering a panic with backtrace output 10. a user thread trapping into the kernel and accessing a NULL pointer, triggering a panic with backtrace output Notes: - Under BUILD_FLAT/BUILD_PROTECTED, scenarios 4/5/6 are treated as a single case, since these build modes don't distinguish user stack from kernel stack; likewise for 7/8/9. Scenario 10 only applies to BUILD_KERNEL. - Under SMP, interrupt-handler scenarios only test backtracing self (cross-CPU backtracing from interrupt context isn't recommended). Test matrix and results (`-` means not applicable for that config): | Config | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |--------------------|---|---|---|---|---|---|---|---|---|----| | nsh | Y | Y | Y | Y | | | Y | | | - | | pnsh | Y | Y | Y | Y | | | Y | | | - | | knsh | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | smp | Y | Y | Y | Y | | | Y | | | - | | ksmp64 | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | nsh (no intstack) | Y | Y | Y | Y | | | Y | | | - | | pnsh (no intstack) | Y | Y | Y | Y | | | Y | | | - | | knsh (no intstack) | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | All the above test cases have been covered, and the backtrace output matched expectations in every case. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
