2019年3月31日日曜日

jdb でリモートデバッグを行う

貰った Java プログラムが不可解な動きをするので、それを追うために jdb を使うことにした。

幸いにも今までそのレベルの問題に遭遇したことが無かったため、 jdb の使い方自体知らない。

勉強したのでその内容を記す。

環境

  • OS: Windows 10 Pro
  • Java: java version "1.8.0_201"

Java プログラム

テスト用にこんなプログラムを用意。

/**
 * Main
 */
public class Main {
    public static void main(String[] args) {
        String message = getMessage();
        System.out.printf("Hello, %s!\n", message);
    }

    private static String getMessage() {
        return "mikoto2000";
    }
}

コンパイル

-g オプションを付けてコンパイル。

javac -g .\Main.java

Main.class が生成される。

デバッグ

jdb から対象プログラムを直接実行する方法と、 jdb 接続まちしているプログラムに jdb で接続する方法(リモートデバッグ)がある。

jdb から直接実行

jdb の引数にクラスパスとクラス名を渡して実行。

> jdb -classpath . Main
jdbの初期化中...
> stop in Main.main
遅延したブレークポイントMain.main。
クラスがロードされた後に設定されます。
> run
Mainの実行
捕捉されないjava.lang.Throwableの設定
遅延した捕捉されないjava.lang.Throwableの設定
>
VMが開始されました: 遅延したブレークポイントMain.mainの設定

ヒットしたブレークポイント: "スレッド=main", Main.main()、行=6 bci=0
6            String message = getMessage();

main[1] next
>
ステップが完了しました: "スレッド=main", Main.main()、行=7 bci=4
7            System.out.printf("Hello, %s!\n", message);

main[1] cont
> Hello, mikoto2000!

アプリケーションが終了しました

リモートデバッグ

「TCP ポート 1234 で jdb を待ち受けて、 jdb が接続されるまでプログラムをスタートしない」 としようとする場合、次のような手順を実施。

デバッグ対象プログラム実行

以下コマンドでデバッグ対象プログラムを実行する。

java -cp . -agentlib:jdwp=transport=dt_socket,server=y,address=1234 Main
  • agentlib:jdwp: Java Debug Wire Protocol を使用してデバッグしますよと宣言
  • transport=dt_socket: トランスポートに使うライブラリの指定
  • server=y: サーバーを起動して待ち受けますよという宣言
  • address=1234: 1234 番ポートで待ち受けますよと宣言

デバッガ実行

以下コマンドでデバッガを実行する。

jdb -attach 1234

Windows の場合は、 SocketAttach コネクタで接続する必要がある。

jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=1234

あとは、直接プログラムを起動するのと同じですね。

以上。

細かい使い方は参考資料を参照のこと。

参考資料

Debian 上の Vim に typesript-language-server を導入する

typescript-language-server の挙動を検証する必要が出てきたので、環境を構築する。

Debian 最小構成に、 vim と vim-lsp, typescript-language-server 環境を載せていく。

環境

  • OS: Debian buster(debian:buster-slim の docker イメージ)

必要なパッケージをインストール

apt-get update
apt-get -y upgrade
apt-get install -y npm vim git

typesript-language-server インストール

以下のコマンドで実行可能な環境を構築できる。

npm install -g typescript typescript-language-server

typesript-language-server 動作確認

下記コマンドを実行。

typescript-language-server --version

バージョンが出力されれば OK.

vim-lsp のインストール

  • Vim のパッケージ機能を使ってプラグインの管理を行う
  • vim-lsp と async.vim を配置する
  • vim-lsp は使うときだけ有効にしたいので、 opt ディレクトリに入れる

~/.vim/pack/lsp/opt にインストールする場合、 以下のコマンドで実行可能な環境を構築できる。

mkdir -p ~/.vim/pack/lsp/opt
cd ~/.vim/pack/lsp/opt
git clone https://github.com/prabirshrestha/vim-lsp.git
git clone https://github.com/prabirshrestha/async.vim.git

typesript-language-server を使うための設定

~/.vimrc に以下を記述。

syntax on
filetype plugin indent on

""" for typesript development
command! Tsd call StartTypescriptDevelopment()
function! StartTypescriptDevelopment()
    packadd async.vim
    packadd vim-lsp

    let g:lsp_log_verbose = 1
    let g:lsp_log_file = expand('~/vim-lsp.log')

    autocmd BufNewFile,BufRead *.ts set filetype=typescript

    autocmd User lsp_setup call lsp#register_server({
        \ 'name': 'typescript-language-server',
        \ 'cmd': {server_info->[
        \     'typescript-language-server', '--stdio'
        \ ]},
        \ 'whitelist': ['typescript'],
        \ })

    call lsp#enable()
    autocmd FileType typescript setlocal omnifunc=lsp#complete
endfunction

動作確認

vim を開いて :Tsd -> :e test.ts として、typescript を書いていくとオムニ補完に typescript-language-server が使われる。

以上。

2019年3月29日金曜日

Debian に php-language-server を導入する

php-language-server の挙動を検証する必要が出てきたので、環境を構築する。

Debian 最小構成に、 vim と vim-lsp, php-language-server 環境を載せていく。

環境

  • OS: Debian buster(debian:buster-slim の docker イメージ)

必要なパッケージをインストール

php-language-server インストール

~/project/php-language-server にインストールする場合、 以下のコマンドで実行可能な環境を構築できる。

php-language-server 動作確認

下記コマンドを実行。

以下のように出力されれば OK.

Ctrl-C で終了する。

vim-lsp のインストール

  • Vim のパッケージ機能を使ってプラグインの管理を行う
  • vim-lsp と async.vim を配置する
  • vim-lsp は使うときだけ有効にしたいので、 opt ディレクトリに入れる

~/.vim/pack/lsp/opt にインストールする場合、 以下のコマンドで実行可能な環境を構築できる。

php-language-server を使うための設定

~/.vimrc に以下を記述。

syntax on
filetype plugin indent on

""" for php development
command! Phpd call StartPhpDevelopment()
function! StartPhpDevelopment()
    packadd async.vim
    packadd vim-lsp

    let g:lsp_log_verbose = 1
    let g:lsp_log_file = expand('~/vim-lsp.log')

    autocmd User lsp_setup call lsp#register_server({
        \ 'name': 'php-language-server',
        \ 'cmd': {server_info->[
        \     'php', expand('~/project/php-language-server/bin/php-language-server.php')
        \ ]},
        \ 'whitelist': ['php'],
        \ })

    call lsp#enable()
    autocmd FileType php setlocal omnifunc=lsp#complete
endfunction

動作確認

vim を開いて :Phpd -> :e test.php として、php を書いていくとオムニ補完に php-language-server が使われる。

以上。

2019年3月14日木曜日

Xilinx QEMU で Aarch64 ベアメタルの勉強をするための準備をした

Raspberry Pi 3+ をつかったり、 qemu-system-aarch64 を使ったりして、 ほんのちょっとベアメタルに挑戦したけど、仕切り直ししたくなったので、開発環境の構築からやりなおす。

今回は、環境一式そろえるところから、デバッグできることを確認するところまで。

前提環境

  • OS: Windows 10 Pro

構築予定環境を決める

ビルド環境と実行環境を決める。

ツールチェイン選定

Aarch64 のベアメタルに使えるツールチェインはいくつかあるらしい。

  • GNU Toolchain – Arm Developer
    • Aarch64 は、 GNU-A のやつ
  • Downloads - Linaro
    • aarch64-linux-gnu
      • ベアメタル開発も可能
      • 前勉強したときはこのツールチェインを使っていた
    • aarch64-elf
      • 本当はこっちを使うのが正しかったっぽい???(未検証)

それぞれのツールチェインの目的とか特徴とか言えれば良いのだけれど、そんな知識はない。

なんとなく、 GNU Toolchain – Arm Developer を使っていくことにする。

実行環境選定

この前コンパイルしたXilinx/qemu で実行するのがよさそう。

Raspberry Pi 3 も UltraScale+ も Cortex-A53 っぽいし、 Xilinx Quick Emulator User Guide を読めば比較的簡単にベアメタルプログラムの実行ができそうな気がする。

構成検討

慣れ親しんだ環境でやろうということで、以下の構成とする。

  • ビルド環境
    • OS: Windows 10 Pro
    • ツールチェイン: GNU Toolchain – Arm Developer
    • シェル環境: MSYS2
  • 実行環境

という感じでやっていく。

環境構築

GNU Toolchain をインストール

Windows 用バイナリが 32bit 用のものしかないけど問題ないでしょう。

  1. GNU Toolchain – Arm Developer にアクセス
  2. GNU toolchain for A-Profile -> Downloads を選択
  3. gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf.tar.xz をダウンロードして任意の場所に展開
    • この記事では ~/app/gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf に展開
  4. ~/app/gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf/bin にパスを通す

MSYS2 インストール

MSYS2 homepageから、 msys2-x86_64-20180531.exe をダウンロード。

  1. インストール先フォルダ: C:\msys64_aarch64_gnu_toolchain
  2. スタートメニューのショートカット: MSYS2 64bit Aarch64 GNU Toolchain
  3. pacman -Syu して再起動
  4. pacman -Su

ビルドに必要なパッケージをインストール

make をインストール。

pacman -S make

ツールチェインへパスを通す

MSYS2 上で、次のコマンドを実行し、ツールチェインへのパスを通す。 必要であれば .bashrc への登録もする。

export PATH=$PATH:~/app/gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf/bin

プログラムの作成

何もせずに無限ループするプログラムを作成する。

プロジェクトディレクトリ構成

first_step/
    +- Makefile : ビルド手順を書くやつ
    +- boot.S   : プログラム本体
    +- memmap   : リンカスクリプト

boot.S

// ブート時のエントリーポイント
.global start
start:
    // スタックポインタの開始位置指定
    mov sp, #0x80000

    // 何もせず無限ループ
    b halt

// 無限ループ
halt:
    wfe
    b halt

memmap

MEMORY
{
    ram : ORIGIN = 0x80000, LENGTH = 0x1000000
}

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

Makefile

CROSS := aarch64-elf

AS = $(CROSS)-as
LD = $(CROSS)-ld
OBJDUMP = $(CROSS)-objdump
OBJCOPY = $(CROSS)-objcopy

AFLAGS = -g3

all: kernel8.img

boot.o: boot.S
    $(AS) $(AFLAGS) $< -o $@

kernel8.elf : memmap boot.o
    $(LD) boot.o -T memmap -o kernel8.elf

kernel8.img : kernel8.elf
    $(OBJCOPY) $< -O binary kernel8.img

.PHONY: clean
clean:
    rm -rf *.o *.elf *.img

プログラムのビルド

MSYS2 の環境で、プロジェクトディレクトリに移動して make するだけ。 (MSYS2 上での作業)

cd /PATH/TO/first_step
make

ビルドに成功すると、boot.okernel8.elf, kernel8.img が生成される。 QEMU での実行時に必要なのは kernel8.elf

実行とデバッグ

デバッグ用の docker コンテナを立ち上げて、実行とデバッグを行う。

QEMU を GDB 接続待ち状態で起動する

プロジェクトディレクトリをマウントしつつ、デバッグを可能とするオプションを付けてコンテナを起動。 (PowerShell 上での作業)

docker run -it --rm -v "$(pwd):/work" --workdir "/work" --name "work" --cap-add=SYS_PTRACE --security-opt="seccomp=unconfined" mikoto2000/qemu-xilinx

起動したコンテナで、QEMU を実行。

qemu-system-aarch64 -M arm-generic-fdt -nographic -serial mon:stdio -dtb /var/dts/LATEST/SINGLE_ARCH/zcu102-arm.dtb -device loader,file=./kernel8.elf,cpu-num=0 -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4 -s -S
  • -M arm-generic-fdt: デバイスツリーでマシン定義しますよという指定
  • -nographic: GUI 無しで起動
  • -serial mon:stdio: シリアル通信を QEMU モニタに統合
  • -dtb /var/dts/LATEST/SINGLE_ARCH/zcu102-arm.dtb: マシン情報が記載されたファイルを指定
  • -device loader,file=./kernel8.img,cpu-num=0: どのバイナリをどの CPU で実行するかの指定
  • -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4: Cortex-A53 の CPU0 を開始するための設定(レジスタ CRF_APB.RST_FPD_APU の設定)
  • -s: GDB からの接続を、 localhost:1234 で待ち受ける
  • -S: 起動と同時に一時停止する

詳細は Xilinx Quick Emulator User Guide 参照。

QEMU に GDB で接続する

同じコンテナに接続し、 GDB で QEMU に接続する。 (PowerShell 上での作業)

docker exec -it work /bin/bash

GDB で QEMU に接続。

gdb-multiarch -q --eval-command="target remote localhost:1234" ./kernel8.elf
  • -q: ロゴを表示しない
  • --eval-command="target remote localhost:1234": GDB 起動後に実行するコマンド
    • 今回は、起動のたびに GDB UI 上で接続コマンドを打つのが面倒だったので起動時に渡すようにした

GDB を使ったステップ実行

デバッグ中のターミナルログ

root@4934294dab10:/work# gdb-multiarch -q --eval-command="target remote localhost:1234" ./kernel8.elf
Reading symbols from ./kernel8.elf...done.
Remote debugging using localhost:1234
start () at boot.S:6
6           mov sp, #0x80000
(gdb) b start
Breakpoint 1 at 0x80000: file boot.S, line 6.
(gdb) b halt
Breakpoint 2 at 0x80008: file boot.S, line 12.
(gdb) n

Thread 1 hit Breakpoint 2, halt () at boot.S:12
12          wfe
(gdb)
13          b halt
(gdb)

Thread 1 hit Breakpoint 2, halt () at boot.S:12
12          wfe
(gdb)
13          b halt
(gdb)
...(snip)

作ったプログラムの先頭から実行され、無限ループに入ることが確認できた。

QEMU モニタによるレジスタ確認

GDB によるデバッグ中でも、 QEMU モニタを使って各種情報を取得できる。 試しにレジスタ情報を表示する。

QEMU を起動しているターミナルで、 CTRL-a c と、キーを押すと QEMU モニタに命令を出せるようになる。

(qemu) info registers
PC=000000000008000c  SP=0000000000080000
X00=0000000000000000 X01=0000000000000000 X02=0000000000000000 X03=0000000000000000
X04=0000000000000000 X05=0000000000000000 X06=0000000000000000 X07=0000000000000000
X08=0000000000000000 X09=0000000000000000 X10=0000000000000000 X11=0000000000000000
X12=0000000000000000 X13=0000000000000000 X14=0000000000000000 X15=0000000000000000
X16=0000000000000000 X17=0000000000000000 X18=0000000000000000 X19=0000000000000000
X20=0000000000000000 X21=0000000000000000 X22=0000000000000000 X23=0000000000000000
X24=0000000000000000 X25=0000000000000000 X26=0000000000000000 X27=0000000000000000
X28=0000000000000000 X29=0000000000000000 X30=0000000000000000
PSTATE=400003cd -Z-- EL3h
q00=0000000000000000:0000000000000000 q01=0000000000000000:0000000000000000
q02=0000000000000000:0000000000000000 q03=0000000000000000:0000000000000000
q04=0000000000000000:0000000000000000 q05=0000000000000000:0000000000000000
q06=0000000000000000:0000000000000000 q07=0000000000000000:0000000000000000
q08=0000000000000000:0000000000000000 q09=0000000000000000:0000000000000000
q10=0000000000000000:0000000000000000 q11=0000000000000000:0000000000000000
q12=0000000000000000:0000000000000000 q13=0000000000000000:0000000000000000
q14=0000000000000000:0000000000000000 q15=0000000000000000:0000000000000000
q16=0000000000000000:0000000000000000 q17=0000000000000000:0000000000000000
q18=0000000000000000:0000000000000000 q19=0000000000000000:0000000000000000
q20=0000000000000000:0000000000000000 q21=0000000000000000:0000000000000000
q22=0000000000000000:0000000000000000 q23=0000000000000000:0000000000000000
q24=0000000000000000:0000000000000000 q25=0000000000000000:0000000000000000
q26=0000000000000000:0000000000000000 q27=0000000000000000:0000000000000000
q28=0000000000000000:0000000000000000 q29=0000000000000000:0000000000000000
q30=0000000000000000:0000000000000000 q31=0000000000000000:0000000000000000
FPCR: 00000000  FPSR: 00000000
(qemu)

こんな感じ。

以上。

参考資料

2019年3月3日日曜日

Xilinx Qemu で Cortex-R5 用 TOPPERS/ATK2 を動かした

TOPPERS/ATK2 を、 Debian 上の Xilinx/qemu で動かしたので手順を記録する。

環境

  • OS: Windows 10 Pro
  • Docker: Docker version 18.09.2, build 6247962
    • debian/buster-slim コンテナを使用
  • ATK2: atk2-sc1-mc_zynqmp_r5_gcc-20170929.tar.gz(Zynq UltraScale+ MPSoC Cortex-R5 用)
  • cfg: TOPPERS/cfg を Linux 64bit 対応したものを使用

ATK2 に必要なファイルのダウンロード

ATK2 ソース

TOPPERS/ATK2 の簡易パッケージダウンロードページから atk2-sc1-mc_zynqmp_r5_gcc-20170929.tar.gz をダウンロードし、任意の場所に展開する。

cfg

64bit Linux 対応版(最適化無効) から cfg をダウンロードし、 atk2-sc1-mc_1.4.2/cfg/cfg/cfg という配置になるように展開する。

環境構築

Docker コンテナの起動

展開した atk2-sc1 のディレクトリをマウントしつつ、 Docker コンテナを起動。

docker run -it -v "/PATH/TO/atk2-sc1-mc_1.4.2:/atk2-sc1-mc" --workdir /work debian/buster-slim

以降はこのコンテナ内で作業を行う。

必要なパッケージのインストール

下記作業を行うので、それに必要なパッケージをインストールする。

  • git リポジトリクローン
  • qemu ビルド
  • qemu の dtb ビルド
  • ATK2 のビルド
# いつもの
apt-get update
apt-get upgrade

# git
apt-get install git

# qemu ビルド
apt-get install build-essential python libglib2.0-dev libgcrypt20-dev zlib1g-dev autoconf automake libtool bison flex libpixman-1-dev

# dtb ビルド
apt-get install device-tree-compiler

# ATK2 ビルド
apt-get install gcc-arm-none-eabi

Xilinx/qemu の入手とビルド・インストール

git clone --recursive https://github.com/Xilinx/qemu.git
cd qemu
./configure --target-list="aarch64-softmmu,microblazeel-softmmu" --enable-fdt --disable-kvm --disable-xen
make
make install

Xilinx/qemu-devicetree の入手とビルド

git clone --recursive https://github.com/Xilinx/qemu-devicetrees.git
cd qemu-devicetrees
make OUTDIR=/tmp/dts

TOPPERS/ATK2 のビルド

cd /atk2-sc1-mc
mkdir mysample
cd mysample
../configure -T zynqmp_r5_gcc
make GCC_TARGET=arm-none-eabi

動作確認

Xilinx Quick Emulator User Guide の Table 3-7:Cortex-R5 Registers and Command に、 R5 で動かすための設定例が書いてある。 それを利用して QEMU を起動。

qemu-system-aarch64 -M arm-generic-fdt -nographic -serial mon:stdio -dtb /tmp/dts/LATEST/SINGLE_ARCH/zcu102-arm.dtb -device loader,file=./atk2-sc1-mc,cpu-num=4 -device loader,addr=0xff5e023c,data=0x80008fde,data-len=4 -device loader,addr=0xff9a000,data=0x8000218,data-len=4

オーディオの初期化に失敗するようだが、とりあえずシリアル出力はできている。

audio: Could not init `oss' audio driver

TOPPERS/ATK2-SC1-MC Release 1.4.2 for ZYNQMP_R5 (Mar  2 2019, 14:12:32)

StartupHook @ core0
activate MainTask! @ core0
Input Command:
1
Input Command:
a
Call ActivateTask(Task1)
RaiseInterCoreInterrupt before
/***********************/
Coer0_ICI_1 run!
Coer0_ICI_1 GetISRID() = 4
Coer0_ICI_1 GetApplicationID() = 0
RaiseInterCoreInterrupt finish
Task1 ACTIVATE
Input Command:

Docker イメージ

Xilinx/qemu の Docker イメージを作った。

今回作った環境とは構成が異なるので注意。

  • gcc-arm-none-eabi 未インストール
  • dts の格納場所を、 /tmp から /var に変更

以上。

参考資料