简体   繁体   English

查找在cortex-m4上发生中断的位置

[英]find where the interrupt happened on cortex-m4

I am trying to find where in my code a specific interrupt happened. 我试图找到我的代码中发生了特定的中断。 In this case it is on a stm32f4 microcontroller and the interrupt is the SysTick_Handler. 在这种情况下,它在stm32f4微控制器上,并且中断是SysTick_Handler。

What i want is basically to figure out from where the systick interrupt happened. 我想要的基本上是弄清楚从哪里发生了systick中断。 I am using arm-none-eabi-gdb to try to find the backtrace, but the only information i am getting from there is: 我正在使用arm-none-eabi-gdb尝试查找回溯,但是我从那里得到的唯一信息是:

(gdb) bt
#0  SysTick_Handler () at modules/profiling.c:66
#1  <signal handler called>
#2  0x55555554 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?)

How can I get some information about where the program was before the interrupt fired? 如何获得有关中断触发之前程序所在位置的信息?

Looking at the arm documentation here , it seems I should be able to read the stack pointer, and get the PC from there. 这里查看 arm文档,看来我应该能够读取堆栈指针,并从那里获取PC。 But then this is exactly what the unwinder in GDB is doing isnt it? 但这就是GDB的取消器正在执行的工作吗?

You were on the right track at the end of your question. 问题的结尾您走在正确的轨道上。 The ARM Cortex-M cores have two stack pointers, the main stack pointer (MSP, used for interrupts) and the process stack pointer (PSP, used for tasks). ARM Cortex-M内核具有两个堆栈指针,主堆栈指针(MSP,用于中断)和进程堆栈指针(PSP,用于任务)。

When an interrupt with priority comes in, the current register values (for most of the registers) are pushed onto the current stack (PSP if interrupting the background application, or MSP if interrupting a lower priority interrupt), and then the stack is switched to the MSP (if not already there). 当具有优先级的中断进入时,当前寄存器值(用于大多数寄存器)被压入当前堆栈(如果中断后台应用程序,则为PSP;如果中断较低优先级的中断,则为MSP),然后将堆栈切换到MSP(如果尚未存在)。

When you first enter an interrupt, the link register (LR, return address) will have a value that is mostly F's rather than an actual return address. 首次输入中断时,链接寄存器(LR,返回地址)将具有一个大多数为F的值,而不是实际的返回地址。 This value tells the core how to exit when branched to. 该值告诉内核分支时如何退出。 Typically, you'll see a value of 0xFFFFFFFD if the background task was interrupted, or 0xFFFFFFF1 if a lower priority interrupt was interrupted. 通常情况下,你会看到的值0xFFFFFFFD如果后台任务被中断,或0xFFFFFFF1如果一个低优先级的中断被中断。 These values will differ if you are using the floating point unit. 如果使用浮点单位,这些值将有所不同。 The magic in this value, though, is that bit 2 ( 0x4 ) tells you whether your stack frame is on the PSP or MSP. 不过,此值的神奇之处在于,位2( 0x4 )告诉您堆栈帧是在PSP还是MSP上。

Once you determine which stack your frame is on, you can find the address you were executing from by looking at the appropriate stack pointer minus 24 (6 32-bit locations). 一旦确定了框架所在的堆栈,就可以通过查看适当的堆栈指针减24(6个32位位置)来找到执行地址。 See Figure 2.3 in your link. 请参见链接中的图2.3。 This will point you to the PC from which you were interrupted. 这会将您指向被中断的PC。

We keep seeing this question in various forms and folks keep saying there are two stacks. 我们一直以各种形式看到这个问题,人们一直说有两个堆栈。 So I tried it myself with the systick. 所以我自己用操纵杆尝试了一下。

The documentation says that we are in thread mode out of reset, and if you halt with openocd it says that 该文档说我们处于退出重置的线程模式,如果您用openocd暂停,它表示

target halted due to debug-request, current mode: Thread 

I have some code to dump registers: 我有一些转储寄存器的代码:

20000000 APSR
00000000 IPSR
00000000 EPSR
00000000 CONTROL
00000000 SP_PROCESS
20000D00 SP_PROCESS after I modified it
20000FF0 SP_MAIN
20000FF0 mov r0,sp  
then I dump the stack up to 0x20001000 which is where I know my stack started
20000FF0 00000000 
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 

I setup and wait for a systick interrupt, the handler dumps registers and ram and then goes into an infinite loop. 我设置并等待一个systick中断,处理程序转储寄存器和ram,然后进入无限循环。 bad practice in general but just debugging/learning here. 通常是不好的做法,但这里只是调试/学习。 Before the interrupt I prep some registers: 在中断之前,我准备了一些寄存器:

.thumb_func
.globl iwait
iwait:
    mov r0,#1
    mov r1,#2
    mov r2,#3
    mov r3,#4
    mov r4,#13
    mov r12,r4
    mov r4,#15
    mov r14,r4
    b .

and in the handler I see 在处理程序中,我看到

20000000 APSR
0000000F IPSR
00000000 EPSR
00000000 CONTROL
20000D00 SP_PROCESS
20000FC0 SP_MAIN
20000FC0 mov r0,sp
20000FC0 0000000F 
20000FC4 20000FFF 
20000FC8 00000000 
20000FCC FFFFFFF9  this is our special lr (not one rjp mentioned)
20000FD0 00000001  this is r0
20000FD4 00000002  this is r1
20000FD8 00000003  this is r2
20000FDC 00000004  this is r3
20000FE0 0000000D  this is r12
20000FE4 0000000F  this is r14/lr
20000FE8 01000074  and this is where we were interrupted from
20000FEC 21000000  this is probably the xpsr mentioned
20000FF0 00000000  stuff that was there before
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 


01000064 <iwait>:
 1000064:   2001        movs    r0, #1
 1000066:   2102        movs    r1, #2
 1000068:   2203        movs    r2, #3
 100006a:   2304        movs    r3, #4
 100006c:   240d        movs    r4, #13
 100006e:   46a4        mov ip, r4
 1000070:   240f        movs    r4, #15
 1000072:   46a6        mov lr, r4
 1000074:   e7fe        b.n 1000074 <iwait+0x10>
 1000076:   bf00        nop

So in this case, straight out of the ARM documentation, it is not using the sp_process it is using sp_main. 因此,在这种情况下,出于ARM文档的考虑,它没有使用sp_process,而是使用了sp_main。 It is pushing the items the manual says it is pushing including the interrupted/return address which is 0x1000074. 它正在推送手册说正在推送的项目,包括中断/返回地址0x1000074。

Now, if I set the SPSEL bit (be careful to set the PSP first), it appears that a mov r0,sp in application/thread mode uses the PSP not MSP. 现在,如果我将SPSEL位置1(请小心先设置PSP),则看来在应用程序/线程模式下的mov r0,sp使用的是PSP而不是MSP。 But then the handler uses msp for a mov r0,sp but appears to put the 但随后处理程序将msp用于mov r0,sp,但似乎将

before in thread/foreground 在线程/前景之前

20000000 APSR
00000000 IPSR
00000000 EPSR
00000000 SP_PROCESS
20000D00 SP_PROCESS modified
00000000 CONTROL
00000002 CONTROL modified
20000FF0 SP_MAIN
20000D00 mov r0,sp

now in the handler 现在在处理程序中

20000000 APSR
0000000F IPSR
00000000 EPSR
00000000 CONTROL (interesting!)
20000CE0 SP_PROCESS
20000FE0 SP_MAIN
20000FE0 mov r0,sp
dump of that stack
20000FE0 0000000F 
20000FE4 20000CFF 
20000FE8 00000000 
20000FEC FFFFFFFD 
20000FF0 00000000 
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 
dump of sp_process stack
20000CE0 00000001 
20000CE4 00000002 
20000CE8 00000003 
20000CEC 00000004 
20000CF0 0000000D 
20000CF4 0000000F 
20000CF8 01000074 our return value
20000CFC 21000000 

So to be in this position of dealing with the alternate stack that folks keep mentioning, you have to put yourself in that position (or some code you rely on). 因此,要处在人们不断提及的替代堆栈的位置,您必须将自己置于该位置(或您所依赖的某些代码)。 Why you would want to do that for simple bare metal programs, who knows, the control register of all zeros is nice and easy, can share one stack just fine. 为什么要对简单的裸机程序执行此操作,谁知道,全零的控制寄存器又好又容易,可以共享一个堆栈。

I dont use gdb, but you need to get it to dump all the registers sp_process and sp_main then depending on what you find, then dump a dozen or so words at each and in there you should see the 0xFFFFFFFx as a marker then count down from that to see the return address. 我不使用gdb,但是您需要获取它以转储所有寄存器sp_process和sp_main,然后根据找到的内容转储,然后在每个处转储十几个单词,在那里您应该看到0xFFFFFFFx作为标记,然后从看到寄信人地址。 You can have your handler read the two stack pointers as well then you can look at gprs. 您也可以让处理程序读取两个堆栈指针,然后再查看gprs。 With gnu assembler mrs rX,psp; 使用gnu汇编程序mrs rX,psp; mrs rX,msp; rX,msp先生; For the process and main stack pointers. 用于进程和主堆栈的指针。

As many of you commented, the PC would be in two different stacks, the way I solved it was by actually finding a HardFault_Handling code in assembly and taking what i needed from there. 正如你们中许多人所评论的那样,PC将放在两个不同的堆栈中,而我解决它的方法是,在组装中实际找到HardFault_Handling代码,然后从那里获取所需信息。 To get the PC value correctly I am using the following code. 为了正确获得PC值,我使用以下代码。

register int *r0 __asm("r0");

__asm(  "TST lr, #4\n"
        "ITE EQ\n"
        "MRSEQ r0, MSP\n"
        "MRSNE r0, PSP\n" // stack pointer now in r0
        "ldr r0, [r0, #0x18]\n" // stored pc now in r0
        //"add r0, r0, #6\n" // address to stored pc now in r0
     );

The value of where the interrupt happended can now be accessed by 现在可以通过以下方式访问发生中断的位置的值:

uint32_t PC = *r0;

and can now be used for whatever I want it. 现在可以用于我想要的任何东西。 Unfortunately I did not manage to get GDB to unwind the stack automatically for me. 不幸的是,我没有设法让GDB为我自动释放堆栈。 But at least I found out where the interrupt was firing, which was the goal. 但是至少我发现了中断在哪里触发,这就是目标。

This is called DEBUGGING. 这称为DEBUGGING。 The easiest way to get started is to just stick a bunch of printf() calls here and there throughout the code. 最简单的入门方法是在整个代码中到处粘贴一堆printf()调用。 Run the program. 运行程序。 If it prints out: 如果打印出来:

got to point A 指向A点
got to point B 到了B点
got to point C 指向C点

and dies, then you know it died between "C" and "D." 死了,那么你知道它死在“ C”和“ D”之间。 You can now refine that downwards by festooning the code between "C" and "D" with more closely spaced printf() calls. 现在,您可以通过使用间距更近的printf()调用“ C”和“ D”之间的代码来向下精简。

This is the best way for a beginner to get started. 这是初学者入门的最佳方法。 Many seasoned experts also prefer printf() for debugging. 许多经验丰富的专家还喜欢使用printf()进行调试。 Debuggers can get in the way. 调试器可能会妨碍您。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM