色々なツールが存在しますが、その中の1つにcscopeがあります。特にemacsのようなエディタと組み合わせて使用すると、大変快適にソースをブラウズすることができます。
実際にlinuxカーネルのソースを読む時のcscopeの使用方法については、ここが参考になりますし、他にも色々と書いている人はいるのですが、最新のソースではディレクトリ構成も異なりますし、emacsから利用する場合の設定も幾つかまとめておきたいので、書いてみました。
CentOSのバージョンは5.3です。
# cat /etc/redhat-release CentOS release 5.3 (Final)
cscopeは標準でパッケージが用意されているので、コンパイルなどの手間は必要ありません。
# rpm -q cscope cscope-15.5-15.1.el5_3.1
カーネルソースは2.6.30を使用します。
$ cd /home/tarou/kernel/ $ tar xvfj ../linux-2.6.30.tar.bz2
cscopeで使用するインデックスファイルを作成します。
今回は、インデックスファイルをソースファイルがあるディレクトリとは別の場所に置くこととします。この例では、$DBに指定したディレクトリがインデックスファイルを配置するディレクトリです。
(まあ、linuxカーネルの場合はMakefileにcscopeターゲットがあるので、後述のインデックスファイル分割とかやらないのであれば、そちらを使った方が楽でしょうけど。)
### 最初に対象となるファイルのリストを作成します。 ### どのファイルを対象にするかは目的に応じて。 ### 例えばdriversなんかも外して良いかも。 $ SRC=/home/tarou/kernel/linux-2.6.30 $ DB=/home/tarou/cscope-db $ find $SRC \ -path "$SRC/arch/*" ! -path "$SRC/arch/ia64*" -prune -o \ -path "$SRC/Documentation*" -prune -o \ -path "$SRC/scripts*" -prune -o \ -path "$SRC/firmware*" -prune -o \ -path "$SRC/sound*" -prune -o \ -path "$SRC/samples*" -prune -o \ -name "*.[chxsS]" -print > $DB/cscope.files ### 次にインデックスファイルを作成します。 ### -q で inverted indexを作成し、-k でカーネルモードであることを指定します。 $ cd $DB $ cscope -bqk ### このようにファイルができているはずです。(かなり大きいです。。。) $ ls -l total 308180 -rw-rw-r-- 1 tarou tarou 790172 Jul 1 22:19 cscope.files -rw-rw-r-- 1 tarou tarou 21553152 Jul 1 22:23 cscope.in.out -rw-rw-r-- 1 tarou tarou 169135368 Jul 1 22:23 cscope.out -rw-rw-r-- 1 tarou tarou 123765900 Jul 1 22:23 cscope.po.out
注意:
"-q" オプションは結構重要です。"-b"オプションだけだとcscope.outファイルしか作成されませんが、"-q"を付けると合計3つのインデックスファイルが作成されます。上記例でわかるようにインデックスファイルサイズは10倍以上になるのですが、そのかわり利用時の検索速度は数倍(状況によってはもっと)になります。環境によって効果は異なると思いますが、大規模プロジェクトでは意識した方が良いオプションです。
.emacsファイルに以下の内容を追加します。
なお、xcscope.el は、/usr/share/cscope/xcscope.el にあります。(site-lispはシンボリックリンクです)
(require 'xcscope)
;;; インデックスファイルの更新を抑制します
(setq cscope-do-not-update-database t)
;;; cscopeの対象と使用するインデックスファイルのディレクトリを指定
;;; 最初がソースディレクトリで "$SRC" に対応します。先頭の "^" を忘れずに。
;;; 次はインデックスファイルを配置したディレクトリで "$DB" に対応します。
(setq cscope-database-regexps
'(
("^/home/tarou/kernel/linux-2.6.30"
("/home/tarou/cscope-db" ("-q" "-k"))
)
)
)
;;; アセンブラコードでも有効になるように
(add-hook 'asm-mode-hook (function cscope:hook))
;;; 色を変更します(これは好みで)
;;; 色づけが嫌なら (setq cscope-use-face nil)
(set-face-foreground 'cscope-file-face "blue")
(set-face-foreground 'cscope-function-face "black")
(set-face-foreground 'cscope-line-number-face "red")
(set-face-foreground 'cscope-line-face "black")
(set-face-foreground 'cscope-mouse-face "black")
ここまで設定が終わっていれば、普通にemacsを起動してcscopeを使用できます。
特に使いそうなものをピックアップしてみます。
C-c s s
(カーソルが置かれた)シンボルを検索
C-c s d
(カーソルが置かれた)シンボルの定義を検索
C-c s c
(カーソルが置かれた)関数を呼んでいる関数群を検索
C-c s C
(カーソルが置かれた)関数から呼び出している関数群を検索
C-c s t
テキスト文字列を検索
C-c s e
egrepパターンで検索
C-c s f
ファイル検索
C-c s i
#include してるファイルを検索
C-c s u
マークをポップ(元のファイルに戻る、*cscope*バッファで'u'でもOK)
ブラウズするのは快適ですが、修正をおこなうとインデックスファイルと実際のソースの対応がズレたり、探せなくなります。
その場合は、インデックスファイルを再作成する必要があります。.emacsで自動更新は抑制してますから、手動で実施します。ファイル構成が変更されていたらファイルリストの更新から、そうでなければインデックスファイルの再作成だけをおこないます。
対象となるファイルが多い場合は、インデックスファイルの再作成のコストもバカになりません。このような場合、再作成を頻繁に行なう必要があるファイル群と、そうでないファイル群でインデックスファイルを分けることができます。
例えば、アーキテクチャ非依存、アーキテクチャ依存、ドライバのそれぞれで分けるとします。(分け方は、それぞれの都合で決めれば良いです)
$ pwd /home/tarou $ ls cscope-db* ## このディレクトリではアーキテクチャ非依存のファイルのみをfindで収集して作成 cscope-db: cscope.files cscope.in.out cscope.out cscope.po.out ## このディレクトリではアーキテクチャ依存のファイルのみをfindで収集して作成 cscope-db-arch: cscope.files cscope.in.out cscope.out cscope.po.out ## このディレクトリではドライバのファイルのみをfindで収集して作成 cscope-db-driver: cscope.files cscope.in.out cscope.out cscope.po.out
このようにして、各ディレクトリ毎にファイルリストとインデックスファイルを作成します。そして、.emacsをそれに併せて修正します。
(setq cscope-database-regexps
'(
("^/home/tarou/kernel/linux-2.6.30"
("/home/tarou/cscope-db" ("-q" "-k"))
("/home/tarou/cscope-db-arch" ("-q" "-k"))
("/home/tarou/cscope-db-driver" ("-q" "-k"))
)
)
)
これで、/home/tarou/kernel/linux-2.6.30 配下のソースコードをブラウズする際に、3つのインデックスファイルを利用するようになります。
xcscope.el以外にもbscopeというのもあるようです。これは、xcscopeが検索毎にプロセス起動したりインデックスの読み込みを行なっている(Do a single search)ので遅いのに対して、Line-oriented interfaceを使用することで、そのオーバーヘッドを削減したというものです。確かに操作してると若干の”間”を感じたりするので、これは試してみたいかも。