みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

Yocto gdbserverでリモートデバッグ

はじめに

gdbserverによるリモートデバッグ環境を今まで真面目に作ったことがなかったので調べてみた。

詳細な手順はここで紹介されている。

ターゲットはラズベリーパイ3。

この方法は、ターゲットにインストールされている既存のアプリケーションのデバッグに役立つ。

環境構築

いつものやつ。

INITはsystemdで。

ソース取得

$ mkdir -p rpi-thud/layers
$ cd rpi-thud/layers
$ git clone git://git.yoctoproject.org/poky.git -b thud
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b thud
$ git clone git://git.openembedded.org/meta-openembedded -b thud
$ cd ../

環境変数設定

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi/

local.confの修正

local.confを次のように修正する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart(optional)
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# debugfs
IMAGE_GEN_DEBUGFS = "1"
IMAGE_FSTYPES_DEBUGFS = "tar.bz2"

# gdbserver
IMAGE_INSTALL_append = " gdbserver"

# debug build
DEBUG_BUILD = "1"

# network 
IMAGE_INSTALL_append = " connman connman-client"
EXTRA_IMAGE_FEATURES += "ssh-server-openssh"

ビルド

$ bitbake core-image-base
$ bitbake core-image-base -c populate_sdk

SDKのインストール

できあがったpoky-glibc-x86_64-core-image-base-cortexa7t2hf-neon-vfpv4-toolchain-2.6.2.shを実行しSDKをインストールする。

$ tmp/deploy/sdk/poky-glibc-x86_64-core-image-base-cortexa7t2hf-neon-vfpv4-toolchain-2.6.2.sh

ターゲットを起動する

できあがったcore-image-base-raspberrypi3.rpi-sdimgをマイクロSDカードに書き込み、ラズベリーパイ3を起動する。

$ sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M

/dev/sdXは環境に応じてsdbsdcに変更する。

debugfsの設定

デバッグに必要なシンボルファイルなどを完全に含んだsysrootを作成するには、debugfsを使用する必要がある。 ここでいうdebugfsカーネル機能のdebugfsとは異なり、デバッグ情報を含んだファイルシステムイメージのことを指す。

debugfsを生成するにはlocal.confに次の行を追記する。

IMAGE_GEN_DEBUGFS = "1"
IMAGE_FSTYPES_DEBUGFS = "tar.bz2"

debugfsはデバッグ情報のみを含んだいわばフラグメントなので、単体で使用しても意味がない。 使用するには次のようにする。

$ mkdir debugfs
$ cd debugfs
$ tar xvf ../build/tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.tar.bz2
$ tar xvf ../build/tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3-dbg.tar.bz2

populate_sdkで作成したツールチェインをインストールした場所にもデバッグ情報を含んだsysrootが存在するが、 ターゲット上のすべてのファイルが含まれているわけではない。 SDKを使用して新規に開発したアプリケーションをデバッグするのには十分だが、それ以外のターゲット上のアプリケーションをデバッグするときには debugfsを含んだ完全なファイルシステムのイメージが必要となる。

デバッグ手順

次のような手順になる。

  1. ターゲットでgdbserverを起動
  2. ホストPCでgdbを起動

試しにlsコマンドをデバッグしてみる。

ターゲット側

$ gdbserver localhost:1234 /bin/ls

ホストPC側

SDKに含まれるgdbを使用する場合は次のようになる。

$ source /opt/poky/2.6.2/environment-setup-cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi
$ cd debugfs/../ #debugfsの1つ上の階層
$ arm-poky-linux-gnueabi-gdb
(gdb) set sysroot debugfs
(gdb) set substitute-path /usr/src/debug debugfs/usr/src/debug
(gdb) target remote ターゲットのIPアドレス:1234

この時点からデバッグ可能となる。

次のようにデバッグする。

(gdb) b ls_main
(gdb) c 

これで(busyboxでの)lsコマンドのエントリポイントでブレークする。

gdbgui

gdbguiを使用する場合は次のようにする。

$ gdbgui -g arm-poky-linux-gnueabi-gdb

デバッグビルドについて

強い最適化がかかっている場合だと、シンボルデバッグ時におかしな挙動に見えることがある。 DEBUG_BUILDを有効化すると、bitbake実行時の最適化を抑えることができる。

local.confに次の行を追加する。

DEBUG_BUILD = "1"

これがない場合はTARGET_CFLAGSは次のようになる。

export TARGET_CFLAGS=" -O2 -pipe -g -feliminate-unused-debug-types -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0=/usr/src/debug/core-image-base/1.0-r0 -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot= -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot-native= "

DEBUG_BUILD= "1"の場合のTARGET_CFLAGSは次のようになる。

export TARGET_CFLAGS=" -O -fno-omit-frame-pointer -g -feliminate-unused-debug-types -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0=/usr/src/debug/core-image-base/1.0-r0 -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot= -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot-native=  -pipe"

-O2-Oになり、-fno-omit-frame-pointerがついている。これによってデバッグ情報が失われないようになっている。

まとめ

SDKを使用して新規に開発したアプリケーションをデバッグするにはSDKに含まれるsysrootのターゲット側のディレクトリで間に合うが、 既存のアプリケーションや、実機環境に近い状態でインテグレートした後のアプリケーションをデバッグする際には、 debugfsを使用する必要があることがわかった。