简单介绍利用 OpenOCD+JTAG 来调试一个demo程序的步骤和说明。
2017.01.18 by chenwu
前提条件:
已经理解并完成《ESP32调试环境搭建》。
步骤
1. 串口线连接好 ESP-WROVER-KIT 和 PC,并将开关调至 ON.
2. 将本目录下的 hello_world_main.c 覆盖 esp-idf/examples/01_hello_world/main/hello_world_main.c 文件
3. 进入example目录(设当前shell 为 shell A):
$ cd ~/esp/esp-idf/examples/01_hello_world
4. 编译程序并烧入ESP32
$ make flash
5. 新建一个shell,并进入 openOCD 目录(设当前shell 为 shell B):
$ cd ~/esp/openocd-esp32
6. OpenOCD和程序通讯:这步 操作之间必须连贯,不能停顿太多。(否则程序会执行到末尾,无法调试)
6.1 将ESP-WROVER-KIT开关从 ON -> OFF -> ON
6.2 shell B 启动openocd:
$ sudo ./src/openocd -s ./tcl -f ./esp32.cfg
or [esp32.cfg 如果更新,则使用下面]
$ cp ../esp-idf/docs/esp32.cfg ./ ; sudo ./src/openocd -s ./tcl -f ./esp32.cfg
7. shell A 启动gdb:
$ xtensa-esp32-elf-gdb -ex 'target remote localhost:3333' ./build/hello-world.elf
shell A端gdb调试一些说明
大概可以仿照如下调试:
b hello_task //设置好断点
display /i $pc //设置好显示下一条汇编
c // 执行到断点处
n // 执行一条源码
n // 执行一条源码
s // 进入函数调用
ni // 执行一条汇编
ni // 执行一条汇编
p i // 输出 i 值
bt // 输出 栈
常用调试命令小结:
断点相关:
b hello_task // 设置hello_task函数处断点
b *hello_task+9 // 设置hello_task后 +9 偏移地址处的汇编为断点(不是hello_task后的第九条汇编)
b +10 // 设置当前行 + 10 行处断点(源码+10行)
b a.c:10 // 设置 a.c文件的第十行(源码)处断点
info breakpoints // 显示断点处信息
b 20 // 设置 当前文件第20行 断点,如果设置 b hello_world_main.c:20 则就是设置hello_world_main.c中第20行断点
b 28 if i == 2 // 条件断点,第28行中,如果i==2,则触发断点
d // 删除所有断点
d 1 // 删除1号断点 : 断点按照顺序编号,1号断点就是第一个断点
watch i // 当 当前行的i值发生改变后,会触发断点
执行相关:
c // 或continue // 执行程序,直到断点处或者该线程结束
q // 退出 gdb
n // 单步执行一条源码,如果遇到一条函数调用,不进入函数
s // 单步执行一条源码,如果遇到一条函数调用,则进入函数
ni // 单步执行一条汇编,如果遇到一条函数调用,不进入函数
si // 单步执行一条汇编,如果遇到一条函数调用,则进入函数
finish // 跳出当前函数
源码相关:
list // 显示当前行前后的源码
list app_main // 显示源码 app_main函数后一段
list 20 // 显示源码的第20行后一段
list 20,40 // 显示 源码的第20-40行
显示相关:
display /i $pc // 执行后,每次单步后会显示下一条汇编指令
p i // 或 print i // 输出变量i的值
bt // 或 where // 显示函数调用栈
bt full // 显示函数调用栈的详细信息
x /3uh buf // 表示从内存地址buf读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示
x /s buf // 显示从buf开始的字符串
whatis i // 显示变量i类型
布局相关:
layout asm // 显示汇编代码
layout regs // 显示寄存器
layout src // 显示源码 ,结合list使用
layout prev // 切换到上一个布局
layout next // 切换到下一个布局
线程和进程:
info threads // 查看线程
thread i // 切换到线程号为 i 的线程
info inferiors // 查看进程
inferior i // 切换到进程号为 i 的进程
mon reset // 重置cpu,程序重新执行
查看其他调试命令:
help all
help
静态调试-反汇编
我们更加方便的调试,我们可以反汇编二进制文件,这样更加清楚的显示所有汇编代码并查找分析编辑。
生成汇编代码步骤:(前提已经编译完成)
1. 进入hello-world目录:
$ cd ~/esp/esp-idf/examples/01_hello_world
2. 拷贝 elf 文件:
$ cp ./build/hello-world.elf ./
3. 生成汇编文件:
$ xtensa-esp32-elf-objdump -S hello-world.elf > compile_info.txt
[4] 我们在gdb调试时候,可以看到执行位置的地址。在汇编文件compile_info.txt中查找该地址,就可以分析。
注意:
1. 运行 sudo ./src/openocd -s ./tcl -f ./esp32.cfg 中如果不能启动,出现如下错误
Error: couldn’t bind tcl to socket: Address already in use
则证明 刚刚启动的 进程未被终止。
解决办法:
a).查看当前活动进程
netstat为显示网络相关信息 a(all:默认显示所有,如果加了其他选项此项不生效) n(number:以数字形式显示) t(仅仅显示tcp连接),p(process:显示该项是由哪个程序建立起来的)
$ sudo netstat -antp
b). 强制杀死它(假设进程号为3560,-9为强制杀死)
$ sudo kill -9 3560
2. make flash失败
确保串口连接正常, ll /dev/ttyUSB*
应该可以看到两个设备 ttyUSB0 ttyUSB1.如果不是,则 重置ESP-WROVER-KIT开关:从 ON-> OFF -> ON 或者 重新插拔串口线。
3. gdb启动后出现如下错误:
0x00000000 in ?? ()
或者 openOCD收到如下错误:
Error: Error allocating memory for 1073433568 threads
解决办法:
重新擦写flash (flash由于上次没有擦除干净)