ArduPilot シミュレーターを gdb でデバッグする方法 (前編)

IDE (Visual Studio や Eclipse などの統合開発環境) のデバッガを利用したことがある方なら、ArduPilot の開発中にステップ実行や変数参照をしたいと思うでしょう。
今回は、シミュレーター (SITL) を gdb を用いてデバッグする方法をご紹介します。

この記事は、Ubuntu 16.04、gdb は下記のバージョンで確認しています。
$ gdb --version
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1

Windows でも gdb は使えますが、問題もあります。
本記事では Windows 環境でのデバッグ方法は取り扱いません。

 

概要

デバッグすることの利点

実行中のプログラムの中で何が起こっているのかを知ることができます。
gdb でデバッグができるようになると下記のことに役立ちます。

  • バグの発見、特定
  • 開発時のデバッグ作業効率化
    (多くの場合 print 文による動作確認より効率的です)
  • ソースコードを理解するための学習
必要な知識

下記の知識と、ソースコードレベルで ArduPilot を理解したいという熱意が必要です。

  • C, C++ で書かれたソースコードを読むことができる
  • デバッガを使用したことがある
  • SITL シミュレーターの実行環境を構築している

SITL の環境構築については ArduPilot 入門第2回をご参照ください。

どんなことができるのか?

色々な機能がありますが、個人的によく利用する機能は下記です。

  • ステップ実行
  • ブレークポイントの設定
  • 変数の参照、編集
  • スタックバックトレースの確認 (関数呼び出し履歴)
  • ソースコードの表示

 

gdb の簡単な使い方

SITL 実行時に gdb を起動

ArduCopter のディレクトリに移動し、SITL 起動時のオプションに -D -G を追加します。

sim_vehicle.py -D -G
-D: ビルド時にデバッグ情報を生成
-G: gdb を起動

sim_vehicle.py の実行手順については ArduPilot 入門第2回をご参照ください。

コンソールやマップなどの他のオプションも合わせて利用できます。
sim_vehicle.py -j4 --console --map -D -G

ビルドが完了してシミュレーターが実行された後、X TERM のアイコンを選択すると gdb のコンソールが表示されます。

※ X TERM (X Window System) のウィンドウは、通常のターミナルと操作性が異なるので初めは戸惑います。例えばコピー&ペーストは、選択範囲を右クリックでコピー、真ん中のボタンをクリックでペーストとなります。ターミナルで操作したい方は、gdb でプロセスを指定してアタッチするとよいでしょう。

コマンドの実行

まずは Ctrl+c でブレークします。

そうすると、コマンドが入力可能な状態になります。

※ ブレークすると、ardupilot のシステムは動作を一時的に停止します。その後は gdb のコマンドを用いてメモリの情報を参照、編集できるようになります。

正常に動作するか、Copter::fast_loop 関数でブレークポイントを設定して、スタックバックトレースを確認してみましょう。
以下のコマンドを順番に実行します。

(gdb) break Copter::fast_loop()
(gdb) continue
(gdb) backtrace

実行例

(gdb) break Copter::fast_loop()
Breakpoint 1 at 0x403cfc: file ../../ArduCopter/ArduCopter.cpp, line 261.

(gdb) continue
Continuing.

Breakpoint 1, Copter::fast_loop (this=0x7a76c0 <copter>)
at ../../ArduCopter/ArduCopter.cpp:261
261 read_AHRS();

(gdb) backtrace
#0 Copter::fast_loop (this=0x7a76c0 <copter>)
at ../../ArduCopter/ArduCopter.cpp:261
#1 0x0000000000403c9f in Copter::loop (this=0x7a76c0 <copter>)
at ../../ArduCopter/ArduCopter.cpp:240
#2 0x00000000004d29b0 in HAL_SITL::run (
this=0x7aed60 <AP_HAL::get_HAL()::hal>, argc=11, argv=0x7fffffffdc28,
callbacks=0x7a76c0 <copter>)
at ../../libraries/AP_HAL_SITL/HAL_SITL_Class.cpp:87
#3 0x0000000000404df1 in main (argc=11, argv=0x7fffffffdc28)
at ../../ArduCopter/ArduCopter.cpp:648

Copter::fast_loop 関数が呼ばれるタイミングでブレークすることができ、その時の関数呼び出しの流れを確認できました。
backtrace で出力したスタックの上2行を見てみます。

#0 Copter::fast_loop (this=0x7a76c0 <copter>)
at ../../ArduCopter/ArduCopter.cpp:261

これは、プログラムが ArduCopter.cpp ファイルの Copter::fast_loop 関数 の 261行目 を実行中であることを示します。
そして、fast_loop 関数がどこから呼び出されたのかというのは #1 に書かれているように、ArduCopter.cpp ファイルの 240行目 (Copter::loop 関数内) になります。

#1 0x0000000000403c9f in Copter::loop (this=0x7a76c0 <copter>)
at ../../ArduCopter/ArduCopter.cpp:240

処理の流れを追うときには、ソースコードを眺めるよりも、デバッグをしてスタックを見ると一目瞭然です。

終了する場合には、SITL を Ctrl + c で終了させます。(gdb のみを終了させる場合には、gdb のウィンドウでブレークした後「quit」コマンドを入力)

IDE などの GUI によりデバッグをしてきた方にとっては、gdb はコマンドを覚える必要があるため最初は大変かもしれません。
次回はブレークポイントの設定、変数の参照、ステップ実行という基本的なデバッグの流れを実践するための gdb コマンドをご紹介します。

 

 

著者紹介

yamaguchi_picture山口達也
ドローンソフトウェアエンジニア養成塾 第1期卒業生。

記事の誤りを見つけた方は下記のメールアドレスまでご連絡ください。
techblog@drone-j.com
(週末にメールを確認していますので、対応が遅くなること、休みの日にメールを返信させていただくこと、ご了承ください。)