みつきんのメモ

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

ラズベリーパイ3でYocto Project 3.0(Zeus)を試す

はじめに

Yocto Project 3.0(Zeus)がリリースされていたのでラズベリーパイ3で動かす。

構築手順

ソース取得

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

環境変数設定

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

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

ツールの説明

今回のバージョンからbitbake以外のツールに関する説明が表示されるようになった。

Other commonly useful commands are:
 - 'devtool' and 'recipetool' handle common recipe tasks
 - 'bitbake-layers' handles common layer tasks
 - 'oe-pkgdata-util' handles common target package tasks

oe-pkgdata-utilははじめて見る。

パッケージのバージョンやファイルの一覧などを見ることができるっぽい。

レイヤ追加

$ 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の修正

MACHINEをraspberrypi3に設定し、UARTを有効化する。

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

# enable uart
ENABLE_UART = "1"

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

# connman
IMAGE_INSTALL_append = " connman \
                 connman-client \
"

ビルド

core-image-baseをビルドする。

$ bitbake core-image-base

書き込み

ddやEtcherで書き込む

ddの場合は次のようにする。

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

/dev/sdXはSDのデバイスに適宜読み替え。

実行

zeusが起動した。

# cat /etc/os-release
ID="poky"
NAME="Poky (Yocto Project Reference Distro)"
VERSION="3.0 (zeus)"
VERSION_ID="3.0"
PRETTY_NAME="Poky (Yocto Project Reference Distro) 3.0 (zeus)"

まとめ

zeusも問題なく立ち上がった。

ベアメタルでHiFIve1 Rev.Bを動かす

はじめに

PlatformIOでHiFive1 Rev.Bを動かすで下記のように書いたので ベアメタルで動かしてみる。

標準のライブラリやヘッダは使っていないので、リンカスクリプトとスタートアップを書けば、 ベアメタルでもこのまま動くはず。

リンカスクリプト

リンカスクリプトlinker.ldとして次の内容で作成する。

MEMORY
{
    rom : ORIGIN = 0x20010000, LENGTH = 0x6a120
    ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}

ENTRY(start)

SECTIONS
{
    .text : {*(.text*)} > rom
    .rodata : {*(.rodata*)} > rom
    .bss : {*(.bss*)} > ram
}

スタートアップ

utils.Sにスタートアップのコードを追加する。

    .global start
start:
    lui sp, 0x80004
    jal main
    j .
    
    .global dummy
dummy:
    ret

    .global put32
put32:
    sw x11,(x10)
    ret

Makefile

Makefileを次の内容で作成する。

RISCVGNU = riscv32-unknown-elf

AOPS = -march=rv32imac
COPS = -march=rv32imac -Wall -O0 -g  -nostdlib -nostartfiles -ffreestanding 
#COPS = -march=rv32imac -Wall -O2  -nostdlib -nostartfiles -ffreestanding

all : blinker.hex

clean :
    rm -f *.o
    rm -f *.elf
    rm -f *.hex
    rm -f *.list
    rm -f *~

utils.o : utils.S
    $(RISCVGNU)-as $(AOPS) utils.S -o utils.o

led.o : led.c
    $(RISCVGNU)-gcc $(COPS) -c led.c -o led.o

blinker.hex : linker.ld utils.o led.o 
    $(RISCVGNU)-ld utils.o led.o -T linker.ld -o blinker.elf
    $(RISCVGNU)-objdump -D blinker.elf > blinker.list
    $(RISCVGNU)-objcopy blinker.elf -O ihex blinker.hex

flash : blinker.hex
    JLinkExe -device FE310 -speed 1000 -if JTAG -jtagconf -1,-1 -autoconnect 1 -CommanderScript "upload.jlink"

Cプログラム

led.cの内容はそのままなので省略。

JLinkコマンドファイル

ターゲットへの書き込みにupload.jlinkとして次の内容でファイルを作成する。

h
loadfile ./blinker.hex
r
q

ツールチェイン

ツールチェインはRISC-Vのベアメタル入門(自分用)を参照。

J-Linkのツール

SEGGERのサイトからパッケージをダウンロードする。

PlatformIOのものを使う場合には.bashrcに次のように追加してPATHを通す。

export PATH="${HOME}/.platformio/packages/tool-jlink:${PATH}"

書き込み

Makefileに次の部分を仕込んだのでmakeコマンドで書き込むことができる。

flash : blinker.hex
    JLinkExe -device FE310 -speed 1000 -if JTAG -jtagconf -1,-1 -autoconnect 1 -CommanderScript "upload.jlink"

書き込みにはJLinkExeを使用するのでJ-Linkのツールが必要となる。

次のようにしてプログラムを書き込む。

$ make flash

まとめ

ベアメタルでもLチカができた。

PlatformIOでHiFive1 Rev.Bをデバッグする

はじめに

前回はPlatformIOでLチカの実行までしたので、デバッグ方法調べる。

pio debug

特にplatform.iniを書き換えたりする必要もなく次のコマンドでデバッグを開始できる。

$ pio debug --interface=gdb
Warning! Please install `99-platformio-udev.rules`. 
Mode details: https://docs.platformio.org/en/latest/faq.html#platformio-udev-rules

SEGGER J-Link GDB Server V6.52 Command Line Version

JLinkARM.dll V6.52 (DLL compiled Sep 27 2019 17:53:31)

Command line: -singlerun -if JTAG -select USB -speed 1000 -jtagconf -1,-1 -device FE310 -port 2331
-----GDB Server start settings-----
GDBInit file:                  none
GDB Server Listening port:     2331
SWO raw output listening port: 2332
Terminal I/O port:             2333
Accept remote connection:      yes
Generate logfile:              off
Verify download:               off
Init regs on start:            off
Silent mode:                   off
Single run mode:               on
Target connection timeout:     0 ms
------J-Link related settings------
J-Link Host interface:         USB
J-Link script:                 none
J-Link settings file:          none
------Target related settings------
Target device:                 FE310
Target interface:              JTAG
Target interface speed:        1000kHz
Target endian:                 little

Connecting to J-Link...
Reading symbols from /home/mickey/work/pio/hifive1_revb/.pio/build/hifive1-revb/firmware.elf...
(gdb) J-Link is connected.
Firmware: J-Link OB-K22-SiFive compiled Aug 22 2019 14:12:56
Hardware: V1.00
S/N: 979001361
Checking target voltage...
Target voltage: 3.30 V
Listening on TCP/IP port 2331
Connecting to target...
J-Link found 1 JTAG device, Total IRLen = 5
JTAG ID: 0x20000913 (RISC-V)
Connected to target
Waiting for GDB connection...

ここまで表示されたあと、Enterを押すとgdbのプロンプトが表示される。

次のコマンドでターゲットに接続すればデバッグが可能になる。

(gdb) target remote :2331
(gdb) mon reset
(gdb) load

たとえば、前回のプログラムであれば次のような感じ。

(gdb) l
18      put32(GPIO_OUTPUT_EN, LED_RGB);
19      put32(GPIO_OUT_XOR, LED_RGB);
20      put32(GPIO_PORT, LED_RGB);
21  
22      while (1) {
23          put32(GPIO_PORT, LED_RGB);
24          for (rx = 0; rx < 2000000; rx++) dummy(rx);
25          put32(GPIO_PORT, 0);
26          for (rx = 0; rx < 2000000; rx++) dummy(rx);
27      }

ブレークポイントを貼る。

(gdb) b 23

実行状態にする。

(gdb) c

main.cの23行目の部分でブレークすればOK。

99-platformio-udev.rulesの警告

実行時に下記の警告が表示されることがあるが、問題なくターゲットと接続できている場合は無視して良い。

Warning! Please install `99-platformio-udev.rules`. 
Mode details: https://docs.platformio.org/en/latest/faq.html#platformio-udev-rules

ターゲットとうまく接続できない場合はudevのルールをインストールすると良い。 詳細はメッセージにあるURLに書いてあるとおり。

$ curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules
$ sudo service udev restart

まとめ

PlatformIOではデバッグが簡単に開始できる。

PlatformIOでHiFive1 Rev.Bを動かす

はじめに

HiFive Rev.Bを入手したのでまずはPlatformIOでLチカする。

プロジェクトの作成

次のコマンドを実行し、PlatformIOのプロジェクトを作成する。

$ platformio init --board hifive1-revb

無印とはブートアドレスが異なるので、ボードもhifive1-revbを使用する。

Lチカのコード

基本的なコードはここのblinker01を参考にしている。

レジスタアクセス

レジスタにアクセスするためにマクロを作成しておく。

utils.Sを次の内容で作成

    .global dummy
dummy:
    ret

    .global put32
put32:
    sw x11,(x10)
    ret

led.cを次の内容で作成する。

void put32(unsigned int, unsigned int);
void dummy (unsigned int);

#define GPIOBASE         0x10012000
#define GPIO_OUTPUT_EN     (GPIOBASE+0x08)
#define GPIO_PORT      (GPIOBASE+0x0c)
#define GPIO_OUT_XOR   (GPIOBASE+0x40)

#define LED_RED (1<<22)
#define LED_BLUE (1<<21)
#define LED_GREEN (1<<19)
#define LED_RGB (LED_RED|LED_GREEN|LED_BLUE)

int main(int argc, char *argv[])
{
    unsigned int rx;

    put32(GPIO_OUTPUT_EN, LED_RGB);
    put32(GPIO_OUT_XOR, LED_RGB);
    put32(GPIO_PORT, LED_RGB);

    while (1) {
        put32(GPIO_PORT, LED_RGB);
        for (rx = 0; rx < 2000000; rx++) dummy(rx);
        put32(GPIO_PORT, 0);
        for (rx = 0; rx < 2000000; rx++) dummy(rx);
    }
    return 0;
}

標準のライブラリやヘッダは使っていないので、リンカスクリプトとスタートアップを書けば、 ベアメタルでもこのまま動くはず。

ビルド

次のコマンドでビルド

$ platformio run

これは次のように短く書ける

$ pio run

書き込み

次のコマンドで書き込み

$ platformio run --target upload

次のように短く書ける

$ pio run -t upload

ラズベリーパイ3の(ソフトウェア)PWMで回転サーボを動かす

はじめに

秋月電子でも入手可能な回転サーボ FS90R を入手したので動かしてみる。

環境はwarrior(Yocto 2.7)でmeta-raspberrypiを使用する。

環境構築

ここの「環境構築」を参照。

local.confに下記の内容を追加する。

IMAGE_INSTALL_append = " rpi-gpio"

core-image-baseを作成する。

$ bitbake core-image-base

ddやEtcherなどでマイクロSDに書き込む。

配線

FS90R側の信号は次のようになる。

信号
オレンジ Signal(PWM)
+(V)
-(GND)

駆動電圧は最低4.8Vなので、5V入れてやれば動く。 制御用の電圧は3.3Vでも問題ない様子。

ラズベリーパイの5VのピンはUSBからの電流を分配しているだけっぽいので、 手っ取り早くやるなら、+のところはラズベリーパイの5Vに接続してしまっても問題ないと思う。 ただし、本体の電源そのものなので、サーボを回すと高頻度で次のように電源低下を検出ようになる。

[ 1747.351869] Under-voltage detected! (0x00050005) 

それが気になる場合は、別系統で電源を引っ張ってきてモーターに入れると良い。

筆者は、USB-シリアル変換ケーブルのPCのUSBから出ている5Vをモーターに入れた。 (ラズベリーパイにコンセントの電源をひっぱているので、普段この5Vは余っている)

プログラム

PWMを制御したいので手っ取り早くpythonで書く。GPIOの制御にはRPi.GPIOを使用する。 RPi.GPIOはソフトウェアでPWMをシミュレートすることができ、普通のGPIOピンでもPWMすることができる。

ただし、ハードウェアPWMと比べると制度が低いので、正確なパルス幅が必要な場合は注意が必要となる。

Duty比

パルス幅のレンジは700〜2300usで、1500usで停止。これを境に小さいと時計回り、大きいと半時計回りとなる。

動作 パルス幅(us)
停止 1500
時計回り 1500未満
半時計回り 1501以上

RPi.GPIOのPWMではベースとなる周波数とDuty比でパルス幅を指定する。

例えば50Hzの場合、1回の振幅に20msかかるので、1500usのパルス幅を指定したい場合次のように計算する。

1500us = 1500us/(20ms*1000)*100 = 7.5%

停止させておくために設定するDutyは7.5%ということになる。

次のプログラムは制御信号にGPIO21を使用して次の動作を行う。

  • 2秒間停止
  • 2秒間時計回りで回転
  • 2秒間半時計回りで回転
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

gp_out = 21
GPIO.setup(gp_out, GPIO.OUT)
servo = GPIO.PWM(gp_out, 50)
servo.start(7.5)
time.sleep(2)
servo.ChangeDutyCycle(5)
time.sleep(2)
servo.ChangeDutyCycle(10)
time.sleep(2)
servo.stop()
GPIO.cleanup() 

しかし、実際に動かしてみるとピッタリと停止しないことがわかる。 これは先述の通りソフトウェアPWMでありパルス幅が正確ではないことが原因とななる。

なので、7.5の時点でどちらに回転しているかを見て値を微調整する。 筆者の環境では7.1でピッタリと停止した。

まとめ

サーボの電源は本体と別系統が無難。

ソフトウェアPWMでも、値を微調整すれば回転サーボの制御は可能。 ただし正確に制御したい場合はハードウェアPWMのほうが確実。

ざっくりとALSAとPulseAudioの関係(の続きのようなもの)

はじめに

以前に書いた2015-04-04 ざっくりとALSAとPulseAudioの関係が未だにアクセスがあるようなのでちょっと振り返ってみる。

基本的な理解としては、

  1. 最終的に音を鳴らすのはデバイス
  2. バイスを叩くためにデバイスドライバ(カーネルランド)
  3. サウンド関連でカーネルランドとユーザーランド橋渡しをする部分を取りまとめているのがALSA
  4. 各アプリケーション間でサウンド関連のリソースを調停するのがPulseAudioなどのサウンドサーバ

これは変わっていない。

先述の記事では次のように書いた。

それを回避するためにPulseAudioなどのサウンドサーバはALSAのデバイスのふりをして、ALSAAPIを直接叩いているアプリケーションのサウンドリソースを横取りしてしまう。 そうして、管理下に置いたあと本物のALSAに音を流す。

ここで言う本物のALSAとは、カーネルランドにいるALSAのドライバフレームワークを主に示している。

実験環境

確認にはラズベリーパイ3 Model Bを使用。 OSはYocto Projectで作成したものを使用。(warrior/2.7)

ホストPCの環境はUbuntu 18.04。

ほんとに横取りしているのか

これを簡単に確認するにはPulseAudioが動く環境でaplayを実行してみるのがわかりやすい。

Yocto環境の設定

aplayとPulseAudioが動く環境を作るためにlocal.confに次の行を追加する。

# userland ALSA
IMAGE_INSTALL_append = " alsa-utils"

# pulseaudio
IMAGE_INSTALL_append = " pulseaudio-server"

# Enable onboard audio
RPI_EXTRA_CONFIG = ' \n \
# Enable audio (loads snd_bcm2835) \n \
dtparam=audio=on \n \
'

RPI_EXTRA_CONFIGでは、オンボードサウンドバイスを有効化している。

bitbakeでcore-image-baseを作成し、そのイメージでラズベリーパイを起動する。

横取りの確認

sshなどを使用して、ラズベリーパイのターミナルを2つ立ち上げる。

片方でpulseaudioを起動する。

$ pulseaudio --verbose

そしてもう片方でaplayを実行する。

$ aplay /usr/share/sounds/alsa/Front_Left.wav

PulseAudioのターミナルの表示が次のようになり、aplayを実行することでPulseAudioが動作していることがわかる。

I: [pulseaudio] main.c: Daemon startup complete.
I: [pulseaudio] module-suspend-on-idle.c: Sink alsa_output.platform-soc_audio.analog-mono idle for too long, suspending ...
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Device suspended...
I: [pulseaudio] client.c: Created 0 "Native client (UNIX socket client)"
I: [pulseaudio] protocol-native.c: Got credentials: uid=0 gid=0 success=1
I: [pulseaudio] sink-input.c: Trying to change sample rate
I: [pulseaudio] alsa-sink.c: Updating rate for device hw:0, new rate is 48000
I: [pulseaudio] source.c: Changed sampling rate successfully
I: [pulseaudio] sink.c: Changed format successfully
I: [pulseaudio] sink-input.c: Rate changed to 48000 Hz
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Trying resume...
I: [alsa-sink-bcm2835 ALSA] alsa-util.c: Cannot disable ALSA period wakeups
I: [alsa-sink-bcm2835 ALSA] alsa-util.c: ALSA period wakeups were not disabled
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Time scheduling watermark is 36.75ms
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Resumed successfully...
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Starting playback.
I: [pulseaudio] sink-input.c: Created input 0 "ALSA Playback" on alsa_output.platform-soc_audio.analog-mono with sample spec s16le 1ch 48000Hz and channel map mono
I: [pulseaudio] sink-input.c:     media.name = "ALSA Playback"
I: [pulseaudio] sink-input.c:     application.name = "ALSA plug-in [aplay]"
I: [pulseaudio] sink-input.c:     native-protocol.peer = "UNIX socket client"
I: [pulseaudio] sink-input.c:     native-protocol.version = "32"
I: [pulseaudio] sink-input.c:     application.process.id = "321"
I: [pulseaudio] sink-input.c:     application.process.user = "root"
I: [pulseaudio] sink-input.c:     application.process.host = "raspberrypi3"
I: [pulseaudio] sink-input.c:     application.process.binary = "aplay"
I: [pulseaudio] sink-input.c:     application.language = "C"
I: [pulseaudio] sink-input.c:     application.process.machine_id = "e08cd0ccf41446ecb446dbd2a097d362"
I: [pulseaudio] sink-input.c:     application.process.session_id = "c1"
I: [pulseaudio] sink-input.c:     module-stream-restore.id = "sink-input-by-application-name:ALSA plug-in [aplay]"
I: [pulseaudio] protocol-native.c: Requested tlength=500.00 ms, minreq=125.00 ms
I: [pulseaudio] protocol-native.c: Final latency 625.00 ms = 250.00 ms + 2*125.00 ms + 125.00 ms
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [pulseaudio] sink-input.c: Freeing input 0 "ALSA Playback"
I: [pulseaudio] client.c: Freed 0 "ALSA plug-in [aplay]"
I: [pulseaudio] protocol-native.c: Connection died.
I: [pulseaudio] module-suspend-on-idle.c: Sink alsa_output.platform-soc_audio.analog-mono idle for too long, suspending ...
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Device suspended...

PulseAudioはフォアグラウンドで実行している時、一定時間クライアントから接続がないと終了してしまうので注意が必要。

callgrindで見てみる

実際のところ、どのようになっているのかをcallgrindで見てみる。

$ valgrind --tool=callgrind aplay /usr/share/sounds/alsa/Front_Left.wav

で作成したコールグラフをホストPCのkcachegrindで開く。

画面左上のリストからaplayを選択し、左下のリストでmainを選択する。

画面右下の方にあるCall Graphのタブをクリックしコールグラフを開く。

するとaplayのメイン関数に注目したコールグラフが表示される。

しかし肝心な部分が<cycle 3>としてまとめられてしまっていて細かいことを読み取ることができない。

f:id:mickey_happygolucky:20190830124314p:plain
aplayのコールグラフ

ツールバーにあるCycle Detectionをクリックし、cycle検出機能をオフにすると、<cycle 3>の内容が展開される。

f:id:mickey_happygolucky:20190830124323p:plain
<cycle 3>の展開

main'2からsnd_pcm_openが呼び出されていることがわかる。

コールグラフ中の関数をダブルクリックすると、注目する関数を切り替えることができる。その時に注目している関数がどのライブラリ(ELF Object)に属しているかは画面左上のリストの選択状態でわかる。

このようにして関数を調べていくと、ALSA APIのインターフェースであるlibasound.so.2.0.0ioplug_priv_transfer_areas関数からPulseAudioのモジュールであるlibasound_module_pcm_pulse.sopulse_start関数が呼び出されていることがわかった。

f:id:mickey_happygolucky:20190830124337p:plain
libasoundからPulseAudioの呼び出し

つまり、アプリケーションからALSAAPIを呼び出すと内部からpulseaudioのモジュールが呼び出されているということになる。

まとめ

aplayやcallgrindをつかってユーザー空間でのALSA APIの挙動を調べたところ、PulseAudioがALSAの処理を横取りしていることが確認できた。

f:id:mickey_happygolucky:20150404105017p:plain
横取りの図式

gstreamerでalsasinkを使う場合などでもcallgrindでみると同じようになっていることが確認できる。

PulseAudioからカーネルALSAフレームワークへの流れはまた機会があれば。

Yocto環境 callgrindでコールグラフを作成する

はじめに

Linuxのユーザー空間のアプリをデバッグする際に、関数が実際にどのように呼び出されているのかを見たいことがある。 valgrindにはcallgrindというツールがあり、これを使うとコールグラフを生成することができる。

ターゲットにRPi3 Model Bを使用して、warrior(2.7)環境を構築する。 ホスト環境はUbuntu 18.04を使用する。

吐き出したコールグラフのデータはkcachegrindというツールで可視化することができる。 実機環境はヘッドレスで運用することとして、kcachegrindはホストPC側で使用する。

kcachegrindのインストール

Ubuntu環境にkcachegrindをインストールする。

$ sudo apt install valgrind kcachegrind

余談だが、kcachegrindの"k"はkernelではなくてKDEの”k”らしい。

環境構築

Yoctoの環境を構築する。

ソース取得

$ mkdir -p rpi-warrior/layers
$ cd rpi-warrior/layers
$ git clone git://git.yoctoproject.org/poky.git -b warrior
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b warrior
$ git clone git://git.openembedded.org/meta-openembedded -b warrior
$ 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

RPi3向けの基本的な設定。

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

# enable uart
ENABLE_UART = "1"

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

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

valgrindのための設定

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

# valgrind
IMAGE_INSTALL_append = " valgrind"
EXTRA_IMAGE_FEATURES += "dbg-pkgs"

実行環境にデバッグシンボルをインストールするためにIMAGE_FEATURESにdbg-pkgsを追加する。 これをしないと、コールグラフ中の関数名が16進数の文字列になってしまう。

ちなみにvalgrindは次のようにしても追加できる。

#  "tools-profile"  - add profiling tools (oprofile, lttng, valgrind)
EXTRA_IMAGE_FEATURES += "tools-profile"

ビルド

とりあえず最小構成をビルドする。

$ bitbake core-image-base

出来上がったイメージをddなどでSDカードに書き込む。

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

sdXは環境によりsdbやsdcなどになる。

lsのコールグラフ

lsコマンドのコールグラフを作ってみる。

$ valgrind --tool=callgrind ls

callgrind.out.xxxというファイルが生成される。xxxはプロセスIDが入る。

これをホストPCにコピーして次のようにする。

$ kcachegrind callgrind.out.xxx

f:id:mickey_happygolucky:20190830002744p:plain
kcachegrind

まとめ

Yoctoの環境でもvalgrindを使うことができる。

その時にIMAGE_FEATURESにdbg-pkgsを入れておくと、デバッグ時のシンボルを解決できる。 また、src-pkgsも一緒に入れておくと、gdbなどでのソースレベルデバッグも可能になる。

Yocto環境ではIMAGE_FEATURESにフラグを指定することで、デバッグシンボルの追加をすべてのパッケージに対し行うことができたりするので便利。

straceやgdbをターゲット上で使用したい場合は、IMAGE_FEATURESにtools-debugを追加できる。 このあたりは、local.confのExtra image configuration defaultsにコメントとして記載されているので参考にすると良い。

多くの場合ターゲットはヘッドレスで運用することが想定されるので、valgrindの解析結果のファイルはホストPC側で可視化するとよい。