systemd環境でのコアダンプ解析
systemd環境下においては、従来(systemd無しの環境)のコアダンプ解析とは勝手が違ってくる。
コアダンプを有効にする
ulimit
コマンドでcore file size
が0になっていないことを確認する。
$ ulimit -a -t: cpu time (seconds) unlimited -f: file size (blocks) unlimited -d: data seg size (kbytes) unlimited -s: stack size (kbytes) 8192 -c: core file size (blocks) unlimited -m: resident set size (kbytes) unlimited -u: processes 63401 -n: file descriptors 1024 -l: locked-in-memory size (kbytes) unlimited -v: address space (kbytes) unlimited -x: file locks unlimited -i: pending signals 63401 -q: bytes in POSIX msg queues 819200 -e: max nice 0 -r: max rt priority 99 -N 15: unlimited
もしも0になっていたら、次のようにしてコアダンプを有効にする。
$ ulimit -c unlimited
ここまでは従来の方法と同じ。
コアダンプの出力場所
コアダンプの出力場所は /proc/sys/kernel/core_pattern を参照するとわかる。 systemdでは、
$ cat /proc/sys/kernel/core_pattern |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %e
となっており、 /var/lib/systemd/coredump/ 以下にLZ4で圧縮されて保存される。
保存されるファイル名は core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4
といったものになる。
コアダンプの確認方法
従来の方法だと、次のコマンドでコアダンプの解析が行える。
$ gdb ./user-app core
systemd環境下で前述の方法と同様に行うと、コアダンプの認識に失敗する。
$ gdb ./user-app /var/lib/systemd/coredump/core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4 GNU gdb (GDB) 8.0.1 Copyright (C) 2017 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./user-app...done. "/var/lib/systemd/coredump/core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4" is not a core dump: File format not recognized
これは /var/lib/systemd/coredump/ 以下のコアダンプファイルがLZ4で圧縮されて保存されているためで、LZ4を展開してやればgdbから認識できるようになる。
$ lz4 -d /var/lib/systemd/coredump/core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4 core $ gdb ./user-app core
しかし、毎回コアダンプを展開するのは手間だ。
そこでsystemdでは coredumpctl
というコマンドが用意されている。
coredumpctl の使い方
保存されているコアダンプ一覧を確認する。
$ coredumpctl list TIME PID UID GID SIG COREFILE EXE Fri 2017-11-24 11:23:30 JST 1242 0 0 11 missing /usr/lib/udisks2/udisksd Sun 2017-11-26 00:12:29 JST 1959 1000 1000 5 missing /usr/bin/ibus-daemon Sun 2017-11-26 00:16:42 JST 10271 1000 1000 5 missing /usr/bin/ibus-daemon Fri 2017-12-15 18:42:18 JST 5242 1000 1000 11 missing /usr/share/franz/franz Tue 2017-12-19 22:49:53 JST 2933 1000 1000 11 missing /opt/dropbox/dropbox Thu 2017-12-21 02:14:13 JST 1723 1000 1000 11 missing /usr/share/franz/franz Fri 2017-12-22 02:04:45 JST 9251 1000 1000 11 missing /usr/lib/electron/electron Sun 2018-01-07 01:25:33 JST 6231 1000 1000 8 present /home/ryo/code/workspace/csfml/user-app
gdbを使ってコアダンプを解析する。例えば、前述のリストから /home/ryo/code/workspace/csfml/user-app の解析をする場合には、PID 6231を指定する。
$ coredumpctl gdb 6231
参考
OpenOCD + ST-LinkでFirmware書き込み
OpenOCDを使ってSTM32F401 Nucleo-64にST-Link経由でFirmwareを書き込む。
OpenOCDとST-Linkドライバのインストール
Arch Linuxなら以下のコマンドでインストールする。
$ pacman -S openocd stlink
Firmwareを書き込む
OpenOCDで指定できるinterface, target, boardの設定ファイルは/usr/share/openocd/scripts
に配置されている。
OpenOCDで接続する
STM32F401 Nucleo-64 boardのUSB接続の場合
$ openocd -f board/st_nucleo_f4.cfg
もしくは、
$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg
STM32F401 Chip + 中華ST-Linkの場合
Nucleoボードを使っているのなら中華ST-Linkを使う必要はないが、試した結果を書き残しておく。
$ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg
なお、Nucleoボード上のSWDコネクタ(CN4)は以下のピンアサインとなっている。
Pin | CN4 |
---|---|
1 | VDD_TARGET |
2 | SWCLK |
3 | GND |
4 | SWDIO |
5 | NRST |
6 | SWO |
※中華ST-Linkでは、一度ST-LinkのFirmwareをアップデートした後に書き込みに成功するようになった。 (書き込みに成功している中華ST-LinkのFW versionはV2J28S7)
telnetで書き込む
上記手順でOpenOCDで接続後、別のterminalからtelnet
を起動する。
$ telnet localhost 4444
telnet
上で以下のコマンドを実行することでFirmwareを書き込む。
> reset halt > flash write_image erase ${elf_file_absolute_path}
telnet無しでFirmwareを書き込む
上記の方法だと、OpenOCDとは別にtelnet
を起動する必要があり、若干煩わしい。
そこで、Firmwareを書き込む手順を記載したOpenOCDのconfigファイルを用意し、OpenOCDだけで書き込みを行えるようにする。
任意の名前で以下の内容のconfigファイルを作成する。 (今回はopenocd.cfgという名前にした)
telnet_port 4444 gdb_port 3333 source [find board/st_nucleo_f4.cfg] init proc flash_elf {elf_file} { reset halt flash write_image erase $elf_file verify_image $elf_file echo "flash write_image ($elf_file) complete" reset exit }
configファイルを作成したら、以下のコマンドで書き込みを実行する。
$ openocd -f openocd.cfg -c "flash_elf ${elf_file_relative_path}"
※OpenOCDのproc
の名前はOpenOCDの既存コマンド名にあるものを使うとエラーになるようだ。
(最初、proc flash {elf_file}
としていたところ、以下のエラーが出て小一時間ほどハマってしまった)
Open On-Chip Debugger 0.10.0 Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD adapter speed: 2000 kHz adapter_nsrst_delay: 100 none separate srst_only separate srst_nogate srst_open_drain connect_deassert_srst Info : Unable to match requested speed 2000 kHz, using 1800 kHz Info : Unable to match requested speed 2000 kHz, using 1800 kHz Info : clock speed 1800 kHz Info : STLINK v2 JTAG v28 API v2 SWIM v18 VID 0x0483 PID 0x374B Info : using stlink api v2 Info : Target voltage: 3.256353 Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints flash Info : Unable to match requested speed 2000 kHz, using 1800 kHz Info : Unable to match requested speed 2000 kHz, using 1800 kHz adapter speed: 1800 kHz target halted due to debug-request, current mode: Thread xPSR: 0x61000000 pc: 0x1fff1048 msp: 0x20002e40 openocd.cfg:13: Error: wrong # args: should be "flash elf_file" in procedure 'flash' at file "openocd.cfg", line 13
ST-LinkのFirmwareアップデート
こちらで公開されているツールを使ってアップデートする。
参考
Rustでクロスコンパイル
x86_64-unknown-linux-gnu
のホスト環境上で、CHIP向けのバイナリをRustでクロスコンパイルすることを目的とする。
概要
rustup target add ${target_triple}
でターゲットのツールチェインを追加- ターゲットのリンカをインストール
- Cargoのconfigファイルにターゲット用のリンカ設定を記述
cargo build --target ${target_triple}
でクロスコンパイル実行
※Rustのツールチェイン管理を行ってくれるツールのrustup
はこちらを参考にインストール済みとする。
環境
ホストのシステム情報。
$ uname -a Linux hostname 4.13.7-1-ARCH #1 SMP PREEMPT Sat Oct 14 20:13:26 CEST 2017 x86_64 GNU/Linux $ rustup show Default host: x86_64-unknown-linux-gnu 1.21.0-x86_64-unknown-linux-gnu (default) rustc 1.21.0 (3b72af97e 2017-10-09)
ターゲットであるCHIPのシステム情報。
chip@chip:~$ uname -a Linux chip 4.4.13-ntc-mlc #1 SMP Thu Nov 3 01:28:54 UTC 2016 armv7l GNU/Linux
ターゲットのツールチェインを追加
rustup target list
で、Rustがサポートするターゲットのリストを確認する。
$ rustup target list | pr -w 90 --columns=2 aarch64-apple-ios mips-unknown-linux-gnu aarch64-linux-android mips-unknown-linux-musl aarch64-unknown-fuchsia mips64-unknown-linux-gnuabi64 aarch64-unknown-linux-gnu mips64el-unknown-linux-gnuabi64 arm-linux-androideabi mipsel-unknown-linux-gnu arm-unknown-linux-gnueabi mipsel-unknown-linux-musl arm-unknown-linux-gnueabihf powerpc-unknown-linux-gnu arm-unknown-linux-musleabi powerpc64-unknown-linux-gnu arm-unknown-linux-musleabihf powerpc64le-unknown-linux-gnu armv7-apple-ios s390x-unknown-linux-gnu armv7-linux-androideabi sparc64-unknown-linux-gnu armv7-unknown-linux-gnueabihf wasm32-unknown-emscripten armv7-unknown-linux-musleabihf x86_64-apple-darwin armv7s-apple-ios x86_64-apple-ios asmjs-unknown-emscripten x86_64-linux-android i386-apple-ios x86_64-pc-windows-gnu i586-pc-windows-msvc x86_64-pc-windows-msvc i586-unknown-linux-gnu x86_64-rumprun-netbsd i686-apple-darwin x86_64-unknown-freebsd i686-linux-android x86_64-unknown-fuchsia i686-pc-windows-gnu x86_64-unknown-linux-gnu (default) i686-pc-windows-msvc x86_64-unknown-linux-musl i686-unknown-freebsd x86_64-unknown-netbsd i686-unknown-linux-gnu x86_64-unknown-redox i686-unknown-linux-musl
armv7-unknown-linux-gnueabihf
が見つかり、RustはCHIPのターゲットをサポートしていることを確認できたため、以下のコマンドでCHIP用のツールチェインを追加する。
$ rustup target add armv7-unknown-linux-gnueabihf
rustup target add
後にrustup show
やrustup target list
を実行してRustのツールチェインの状態を確認すると、armv7-unknown-linux-gnueabihf
が(installed)
になっているのがわかる。
$ rustup show Default host: x86_64-unknown-linux-gnu installed targets for active toolchain -------------------------------------- armv7-unknown-linux-gnueabihf x86_64-unknown-linux-gnu active toolchain ---------------- 1.21.0-x86_64-unknown-linux-gnu (default) rustc 1.21.0 (3b72af97e 2017-10-09) $ rustup target list | pr -w 90 --columns=2 aarch64-apple-ios i686-unknown-linux-musl aarch64-linux-android mips-unknown-linux-gnu aarch64-unknown-fuchsia mips-unknown-linux-musl aarch64-unknown-linux-gnu mips64-unknown-linux-gnuabi64 arm-linux-androideabi mips64el-unknown-linux-gnuabi64 arm-unknown-linux-gnueabi mipsel-unknown-linux-gnu arm-unknown-linux-gnueabihf mipsel-unknown-linux-musl arm-unknown-linux-musleabi powerpc-unknown-linux-gnu arm-unknown-linux-musleabihf powerpc64-unknown-linux-gnu armv7-apple-ios powerpc64le-unknown-linux-gnu armv7-linux-androideabi s390x-unknown-linux-gnu armv7-unknown-linux-gnueabihf (installed) sparc64-unknown-linux-gnu armv7-unknown-linux-musleabihf wasm32-unknown-emscripten armv7s-apple-ios x86_64-apple-darwin asmjs-unknown-emscripten x86_64-apple-ios i386-apple-ios x86_64-linux-android i586-pc-windows-msvc x86_64-pc-windows-gnu i586-unknown-linux-gnu x86_64-pc-windows-msvc i686-apple-darwin x86_64-rumprun-netbsd i686-linux-android x86_64-unknown-freebsd i686-pc-windows-gnu x86_64-unknown-fuchsia i686-pc-windows-msvc x86_64-unknown-linux-gnu (default) i686-unknown-freebsd x86_64-unknown-linux-musl i686-unknown-linux-gnu x86_64-unknown-netbsd
ターゲットのリンカをインストール
Rustのツールチェインではターゲットのリンカはサポートしていないため、別途用意する。
$ apt-get install gcc-arm-linux-gnueabihf
Cargoのconfigにターゲットのリンカ設定を記述
クロスコンパイルする対象とするプロジェクトを生成する。
$ cargo new --bin hello
Cargoが生成したスケルトンコードのままで実行すると、Hello, world!
が標準出力に出力される。
$ cargo run Compiling hello v0.1.0 (file:///host_shared/hello) Finished dev [unoptimized + debuginfo] target(s) in 0.44 secs Running `target/debug/hello` Hello, world!
これをそのままターゲット(CHIP)上で動作するようにするために、プロジェクトのconfigファイルを作成し、ターゲットのリンカの設定を記述する。
$ mkdir .cargo $ cat > .cargo/config << EOF > [target.armv7-unknown-linux-gnueabihf] > linker = "arm-linux-gnueabihf-gcc" > EOF
クロスコンパイル実行
$ cargo build --target armv7-unknown-linux-gnueabihf
ビルドされたバイナリの情報を確認すると、ターゲット(CHIP)向けのバイナリになっていることがわかる。
$ file target/armv7-unknown-linux-gnueabihf/debug/hello target/armv7-unknown-linux-gnueabihf/debug/hello: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=70dfb01aa67d797fc64005ac8cff1dba94a48a34, not stripped $ readelf -A target/armv7-unknown-linux-gnueabihf/debug/hello Attribute Section: aeabi File Attributes Tag_CPU_name: "7-A" Tag_CPU_arch: v7 Tag_CPU_arch_profile: Application Tag_ARM_ISA_use: Yes Tag_THUMB_ISA_use: Thumb-2 Tag_FP_arch: VFPv3-D16 Tag_ABI_PCS_GOT_use: GOT-indirect Tag_ABI_PCS_wchar_t: 4 Tag_ABI_FP_rounding: Needed Tag_ABI_FP_denormal: Needed Tag_ABI_FP_exceptions: Needed Tag_ABI_FP_number_model: IEEE 754 Tag_ABI_align_needed: 8-byte Tag_ABI_enum_size: int Tag_ABI_VFP_args: VFP registers Tag_CPU_unaligned_access: v6 Tag_ABI_FP_16bit_format: IEEE 754
クロスコンパイルしたバイナリをターゲット上で実行
chip@chip:~$ ./hello Hello, world!
ターゲット(CHIP)上で実行できることが確認できた。
参考
Rustでのクロスコンパイルに必要な情報が、以下のページに非常にわかりやすく説明されている。
japaric/rust-cross: Everything you need to know about cross compiling Rust programs!
環境構築の検証用にDockerを使う
Rustのクロスコンパイル環境構築の検証にDockerを使い始めた。
開発環境を構築する際、最初は試行錯誤しながら行なうため、さぁ環境が構築ができた、となった時に行なってきた手順のどれが最低限必要なものだったのかがわからなくなることが多い。 必要だと思ってインストールしたものが実は必要なかったり、必要ないと思ったものに実は依存していたりするため、手順に再現性があるのかどうかを検証したくなる。
このようなケースにおいて、いつでもクリーンな環境から手順の確認ができるDockerが非常に便利だったので、以下にRustの開発環境をDockerで立ち上げるまでにやったことをメモとして残しておく。
$ docker --version Docker version 17.10.0-ce, build f4ffd2511c
Docker HubでRustがインストールされたDockerイメージが公開されているのでpullする。
$ docker pull rust
Rustのイメージが取得できたことを確認する。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE rust latest 9578919665db 7 days ago 1.33GB
RustのDockerイメージからコンテナを起動する。
$ docker run -i -t -v ${host_dir_path}:${container_dir_path} --rm rust
docker run
のオプションの意味は以下を参照。
-i
: 標準入力を有効にする-t
: 疑似ターミナルを割り当てる-v ${host_dir_path}:${container_dir_path}
: ホストとコンテナの共有ディレクトリを指定する--rm
: コンテナからExitした時に自動的にコンテナを削除する
共有ディレクトリを指定しているのは、ホストで書いたコードをコンテナ内でビルドし、コンテナでビルドしたファイルをホストから参照するのを容易にするため。
Target Triple
Rustのコンパイラのソースコードの中で使用されていた"Triple"という言葉の意味がわからなかったので調べた。
Target Triplets describe a platform on which code runs and are a core concept in the GNU build system. They contain three fields: the name of the CPU family/model, the vendor, and the operating system name.
サポートするターゲットを表す表現方法を Target Triple
と呼んでいる。
ターゲットの指定が以下の3つのフィールドから成っていることが、Tripleと呼ぶ理由らしい。
- CPU family/model
- Vendor
- OS
具体例としては、以下のようなターゲット指定が挙げられる。(よく見るやつ)
なお、clangのドキュメントでは以下のようにフィールド指定が定義されている。
Cross-compilation using Clang
The triple has the general format
<arch><sub>-<vendor>-<sys>-<abi>
, where: