如何你对单纯的通过硬件断点、在内存窗口和观察窗口下调试代码不满意的话,可以尝试一下中断命令,你可以设置进入中断的“条件”。这个“条件”可以是执行多少次后才出发中断,读写某个内存地址或地址范围时触发中断。
值得一提的是,3.2节测试1和测试2显示出了Cortex-M7的六级流水微架构的特性。
目录
1 调试命令窗口
在默认布局下,调试命令窗口在Keil的Debug模式下的左下角。
调试命令窗口中的命令可见译问[2] Keil调试笔记:调试表达式(翻译)。
2 BD/BL/BE/BK
BD:断点失能,BL:显示断点列表
BE:断点使能
BK:断点清除
3 BreakSet
BreakSet命令为指定的表达式(exp)设置断点。断点是程序地址或表达式,如果为真,则停止执行目标程序或执行指定的命令(“cmd”)。
Syntax
Description
BreakSet exp , cnt , "cmd"
Set an execution or conditional breakpoint.
BreakSet READ exp , cnt , "cmd"
Set a read access breakpoint.
BreakSet WRITE exp , cnt , "cmd"
Set a write access breakpoint.
BreakSet READWRITE exp , cnt , "cmd"
Set a read/write access breakpoint.
断点参数:
where:
exp
是地址说明(address specification)或在执行期间计算的表达式。有关细节,请参阅表达式。
cnt
用于确定在目标程序停止或执行指定命令之前应满足断点条件的次数。默认计数值为1。在第一个断点触发器之后,将忽略计数。
cmd
是µVision命令字符串。当断点触发时,该命令字符串将被执行。程序执行不会停止。用户定义函数和信号函数可以在命令表达式中使用。在函数中,可以将系统变量_break_设置为1来停止程序执行。当没有指定任何命令时,程序执行将停止。
断点类型通过以下规则进行分类:
类型 |
程序访问速度 |
说明 |
访问断点 |
不受影响 |
语句中有一种内存访问模式被声明(Read、Write或 READWRITE)的点断。 内存访问断点的表达式exp必须能解析为内存地址和内存类型(Cortex-M有个特例,见测试4)。 详细使用限制见[1] |
执行断点 |
不受影响 |
特定表达式exp为一个简单的代码地址的断点。 这里的简单指的是不能有内存访问模式说明。 一处代码地址,只能指定一次执行断点。 |
条件断点 |
受影响 |
特定表达式exp不能被解析为地址的断点。 当指定的条件表达式为真时,条件断点停止程序执行或执行命令。 程序访问速度降低的原因是:条件表达式在每一个汇编指令后会再计算一次,从而判断条件是否满足。 貌似CortexM系类不支持条件断点。 |
3.1 执行断点
执行断点测试代码
测试1:按Ctrl+B,进入断点对话框:
然后在命令行窗口就会生成:
>>BS \\GNSS_Project\../User/stm32h7xx_hal_msp.c\55, 7
或写出路径所对应的汇编地址:BS 0x08000E9A,7
exp选择了地址说明(可解析为地址),格式详见[x]"程序变量(符号)".
注:为了便于阅读,用“>>”表示输入指令,在实际命令行窗口内并没有该符号的显示。
于是运行从断点0后,按Run可直接运行到断点1,栈内置为:
并且断点从条件断点退化为普通断点。
测试2:
说明:程序并没有在条件中断中停下来
测试3:
>>BS 0x08000E9A,7,"printf(\"@i=%u\\n\",i)\r\n_break_=1"
@i=6
说明:程序运行到条件中断后,打印i并停下来。
测试4:
>>BS 0x08000E9A,7,"printf(\"@i=%u\\n\",i)\r\nBD 1"
@i=6
说明:程序运行到条件中断后运行指令,运行后将改断点失效,并运行后续代码。
测试5:
>>BS BreakPointTest
则程序在BreakPointTest函数开始处中断。
3.2 访问断点
访问断点测试代码及汇编:
测试1:
>>BS WRITE 0x24000000,6,"printf(\"@i=%u\\n\",i)\r\n_break_=1" 或者
>>BS READ &BreakPointFlag,6,"printf(\"@i=%u\\n\",i)\r\n_break_=1"
注:BreakPointFlag的地址可以通过在命令窗口中输入&BreakPointFlag获得
@i=5
Watch 1中: BreakPointFlag == 0x06;程序指针指向0x81013E6
可先写访问断点的执行是在写结束以后进行的,程序指针是在写指针之后的第6个指令,可见微架构为有六级流水。
测试2:
>>BS READ 0x24000000,6,"printf(\"@i=%u\\n\",i)\r\n_break_=1"
@i=5
Watch 1中: BreakPointFlag == 0x05;程序指针指向0x81013D6,程序指针是在读指针之后的第3个指令,这是因为程序必须在读完后才能回写,所有必须等待,流水被短暂阻塞。
测试3:
>>BS READWRITE &BreakPointFlag,6,"printf(\"@i=%u\\n\",i)\r\n_break_=1"
@i=2
Watch 1中: BreakPointFlag == 0x06
测试4:
For Cortex-M, remove ambiguity using a pointer type cast to resolve the constant address.
>>BS WRITE * ((unsigned int*)0x20000018) == 0x00000003 /* Valid expression */
>>BS WRITE *((unsigned int*)0x24000000)==6,1,"printf(\"@i=%u\\n\",i)\r\n_break_=1"
@i=5
Watch 1中: BreakPointFlag == 0x06
测试5:
The following breakpoint is invalid, because adding two values (timer.sec and i0) does not result in a memory type:
>>BS WRITE time.sec + i0 /* Invalid expression */
Because : Only a few operators (&, &&, <, <=, >, >=, ==, and !=) are allowed in expressions.
3.2 条件断点
条件断点测试:
不支持条件断点,尽量用访问断点的调试4来代替。
4 BreakAccess
BreakAccess命令允许您使用地址范围定义访问断点。
Syntax |
Description |
BA READ exp, len, cnt, "cmd" |
Set a read access breakpoint. |
BA WRITE exp, len, cnt, "cmd" |
Set a write access breakpoint. |
BA READWRITE exp, len, cnt, "cmd" |
Set a read/write access breakpoint. |
- len is an expression that specifies the memory range in which the breakpoint triggers.
由于使用方法与BreakSet的访问断点模式类似,故这里就不再进行测试。
5 参考资料
[1]http://www.keil.com/support/man/docs/uv4/uv4_cm_breakset.htm
[2] Keil调试笔记:调试表达式(翻译)https://blog.csdn.net/NoDistanceY/article/details/104229096