由于某些未知的错误导致程序突然崩溃, 进入了zynq的异常处理程序中. 其它信息没了. 暂停代码执行的时候就只有进入了下面的这个函数. 这个函数进入的可能性非常高. 问题是怎么找到原因定位到有问题的代码行呢? 经过一番查找和人工智能的辅助下, 我终于知道如何定位到代码行了.
void Xil_DataAbortHandler(void *CallBackRef)
{
(void) CallBackRef;
//#ifdef DEBUG
volatile u32 FaultStatus;
xdbg_printf(XDBG_DEBUG_ERROR, "Data abort \n");
#ifdef __GNUC__
FaultStatus = mfcp(XREG_CP15_DATA_FAULT_STATUS);
#elif defined (__ICCARM__)
mfcp(XREG_CP15_DATA_FAULT_STATUS,FaultStatus);
#else
{
volatile register u32 Reg __asm(XREG_CP15_DATA_FAULT_STATUS);
FaultStatus = Reg;
}
#endif
xdbg_printf(XDBG_DEBUG_GENERAL, "Data abort with Data Fault Status Register %lx\n",FaultStatus);
xdbg_printf(XDBG_DEBUG_GENERAL, "Address of Instruction causing Data abort %lx\n",DataAbortAddr);
//使用GDB添加断点到 出错的地址, 然后再次进行调试即可.步骤如下
//在GDB窗口中执行命令,break *0x1005cc 其中0x1005cc是DataAbortAddr中存储的触发异常的代码地址.
//#endif
while(1) {
;
}
}
Xil_DataAbortHandler 这断代码是我修改过的 , 注释掉了while死循环,防止无法重现DataAbortAddr
这段代码中,xdbg_printf() 我建议改成普通的printf 或者xli_printf 这样可以直接输出有错误的地址, 然后直接再界面上进行调试. 修改后的代码如下, 增加了更加人性化的提示信息.
@file xil_exception.c
void Xil_DataAbortHandler( void *CallBackRef)
{
// (void) CallBackRef;
volatile u32 FaultStatus;
printf("Xil_DataAbortHandler Data abort at function %x \n",CallBackRef);
#ifdef __GNUC__
FaultStatus = mfcp(XREG_CP15_DATA_FAULT_STATUS);
#elif defined (__ICCARM__)
mfcp(XREG_CP15_DATA_FAULT_STATUS,FaultStatus);
#else
{
volatile register u32 Reg __asm(XREG_CP15_DATA_FAULT_STATUS);
FaultStatus = Reg;
}
#endif
xil_printf("Data abort with Data Fault Status Register %x\n",FaultStatus);
xil_printf("Address of Instruction causing Data abort %x\n ",DataAbortAddr);
xil_printf("Please run the command\"break * %x\" in the GDB console window, add breakpoints to continue debugging \n ",DataAbortAddr);
//使用GDB添加断点到 出错的地址, 然后再次进行调试即可.步骤如下
//在GDB窗口中执行命令,break *0x1005cc 其中0x1005cc是DataAbortAddr中存储的触发异常的代码地址.
while(1) {
xil_printf("Please run the command\"break *0x%x\" in the GDB console window, add breakpoints to continue debugging \n ",DataAbortAddr);
sleep(1);
}
}
在GDB中,如果你已经知道了引发异常的代码指令行(或者至少是接近该行的地址),你可以在该位置添加断点,然后让程序运行到该断点处继续执行,并在程序中断时进行调试。以下是具体步骤:
为防止有的人找不到在哪里执行break 命令. 截个图. 把串口输出的调试信息放到gdb窗口中执行.
下面是一些参考信息, 百度的文心一言人工智能给的. 挺不错的.
添加断点:
如果你知道具体的源代码行号,可以使用break 命令来在该行添加断点。例如,如果异常发生在第123行,你可以输入break 123。
如果你只知道引发异常的指令地址(比如0x1050060),你可以使用break *
命令来在该地址处添加断点。例如,输入break *0x1050060。
运行程序:
使用run命令来运行程序。如果程序之前已经运行过并且你希望重新开始,可以先使用run命令并跟上任何需要的程序参数,或者先使用kill命令终止当前运行的程序,然后再使用run命令。
程序中断:
当程序运行到添加的断点时,它会停下来并等待GDB的进一步命令。此时,你可以使用GDB的各种调试命令来检查程序的状态。
调试:
在程序中断时,你可以使用print命令来查看变量的值,使用step或next命令来单步执行代码,使用backtrace命令来查看调用栈等。
你可以根据需要继续调试,直到找到问题的根源并修复它。
继续执行:
如果你已经找到了问题并修复了它,或者只是想要继续运行程序以查看后续的行为,你可以使用continue命令来让程序继续执行。
移除断点:
如果你不再需要某个断点,可以使用delete 命令来删除它。你可以使用info breakpoints命令来查看当前所有断点的编号。
需要注意的是,如果你是在一个复杂的程序中进行调试,并且异常发生在某个库函数或深层调用栈中,那么仅仅在引发异常的指令行上添加断点可能不足以完全理解问题的根源。在这种情况下,你可能需要设置多个断点,或者使用GDB的条件断点、监视点等功能来更深入地了解程序的执行流程。
此外,由于编译器优化和代码生成的影响,有时源代码中的一行可能对应多条汇编指令,或者一条汇编指令可能跨越了源代码中的多行。因此,在设置断点和解读GDB的输出时,需要谨慎并具备一定的汇编语言和编译器优化知识。