RISC-V Linux下Trap日志解析
作为日常RISC-V Linux Trap问题定位参考。
1 Trap输出日志解析
[ 220.441130] dddd[2177]: unhandled signal 11 code 0x1 at 0x000000a8 in dddd[10000+37000]
- 表示进程 dddd(进程ID为2177)收到了一个未处理的信号11(SIGSEGV),信号11对应于段错误(Segmentation Fault),错误码为1(SEGV_MAPPER,表示这个地址未被映射)。epc指向的vma文件名为dddd,vma区间从0x10000开始,大小为0x37000。
- 错误发生在dddd程序的地址空间的 0x000000a8 位置。
[ 220.451993] CPU: 0 PID: 2177 Comm: ddd Not tainted <kernel version> #1
- 表示这个错误发生在CPU 0上,进程ID(PID)为2177,进程命令名称为dddd。"Not tainted" 表示内核没有因为此错误而被标记为“污染”(通常是由于某些不安全的操作导致的),内核版本为<kernel version>。
[ 220.463629] Hardware name: xxx (DT)
- 表示硬件平台的名称是xxx,这是从设备树(Device Tree,DT)中获取的信息。
[ 220.474064] epc: 000249b2 ra: 00017650 sp: 945fed50
- epc(Exception Program Counter)是一个重要的寄存器,它用于保存发生异常时指令的地址。当发生陷阱(Trap)时,RISC-V会在csr_epc寄存器中保存程序计数器pc的值,因为pc会被mtvec覆盖。
- 根据epc查找dddd的vma,然后给出对应vma的vm_start+(vm_end-vm_start)。即epc (0x000249b2) 发生在0x10000+0x37000之间。所以出问题的epc指向dddd文件的0x000149b2处。
- b C:0669:000249b2 /onchip: 给进程0x669的代码0x000249b2打上断点。
[ 220.484998] gp: 00048800 tp: 945ff920 t0: 00001000
[ 220.495778] t1: 000134dc t2: 00000000 s0: 0004cea0
[ 220.503484] s1: 94700aa0 a0: 945fed90 a1: 00000000
[ 220.508735] a2: 0000003f a3: 945fee4c a4: 20000000
[ 220.513948] a5: 00000000 a6: 0004cfaa a7: 00000000
[ 220.519158] s2: 950ffbd8 s3: 00000010 s4: 00000000
[ 220.524383] s5: 94700c74 s6: 93dff000 s7: 00000000
[ 220.529595] s8: 945ff440 s9: 945ff440 s10: 00000001
[ 220.534814] s11: 00000001 t3: 955f9362 t4: 947003d0
[ 220.540024] t5: 00000008 t6: 950ff858
在RISC-V中,通用寄存器(GP寄存器)用于存储数据和地址。它们包括:
- x0 别名 zero,硬件固定为零,用作源寄存器或目标寄存器。
- x1 别名 ra,链接寄存器,保存函数返回地址。
- x2 别名 sp,栈指针寄存器,指向栈的地址。
- x3 别名 gp,全局寄存器,用于链接器优化。
- x4 别名 tp,线程寄存器,通常在操作系统中用于保存指向进程控制块的指针。来源于CSR_SCRATCH。
- x5 别名 t0,临时/备用链接寄存器,用作临时变量、备用链接寄存器。
- x6-x7 别名 t1-t2,临时寄存器,用作一般的操作。
- x8 别名 s0/fp,保存寄存器/帧指针,用于函数调用,被调用函数需保存数据。
- x9 别名 s1/gp,保存寄存器/全局指针,用于函数调用,被调用函数需要保存的数据。
- x10-x11 别名 a0-a1,函数参数/返回值,用于函数调用时传递参数和返回结果。
- x12-x17 别名 a2-a7,函数参数,用于函数调用时传递参数。
- x18-x27 别名 s2-s11,保存寄存器,用于函数调用,被调用函数需要保存的数据。
- x28-x31 别名 t3-t6,临时寄存器,用于一般的操作。
[ 220.544018] status: 00004020 badaddr: 00000000a8 cause: 0000000f
- status 表示异常状态,来源于CSR_STATUS。
- badaddr 表示导致异常的“坏地址”(这里是 0x000000a8),来源于CSR_TVAL。
- cause 表示异常的原因(这里是 0x000000f,对应于段错误),来源于CSR_CAUSE。
2 Trap打印处理流程
在RISC-V架构的Linux内核中,do_trap 是陷阱处理的核心函数,负责处理CPU产生的同步异常(如系统调用、页错误、非法指令等)和部分中断。
do_trap
pr_info--按照此格式显示错误信息:<进程名>[<进程ID>]: unhandled signal <未处理信号> code <错误码> at 0x<错误指向地址>。
instruction_pointer--获取epc的值。
print_vma_addr--根据epc的值找到vma,进而找到vm_file。此时就获取到epc执行的vma区域和vma区域对应的文件。显示错误信息:in <vma对应文件>[16进制vma起始地址+16进制vma区域大小]。
__show_regs
show_regs_print_info
dump_start_print_info--显示发生错误的进程相关信息:CPU: <CPU ID> PID: <进程ID> Comm: <进程名> <> <Tainted信息> <内核版本号> <>。
printk--显示错误信息:Hardware name: <dump_stack_arch_desc_str>。
user_mode--非内核显示epc和ra信息。
pr_cont--显示一系列寄存器。
force_sig_fault
常见的signal有:
#define SIGHUP 1 #define SIGINT 2 #define SIGQUIT 3 #define SIGILL 4 #define SIGTRAP 5 #define SIGABRT 6 #define SIGIOT 6 #define SIGBUS 7 #define SIGFPE 8 #define SIGKILL 9 #define SIGUSR1 10 #define SIGSEGV 11 #define SIGUSR2 12 #define SIGPIPE 13 #define SIGALRM 14 #define SIGTERM 15 #define SIGSTKFLT 16 #define SIGCHLD 17 #define SIGCONT 18 #define SIGSTOP 19 #define SIGTSTP 20 #define SIGTTIN 21 #define SIGTTOU 22 #define SIGURG 23 #define SIGXCPU 24 #define SIGXFSZ 25 #define SIGVTALRM 26 #define SIGPROF 27 #define SIGWINCH 28 #define SIGIO 29 #define SIGPOLL SIGIO /* #define SIGLOST 29 */ #define SIGPWR 30 #define SIGSYS 31 #define SIGUNUSED 31
#define ILL_ILLOPC 1 /* illegal opcode */ #define ILL_ILLOPN 2 /* illegal operand */ #define ILL_ILLADR 3 /* illegal addressing mode */ #define ILL_ILLTRP 4 /* illegal trap */ #define ILL_PRVOPC 5 /* privileged opcode */ #define ILL_PRVREG 6 /* privileged register */ #define ILL_COPROC 7 /* coprocessor error */ #define ILL_BADSTK 8 /* internal stack error */ #define ILL_BADIADDR 9 /* unimplemented instruction address */ #define __ILL_BREAK 10 /* illegal break */ #define __ILL_BNDMOD 11 /* bundle-update (modification) in progress */ #define NSIGILL 11
/* * SIGTRAP si_codes */ #define TRAP_BRKPT 1 /* process breakpoint */ #define TRAP_TRACE 2 /* process trace trap */ #define TRAP_BRANCH 3 /* process taken branch trap */ #define TRAP_HWBKPT 4 /* hardware breakpoint/watchpoint */ #define TRAP_UNK 5 /* undiagnosed trap */ #define TRAP_PERF 6 /* perf event with sigtrap=1 */ #define NSIGTRAP 6
#define BUS_ADRALN 1 /* invalid address alignment */ #define BUS_ADRERR 2 /* non-existent physical address */ #define BUS_OBJERR 3 /* object specific hardware error */ /* hardware memory error consumed on a machine check: action required */ #define BUS_MCEERR_AR 4 /* hardware memory error detected in process but not consumed: action optional*/ #define BUS_MCEERR_AO 5 #define NSIGBUS 5
/* * SIGFPE si_codes */ #define FPE_INTDIV 1 /* integer divide by zero */ #define FPE_INTOVF 2 /* integer overflow */ #define FPE_FLTDIV 3 /* floating point divide by zero */ #define FPE_FLTOVF 4 /* floating point overflow */ #define FPE_FLTUND 5 /* floating point underflow */ #define FPE_FLTRES 6 /* floating point inexact result */ #define FPE_FLTINV 7 /* floating point invalid operation */ #define FPE_FLTSUB 8 /* subscript out of range */ #define __FPE_DECOVF 9 /* decimal overflow */ #define __FPE_DECDIV 10 /* decimal division by zero */ #define __FPE_DECERR 11 /* packed decimal error */ #define __FPE_INVASC 12 /* invalid ASCII digit */ #define __FPE_INVDEC 13 /* invalid decimal digit */ #define FPE_FLTUNK 14 /* undiagnosed floating-point exception */ #define FPE_CONDTRAP 15 /* trap on condition */ #define NSIGFPE 15
#define SEGV_MAPERR 1 /* address not mapped to object */
#define SEGV_ACCERR 2 /* invalid permissions for mapped object */
#define SEGV_BNDERR 3 /* failed address bound checks */
#ifdef __ia64__
# define __SEGV_PSTKOVF 4 /* paragraph stack overflow */
#else
# define SEGV_PKUERR 4 /* failed protection key checks */
#endif
#define SEGV_ACCADI 5 /* ADI not enabled for mapped object */
#define SEGV_ADIDERR 6 /* Disrupting MCD error */
#define SEGV_ADIPERR 7 /* Precise MCD exception */
#define SEGV_MTEAERR 8 /* Asynchronous ARM MTE error */
#define SEGV_MTESERR 9 /* Synchronous ARM MTE exception */
#define NSIGSEGV 9
3 Linux下信号特性列表
| 信号值 | 信号名 | 说明 | 常见si_codes值(宏定义) | 可屏蔽 | 导致Coredump |
|---|---|---|---|---|---|
| 1 | SIGHUP |
终端连接断开或控制进程终止,常用于通知守护进程重载配置 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 2 | SIGINT |
交互式中断(Ctrl+C),请求进程终止 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 3 | SIGQUIT |
交互式退出(Ctrl+\),请求进程终止并生成核心转储 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 是 |
| 4 | SIGILL |
非法CPU指令,通常因执行无效或特权指令触发 | ILL_ILLOPC (1) - 非法操作码ILL_ILLOPN (2) - 非法操作数ILL_ILLADR (3) - 非法寻址模式ILL_PRVOPC (5) - 特权指令ILL_PRVREG (6) - 特权寄存器访问 |
是 | 是 |
| 5 | SIGTRAP |
调试陷阱,用于断点和单步执行 | TRAP_BRKPT (1) - 断点陷阱TRAP_TRACE (2) - 跟踪陷阱TRAP_UNK (5) - RISC-V未知陷阱 |
是 | 是 |
| 6 | SIGABRT |
进程中止信号,由abort()函数触发 |
SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送SI_QUEUE (-1) - sigqueue()发送 |
是 | 是 |
| 7 | SIGBUS |
总线错误,内存访问违反硬件约束 | BUS_ADRALN (1) - 地址未对齐BUS_ADRERR (2) - 物理地址无效BUS_OBJERR (3) - 硬件错误 |
是 | 是 |
| 8 | SIGFPE |
算术异常,如除零或浮点溢出 | FPE_INTDIV (1) - 整数除零FPE_INTOVF (2) - 整数溢出FPE_FLTDIV (3) - 浮点除零FPE_FLTOVF (4) - 浮点上溢 |
是 | 是 |
| 9 | SIGKILL |
强制终止信号,不可被捕获或忽略 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
否 | 否 |
| 10 | SIGUSR1 |
用户自定义信号1,可用于进程间通信 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送SI_QUEUE (-1) - sigqueue()发送 |
是 | 否 |
| 11 | SIGSEGV |
段错误,无效内存访问(最常见信号之一) | SEGV_MAPERR (1) - 地址未映射SEGV_ACCERR (2) - 权限不足SEGV_BNDERR (3) - 地址边界错误 |
是 | 是 |
| 12 | SIGUSR2 |
用户自定义信号2,可用于进程间通信 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送SI_QUEUE (-1) - sigqueue()发送 |
是 | 否 |
| 13 | SIGPIPE |
管道破裂,向已关闭的管道或套接字写入 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 14 | SIGALRM |
实时定时器到期,由alarm()或setitimer()设置 |
SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 15 | SIGTERM |
终止请求(kill默认),请求进程正常退出 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 16 | SIGSTKFLT |
协处理器栈错误(历史遗留,现代系统很少使用) | SI_USER (0) - 用户进程发送 |
是 | 否 |
| 17 | SIGCHLD |
子进程状态变更(终止/停止/继续) | CLD_EXITED (1) - 子进程退出CLD_KILLED (2) - 子进程被杀死CLD_STOPPED (5) - 子进程停止CLD_CONTINUED (6) - 子进程继续 |
是 | 否 |
| 18 | SIGCONT |
继续执行先前停止的进程 | SI_USER (0) - 用户进程发送 |
是 | 否 |
| 19 | SIGSTOP |
强制停止信号,不可被捕获或忽略 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
否 | 否 |
| 20 | SIGTSTP |
终端停止请求(Ctrl+Z),请求进程停止 | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 21 | SIGTTIN |
后台进程尝试读取控制终端 | SI_USER (0) - 用户进程发送 |
是 | 否 |
| 22 | SIGTTOU |
后台进程尝试写入控制终端 | SI_USER (0) - 用户进程发送 |
是 | 否 |
| 23 | SIGURG |
紧急/带外数据到达(TCP套接字) | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 24 | SIGXCPU |
CPU时间超限(超过RLIMIT_CPU限制) | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 可能 |
| 25 | SIGXFSZ |
文件大小超限(超过RLIMIT_FSIZE限制) | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 可能 |
| 26 | SIGVTALRM |
虚拟时间定时器到期(ITIMER_VIRTUAL) | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 27 | SIGPROF |
性能分析定时器到期(ITIMER_PROF) | SI_USER (0) - 用户进程发送SI_KERNEL (128) - 内核发送 |
是 | 否 |
| 28 | SIGWINCH |
终端窗口大小改变 | SI_USER (0) - 用户进程发送 |
是 | 否 |
| 29 | SIGPOLL |
可轮询事件发生(System V信号名,等同SIGIO) | POLL_IN (1) - 数据可读POLL_OUT (2) - 输出缓冲可用POLL_MSG (3) - 消息到达POLL_ERR (4) - I/O错误 |
是 | 否 |
| 30 | SIGPWR |
电源故障/电池电量低,通常由UPS监控进程发送 | SI_USER (0) - 用户进程发送 |
是 | 否 |
| 31 | SIGSYS |
无效系统调用/seccomp策略违规 | SYS_SECCOMP (1) - seccomp过滤触发SYS_USER_DISPATCH (2) - 用户调度机制触发 |
是 | 是 |
| 32-64 | SIGRTMIN-SIGRTMAX |
实时信号,用于自定义事件,支持排队处理 | SI_QUEUE (-1) - sigqueue()发送SI_TIMER (-2) - 定时器到期SI_ASYNCIO (-4) - 异步I/O完成 |
是 | 否 |
浙公网安备 33010602011771号