【ESP32】gaddr2lineを活用したバックトレース分析

この記事は約10分で読めます。

はじめに

ESP32を使ってネコ型おしゃべりロボットを開発中だが、デバイスを初回起動した時に下記エラーが発生するようになった。

C++
Leaving...
Hard resetting via RTS pin...
--- Terminal on /dev/cu.wchusbserial1420 | 115200 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
E (1340) esp_core_dump_flash: No core �f~mp partition found!
E (1340) esp_core_dump_flash: No core dump partition found!
Starting
After Starting - Available heap size: 196328 bytes
{"device":{"id":"test_002","type":"prototype"},"mqtt":{"host":"a351w29x0awoaq-ats.iot.ap-northeast-1.amazonaws.com","port":8883,"credential":"/root.ca.pem","client_cert":"/device.cert.pem","private_key":"/device.private.key"},"resource":{"voices":"https://clocky-dev.s3.ap-northeast-1.amazonaws.com/voices"},"runtime":{}}
Connecting to WiFi with SSID and password from build flags...
Connecting to WiFi...
Connecting to WiFi...
Connecting to WiFi...
Connecting to WiFi...
Connecting to WiFi...
Not connected to WiFi
After Wi-Fi Connection - Available heap size: 146928 bytes
Initialise eye objects
Create display #0
Create display #1
Initialise Audio
After Setup Completed - Available heap size: 146436 bytes
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x400dd3c0  PS      : 0x00060730  A0      : 0x800dd467  A1      : 0x3ffd0080  
A2      : 0x3ffd0198  A3      : 0x00000090  A4      : 0x3ffdb674  A5      : 0x00000000  
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x8011cb3f  A9      : 0x3ffd0190  
A10     : 0x00000000  A11     : 0x400ef1dc  A12     : 0x3ffdb774  A13     : 0x3ffec6cc  
A14     : 0x00000000  A15     : 0x00000000  SAR     : 0x00000015  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000100  LBEG    : 0x400911fd  LEND    : 0x4009121f  LCOUNT  : 0xffffffff  


Backtrace: 0x400dd3bd:0x3ffd0080 0x400dd464:0x3ffd00a0 0x400d9282:0x3ffd00c0 0x400d977e:0x3ffd0230 0x400da10b:0x3ffd02f0 0x40265035:0x3ffd0350 0x400d36a2:0x3ffd0370 0x400d3719:0x3ffd0390 0x400d9e6a:0x3ffd03b0 0x4013be45:0x3ffd0400




ELF file SHA256: dbc7e31d9cbb3c3c

E (6283) esp_core_dump_flash: Core dump flash config is corrupted! CRC=0x7bd5c66f instead of 0x0
Rebooting...
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13192
load:0x40080400,len:3028
entry 0x400805e4
E (1340) esp_core_dump_flash: No core �f~mp partition found!
E (1340) esp_core_dump_flash: No core dump partition found!
Starting
After Starting - Available heap size: 196328 bytes

CPUが許可されていない方法でメモリにアクセスしようとしてLoadProhibitedエラーが発生。通常は、割り当てられていないメモリアドレス(0x00000110)に読み書きしようとしたことを意味する。

バックトレース情報(クラッシュ時の関数呼び出しスタック)が記載されているので、クラッシュに至るまでにどの関数が呼び出されたかの経路を辿ることができる。

今回はバックトレースの分析を試みる。

バックトレースの取得

クラッシュ時にデバイスが生成するバックとレースを確認。今回の場合は下記のように記載されている。

C++
Backtrace: 0x400dd661:0x3ffd0080 0x400dd714:0x3ffd00a0 0x400d93fa:0x3ffd00c0 0x400d98f6:0x3ffd0230 0x400da273:0x3ffd02f0 0x402653fd:0x3ffd0350 0x400d369a:0x3ffd0370 0x400d3711:0x3ffd0390 0x400d9fe2:0x3ffd03b0 0x4013c20d:0x3ffd0400

シンボルの解決

バックトレースには関数のアドレスが含まれているが、これらのアドレスを人が読める関数名に変換する必要がある。このプロセスを、「シンボルの解決」と呼ぶ。

ESP32の場合、addr2lineツール(macOSの場合)を使用して、ELFファイルからアドレスをソースコードの行に変換できる。

ELFファイルとは・保存場所

ELFは「Executable and Linkable Format」の略称で、実行可能ファイル、オブジェクトコード、共有ライブラリ、コアダンプなどを格納するためのファイルフォーマットを指す。

ELFファイルは、プロジェクトのビルドプロセス中に生成される。PlatformIOを使用してESP32で開発している場合、ビルドされたファイル(ELFファイルを含む)は.pioディレクトリ内に格納される。

C++
<プロジェクトディレクトリ>/.pio/build/<環境>/firmware.elf

自分の場合は、下記ディレクトリにfirmware.elfファイルがあった。

addr2lineツールを使用して、ELFファイルをソースコードの行に変換

macOSの場合は、Homebrewを使用してGNU Binutilsをインストール。インストール後、addr2lineコマンドを使用してアドレスを解析できる。Windows OSではMSYS2やCygwinなど、Unix系の環境を提供するツールをインストールする必要がある。

C++
brew install binutils

環境変数の設定 (オプション)

ShellScript
# Bash
export PATH=$PATH:/usr/local/opt/binutils/bin
source ~/.bashrc

#Zsh
export PATH=$PATH:/usr/local/opt/binutils/bin
source ~/zshrc

下記のコマンドを実行して、ELFファイルをソースコードの行に変換する。

C++
addr2line -e your_program.elf -a <アドレス>

ELFファイルが存在するディレクトリに移動

C++
cd /Users/ky/dev/clocky/clocky_platformio/.pio/build/upesy_wrover

次に、バックトレースに含まれる各アドレスに対してaddr2lineコマンドを実行する。 これを、バックトレースに記載されている他のアドレスに対して繰り返す。一気に実行することもできる。

実行すると、エラーになっているコードのファイルと行番号が分かる形で出力された。

ソースコードの確認・デバッグ

gaddr2lineから得られたソースコードの行を確認し、問題が発生している可能性がある部分を特定する。

該当行前後のポインタの使用、メモリアクセス、APIの呼び出し、nullチェックが適切に行われているかなどを重点的にチェックしていく。

ちなみに、この記事を書いた後から知ったのだが、platform.iniファイルにverbose設定で実行すると、バックトレースをする必要なく、エラー行が出力されることがわかった。

CORE_DEBUG_LEVEL はデバッグ出力のレベルを設定する定数で、5は最も詳細な出力を提供する。

ShellScript
; PlatformIO Project Configuration File
; https://docs.platformio.org/page/projectconf.html
[env]
platform = espressif32
framework = arduino
board = esp32dev

[env:debug]
build_flags =
    ${env.build_flags}
    -DDEBUG_BUILD #こちらを設定
    -DCORE_DEBUG_LEVEL=5

上記設定した後に、以下のコマンドでビルドとモニタリングを実行できる。

ShellScript
pio run -e debug -t upload
pio device monitor -e debug

これにより、下記のように、ESP32にプログラムが書き込まれ、シリアルモニターが起動して詳細なデバッグ情報をリアルタイムで確認できる。バックトレースを解析せずにエラー行も特定できる。

コメント

タイトルとURLをコピーしました