以安裝 ccache 為例, 說明如何使用系統工具除錯

ccache 是什麼?

ccache 藉由暫存編譯過的 object 檔, 可以減少不必要的重新編譯時間。

用法很簡單:

# 安裝
$ sudo aptitude install ccache
# 啟用 ccache
$ export PATH="/usr/lib/ccache:$PATH"
$ gcc ...  # 這時會用到 /usr/lib/ccache/gcc

以我測試的例子來說, 從頭重新編譯一次是半小時左右, 裝了 ccache 後變成一分半。

ccache 官網所言, ccache 有可能重編譯不必要的程式, 但不會用到不對的暫存檔, 這也是使用這類工具時最重要的保證。

不過這篇的重點不在 ccache 的用法, 而是如何「知道如何安裝和使用它」。以前的文章或多或少有提到下文用的工具, 這篇比較有系統地用一個完整的例子使用它們。

已知

  • ccache 替換 gcc 等編譯工具, 檢查編譯條件和檔案沒變時, 直接取得暫存檔作為編譯結果。
  • 粗略地掃過 ccache manual, 得知是用 symbolic link 替換 gcc 為 ccache。

截至目前為止, 我們已得知 ccache 的核心運作原理, 接下來可以動手試看看。

安裝和使用過程

1. 首先確認是否能透過系統套件安裝 ccache:

$ aptitude search ccache
i   ccache                                                                              - Compiler cache for fast recompilation of C/C++ code
p   ccache:i386                                                                         - Compiler cache for fast recompilation of C/C++ code

2. 再來看套件訊息, 確定沒搞錯套件, 還有看看版號是否夠新:

$ aptitude show ccache
Package: ccache
State: installed
Automatically installed: no
Version: 3.1.6-1
...
Description: Compiler cache for fast recompilation of C/C++ code
 ccache is a compiler cache. It speeds up recompilation by caching previous compilations and detecting when the same compilation is being done again. Supported languages are C, C++,
 Objective-C and Objective-C++.
Homepage: http://ccache.samba.org

對照官網版號 3.1.9, 3.1.6-1 還算 OK, 日後有必要再仔細看 release note 決定是否要抓原始檔重編。

3. 開始安裝:

$ sudo aptitude ccache

裝完後沒任何反應 ......, 依先前得知的訊息, 看看 gcc 有沒有被換掉:

$ which gcc
/usr/bin/gcc
$ ls -l /usr/bin/gcc
lrwxrwxrwx 1 root root 7 Nov 14  2012 /usr/bin/gcc -> gcc-4.6*
$ ls -l /usr/bin/gcc-4.6
-rwxr-xr-x 1 root root 353216 Apr 16  2012 /usr/bin/gcc-4.6*

結果 gcc 沒有被換掉 ......。

4. 腦袋卡住了一下下, 接著想到來確認 ccache 裝了什麼, 也許有些線索:

$ dpkg -L ccache
/.
/usr
/usr/share
...
/usr/share/man/man8/update-ccache-symlinks.8.gz
...
/usr/sbin/update-ccache-symlinks
/usr/bin
/usr/bin/ccache
/usr/lib
/usr/lib/ccache

裡面有個可疑的檔案 /usr/sbin/update-ccache-symlinks, 馬上來看看檔案內容。

結果是個 perl script, 從開頭得知重要的資訊:

  6 my $ccache_dir = "/usr/lib/ccache";
  7 my $old_gcc_dir = "/usr/lib/gcc";
  8 my $new_gcc_dir = "/usr/lib/x86_64-linux-gnu/gcc";
  9 my %old_symlinks; # Current compiler names in /usr/lib/ccache
 10 my %new_symlinks; # Compiler names that should be in /usr/lib/ccache
 11 my @standard_names = qw(cc c++);

對照後面的程式碼和這些變數名稱, /usr/lib/ccache 是關鍵的角色:

$ ls -l /usr/lib/ccache/
total 0
...
lrwxrwxrwx 1 root root 16 Aug 24 21:19 g++ -> ../../bin/ccache*
lrwxrwxrwx 1 root root 16 Aug 24 21:19 g++-4.6 -> ../../bin/ccache*
lrwxrwxrwx 1 root root 16 Aug 24 21:19 gcc -> ../../bin/ccache*
lrwxrwxrwx 1 root root 16 Aug 24 21:19 gcc-4.6 -> ../../bin/ccache*
...

Bingo!! 將 /usr/lib/ccache 加到 PATH 前面, 應該會有效果。但是, 要如何快速驗證 ccache 確實有發揮效果呢? 我可不想重編一堆檔案, 過個半小時才發覺「好像沒效」。

依前面的假設, ccache 編譯時會從暫存區取出編譯過的檔案, 所以第一次編譯時, ccache 應該也會寫入編譯過的檔案到暫存區。幸好開檔的 system call 只有一個 open(2), 這時就該 strace 上場了:

$ cd ~/tmp/
$ export  PATH="/usr/lib/ccache:$PATH"
$ strace -eopen -f g++ t.cpp -c > log 2>&1

翻一下 log 會看到

 10 [pid 18133] open("/home/fcamel/.ccache/tmp/t.tmp.fc-vm.18132.ii", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0666) = 4
 11 [pid 18133] open("/home/fcamel/.ccache/tmp/tmp.cpp_stderr.fc-vm.18132", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0666) = 4

由此順便找到暫存目錄在 $HOME/.ccache 下。日後有需要也可以到這裡清清暫存檔。

有興趣的話可以用 strace 追更多 system call, 比對 open 開啟的 file descriptor (fd) 和後續 read、write 操作的 fd, 可以證實更多推測。到此就大功告成了!

小結

相信以 ccache 這樣成熟的套件, 仔細翻翻官網文件、man page 或 google 一下, 就可以找到答案。不過了解如何使用這些系統工具以後, 日後遇到文件不詳細或不易 google 的套件時, 有機會自行找出一線生機。

留言

這個網誌中的熱門文章

virtualbox 使用 USB 裝置

熟悉系統工具好處多多

如何 git merge 更改檔名的檔案