Rtags で BitVisor の開発を楽になるか?

この記事は，BitVisor Advent Calendar の3日目の記事です．

# はじめに
BitVisor は C 言語とアセンブラで書かれたハイパバイザです．
コード規模がそこそこ大きいため，コードを読んだり編集したりするのにそれなりに労力がかかるものです．
そういうときには便利な道具を使いたいものです．
IDE とかもありますが，CUI ツールしか使いたくないという人やお気に入りのエディタ(Emacs, Vim, etc)で使いたいという人には少し使いづらいです．
そこで， ctags や gtags といったシンボルのタグリストを作成し，タグジャンプや入力補完機能を提供するソフトを使うことがあります．
最近，これらのソフトの亜種として，rtags というものを見つけましたので，これを BitVisor で試してみようと思います．
(お察しの方もいるかと思いますが，技術的にはあまり BitVisor と関係なかったりします．)


# Rtags の特徴．
* 対応言語: C, C++, Objectiv C
* クライアント/サーバ なアーキテクチャ
* コンパイル時の情報を用いて解析
    * マクロで展開されるようなシンボルも解析できる

# 対応エディタ
* 公式
    * Emacs (公式リポジトリで管理されている)

* サードパーティ
    * Vim: https://github.com/lyuts/vim-rtags and https://github.com/shaneharper/vim-rtags
    * Sublime Text: https://github.com/rampage644/sublime-rtags
    * Atom: https://github.com/artagnon/atomic-rtags and https://github.com/rajendrant/atom-rtags

* その他，CLI もあるっぽい
* この記事で動かしたのは，Emacs だけ

# 導入

##検証環境
* OS: Arch Linux (4.8.11-1-ARCH)

## ビルド
僕は以下の手順でインストールできました．
元の環境次第では，他にもインストールしないといけないパッケージが他にもあるかもしれません．
おそらく参考URLの方が詳しいかと．

* llvm と clang のパッケージをインストール
* Github からクローン
    * GitHub - Andersbakken/rtags: A c/c++ client/server indexer for c/c++/objc[++] with integration for Emacs based on clang. - https://github.com/Andersbakken/rtags
* Submodule も入れる
* ビルド

コマンドは以下の通り
(2016/12/09 コマンドリスト修正)

```
$ yaourt clang # clang パッケージを選択，インストール
$ yaourt llvm  # llvm パッケージを選択，インストール
$ git clone https://github.com/Andersbakken/rtags
$ git clone --recursive https://github.com/Andersbakken/rtags
$ cd rtags/
$ mkdir build
$ cd build/
$ cmake ..
$ make -j5
$ sudo make install
```


### Linux Mint でのインストールで必要だったもの (2016/12/09 追記)

* clang
* libclang-dev

### ビルドがうまくいかない時は (2016/12/09 追記)
build ディレクトリを削除して，もう一度， `mkdir build` からやり直してみましょう．



## Bear のインストール

ソースコード解析をするためには，コンパイル情報を集めないといけない．
しかし，それを自前で集めるのは大変なので，Bear というコマンドで集めますので，これをインストールします．

インストールは，普通はビルドするだけ．
インストールのためのコマンドリストは以下の通り．
(2016/12/09 Bear の Github の URL を修正)

```
$ git clone https://github.com/rizsotto/Bear
$ cd Bear/
$ mkdir build 
$ cd build/
$ cmake ..
$ make
$ sudo make install
```

# ソースコード解析

* bear にコンパイルのコマンドを渡して，コンパイルの情報を集めつつビルドする
* rtags のサーバを起動
* 集めた情報を元に解析を行う
 
```
$ cd bitvisor/
$ bear make -j5
$ rdm &         # サーバ起動
$ rc -J         # rtags の解析コマンド
```

# Emacs 側の準備
* パッケージ `rtags` をインストール
    * rtags パッケージは melpa のパッケージ

# Emacs からの使い方
* rtags-find-symbol: 指定したシンボルの定義を探す
* rtags-find-references: 指定したシンボルを参照している場所を探す
* rtags-rename-symbol: カーソルがある部分のシンボルを一括で変換する


## rtags-find-symbol の例
* pci_read_config_data16_without_lock で rtags-find-symbol を検索すると，ちゃんと DEFINE_pci_read_config_data_without_lock(16)
にジャンプできる．
    * https://bitbucket.org/bitvisor/bitvisor/src/76db0ae4260b398f939486faedbd6dedb3e79b5f/drivers/pci_init.c?at=default&fileviewer=file-view-default#pci_init.c-334 から
    * https://bitbucket.org/bitvisor/bitvisor/src/76db0ae4260b398f939486faedbd6dedb3e79b5f/drivers/pci_internal.h?at=default&fileviewer=file-view-default#pci_internal.h-67 へジャンプできる


## rtags-rename-symbol の例 (2016/12/09 修正: struct data2 じゃなくて struct data をリネーム)

例えば，`driver/net/pro1000.c` の `struct data` を `struct pro1000_bar_data` にリネームするときには，rtags-rename-symbol 一発で以下のような変更ができますね．

``` diff
$ hg diff
diff -r 76db0ae4260b drivers/net/pro1000.c
--- a/drivers/net/pro1000.c     Mon Sep 12 20:01:54 2016 +0900
+++ b/drivers/net/pro1000.c     Fri Dec 09 18:21:57 2016 +0900
@@ -191,7 +191,7 @@
        } u;
 };

-struct data;
+struct pro1000_bar_data;

 struct data2 {
        spinlock_t lock;
@@ -205,7 +205,7 @@
        bool tse_first, tse_tcpfin, tse_tcppsh;
        u16 tse_iplen, tse_ipchecksum, tse_tcpchecksum;
        struct desc_shadow tdesc[2], rdesc[2];
-       struct data *d1;
+       struct pro1000_bar_data *d1;
        struct netdata *nethandle;
        bool initialized;
        net_recv_callback_t *recvphys_func, *recvvirt_func;
@@ -223,7 +223,7 @@
        struct pci_msi *virtio_net_msi;
 };

-struct data {
+struct pro1000_bar_data {
        int i;
        int e;
        int io;
@@ -1089,7 +1089,7 @@
 }

 static void
-mmhandler2 (struct data *d1, struct data2 *d2, phys_t gphys, bool wr,
+mmhandler2 (struct pro1000_bar_data *d1, struct data2 *d2, phys_t gphys, bool wr,
            union mem *buf, uint len, u32 flags)
 {
        union mem *q;
@@ -1181,7 +1181,7 @@
 static int
 mmhandler (void *data, phys_t gphys, bool wr, void *buf, uint len, u32 flags)
 {
-       struct data *d1 = data;
+       struct pro1000_bar_data *d1 = data;
        struct data2 *d2 = d1->d;

        if (d2->virtio_net && d2->virtio_net_msi && d1 == &d2->d1[0]) {
@@ -1201,7 +1201,7 @@
 }

 static void
-unreghook (struct data *d)
+unreghook (struct pro1000_bar_data *d)
 {
        if (d->e) {
                if (d->io) {
@@ -1215,7 +1215,7 @@
 }

 static void
-reghook (struct data *d, int i, struct pci_bar_info *bar)
+reghook (struct pro1000_bar_data *d, int i, struct pci_bar_info *bar)
 {
        if (bar->type == PCI_BAR_INFO_TYPE_NONE)
                return;
@@ -1249,7 +1249,7 @@
 change_bar0 (struct pci_device *pci_device, u8 iosize, u16 offset,
             union mem *data)
 {
-       struct data *d = pci_device->host;
+       struct pro1000_bar_data *d = pci_device->host;
        struct data2 *d2 = d->d;
        struct pci_bar_info bar_info;
        void *old_map;
@@ -1465,7 +1465,7 @@
 {
        int i;
        struct data2 *d2;
-       struct data *d;
+       struct pro1000_bar_data *d;
        void *tmp;
        struct pci_bar_info bar_info;
        struct nicfunc *virtio_net_func;
@@ -1555,7 +1555,7 @@
 vpn_pro1000_config_write (struct pci_device *pci_device, u8 iosize,
                          u16 offset, union mem *data)
 {
-       struct data *d = pci_device->host;
+       struct pro1000_bar_data *d = pci_device->host;
        struct pci_bar_info bar_info;
        int i;

@@ -1617,7 +1617,7 @@
 pro1000_config_read (struct pci_device *pci_device, u8 iosize,
                     u16 offset, union mem *data)
 {
-       struct data *d1 = pci_device->host;
+       struct pro1000_bar_data *d1 = pci_device->host;
        struct data2 *d2 = d1[0].d;

        if (d2->virtio_net) {
@@ -1647,7 +1647,7 @@
 pro1000_config_write (struct pci_device *pci_device, u8 iosize,
                      u16 offset, union mem *data)
 {
-       struct data *d1 = pci_device->host;
+       struct pro1000_bar_data *d1 = pci_device->host;
        struct data2 *d2 = d1[0].d;

        if (d2->virtio_net) {
```



# メリット
* マクロを使って定義されているシンボルにもジャンプできる
* シンボルの一括変更ができる

# デメリット
* コンパイルが必要
    * コンパイルに時間がかかるときにはつらいかも
* (試してはないがたぶん)ビルドしたコードしか解析できない
    * リネームは便利だけど，ビルドしていないコードがリネーム対象から漏れたりしそう...
* アセンブラ内のラベルは見つけられない
    * 他の*tagsも同じだけど...

# BitVisor で役に立ちそうな場面
* イケてない変数や関数の名前を一気に書き換えるとき
* find-grep で見つけられない変数や関数の定義を探すとき
    * マクロで作られているときには見つけられる



# 参考URL
* 最強のC/C++インデクサー "Rtags" を本気で使う - Qiita - http://qiita.com/kota65535/items/39aa4d6e8adf6ab5f98c
* GitHub - Andersbakken/rtags: A c/c++ client/server indexer for c/c++/objc[++] with integration for Emacs based on clang. - https://github.com/Andersbakken/rtags


# まとめ
* BitVisor 開発で時々困るときには，役に立ちそう
* だけど，過信は禁物だと思う(ビルドした範囲しか見つけられない，変換できないので)