ESP32 编译和调试

时间:2020-12-27 02:16:44

简单介绍利用 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由于上次没有擦除干净)