2016年9月27日 星期二

筆電重灌全記錄

最近家目錄 100 GB 的硬碟被我塞滿了。

雖然後來刪掉一些東西,例如之前玩一些<資料>分析contest 的資料,還有之前玩虛擬貨幣primecoin載了整個區塊鏈,也全刪掉;清了大概30 GB 的空間出來,不過覺得還是不夠好,畢竟現在的電腦從2013年4月用到現在,硬碟分割不是很好,有太多長期留下來的東西,設定檔愈來愈亂,空間分配也不是很好;覺得是時候重灌系統調整體質,以迎接下個十年,逆風高灰(X。

重灌系統,麻煩的就是要先備份系統,我選擇的是把家目錄備份出來,備份使用rsync,用rsync 的好處是快,而且可以增量備份,隨時加一點東西上去也OK,例如週末有空的時候做全資料夾備份,直到重灌前一刻再增量備份:
http://newsletter.ascc.sinica.edu.tw/news/read_news.php?nid=1742
rsync -av --exclude=”.*” --exclude=”.*/” src_dir dest_dir
不過如果你的資料夾裡有 git 的話,就要小心,上面這個不會備份隱藏的資料夾,要把那個exclude 拿掉。

硬碟分割的部分,這次決定採用激進的分割方式,雖然電腦上會保留Windows…用來……打遊戲,不過這次不分割所謂的D槽給Windows,資料分割區從NTFS 改為ext4。

以下是這次的分割狀況,沒特別註明就是GB:
  • /boot 200 MB
  • windows 150 之前的windows 被塞了不少acer 預設的程式在裡面,這回全部清得乾乾淨淨,給150 應該很夠用了。
  • Linux root 75,現在是給50 GB,我也從來沒想過它會裝滿,直到有一天要燒FPGA裝了Xilinx Vivado,瞬間root 剩下零頭,每回開機都會警告空間不足…
  • Linux /var 30 曾看archlinux 的wiki 頁面分12 G 給它,結果安裝一堆套件之後立刻就滿了
  • Linux /home 200 一口氣倍增到200 G,應該絕對夠用了吧
  • Linux /data 硬碟剩下的空間大概500 G就全分給data,之前用NTFS 現在改用ext4,用來放一些像是東方啦動畫之類的東西。

另外備份所有已裝套件:archlinux 使用pacman -Q > allpackage 或 yaourt -Q > allpackage 把所有安裝的套件包都列出來

然後就可以放心的把磁碟區全部clean 掉,然後先裝windows,再裝我的不敗開發環境:archlinux;颱風天一個人躲在房間,任窗外狂風暴雨,我的電腦也是狂風暴雨XD。不過我太久沒裝了,還是參考一下強者我同學的安裝記錄,大體上沒什麼不同,我是選用mate 作為我的桌面環境:
http://johnjohnlys.blogspot.tw/2016/06/archlinux.html
小試一下,我的archlinux就回來啦,重裝套件的部分,就用剛剛上面的reinstall 文件,搭配vimdiff,很快就能把套件全部裝回來了…才怪。

老實說,因為選用的是archlinux 的關係,有不少比較不那麼正式卻又很常用到的東西,都要從AUR 去裝;而AUR 跟pacman相比就是很吃網路跟編譯時間,結果我整個裝下來,花的時間不比windows 的少,雖然說爽度還是有差,AUR 只要yaourt 裝好,指令打下去就是了;windows 還要自己去找安裝檔,用滑鼠一個一個安裝,只能說重灌就是麻煩……希望這次轉骨完之後,下次可以等到很久很久之後再重灌了。

ps. 後來發現在 /etc/makepkg 裡面的 -j 選項沒有開,等於是一直用單核心編譯,不知道速度上能差到多少

2016年9月21日 星期三

國際觀大補帖

最近某位教授覺得大家竟然不知道希特勒是誰,覺得大家都沒國際觀,台灣未來一定超沒競爭力,大家都只能領22K。
他還提供了一個網站<國際觀檢測網>:「每次點進去都會出現10道題目,例如尼克森、翁山蘇姬是哪國人、做過什麼事,並提供詳解」,連結在此:
http://doc.boyo.org.tw/gp/

老實說我覺得這樣太沒效率了,一次只有10題不小心還會重複,豈不是浪費應考人的時間!?因此我就做了一份增進國際觀大補帖,把網站的題庫全部拉出來,讓大家題目可以一看再看,準備完了再上線考試,就像考駕照一樣。
相信有了這份大補帖,一定能幫大家有效的提升國際觀,讓台灣人才競爭力逆風高灰超英趕美。

概念其實不難,就跟之前做的東西一樣,我是用python2.7 來實作,著眼點是它的lxml 套件,不過python2.7 對utf-8 的支援沒那麼好,script 開始前要把default encoding 設定為utf-8,不然常會印出亂碼。
reload(sys)
sys.setdefaultencoding('utf-8')
首先用urllib2把網頁抓下來,因為這個網頁會cache 要求過的內容,因此要用這樣來開啟網頁,讓HTTP proxy server不要cache 回傳的資料。
request = urllib2.Request(TARGET)
request.add_header("Pragma", "no-cache")
response = urllib2.build_opener().open(request)
再來用lxml把裡面問題和答案的部分給挖出來,填到一個python dictionary 即可,每抓一次都會比對不要抓到重複的東西。
因為爬蟲有時候會遇到網頁錯誤,因此一定要把爬過的東西先存下來,不然跑一跑壞掉了,重跑又要重抓資料,會哭的;幸好在python 上面有pickle 的支援,serialize 資料算相當方便,只要在開始的時候,先用pickle 讀入一個字典檔,沒有的話就回一個空的:
def openPickle():
  try:
    return pickle.load(open("global", "rb"))
  except (EOFError, IOError):
    return {}
然後存檔也很簡單,一行就解決了:
pickle.dump(data, open("global", "wb"))
再來就是一直跑一直跑一直跑,題目就會如洪水般湧進來,老實說挖出來的東西比我想得還要多…好多,wc 一下有1500多行,估計大約就是750 題左右吧。
Well 至於我爬出來的東西,我就無償公開好了,反正這種東西你它的網站一直按F5 也可以抓全,我只是請機器人幫我抓,原始碼跟大補帖放在這裡:
原始碼:https://github.com/yodalee/globalizaion
大補帖:https://github.com/yodalee/globalizaion/blob/master/global
其實我對這個題…沒什麼意見啦,不過我還是不樂見有人真的把這個拿來背(是…應該不會有人這麼蠢吧…應該啦……),畢竟與其死背希特勒是哪國人,還不如去了解他崛起的背景,與其多看大補帖還不如念念「希特勒回來了」。

不過自從我把大補帖公平出來之後,受到各方熱烈的回應,在此僅節錄幾則:
使用者評價一:自從用了國際觀大補帖,頭腦就靈光了很多,考試都考87分呢!
使用者評價二:上學的時候我國際相關的東西都答不出來,自從用國際觀大補帖,成績突飛猛進,現在已經準備要考托福跟日檢N1了。
使用者評價三:原本人生一片黑暗,直到遇到國際觀大補帖,和家人關係變好不說,上周不但交到了女友,老闆還幫我加薪25元呢。
使用者評價四:看國際觀大補帖,平常和朋友聊天信手拈來就是一堆國際知識,朋友們都改用欽佩的眼光看我,覺得我人生從此都不一樣了。
使用者評價五:之前男朋友都看不起我,自從把國際觀大補帖印下來帶在身邊,除了平時閱讀增進國際知識,男朋友欺負我的時候還可以用厚重的大補帖打他的臉,被沉重的歷史感打到一定很痛!
使用者評價六:難得一見的好書,我把每一題都記得滾瓜爛熟,上周考汽車駕照一次就通過了,謝謝你,國際觀大補帖。

Ps. 其實這篇寫程式的時間遠不及寫上面那堆嘴砲文的時間lol

2016年9月20日 星期二

在Archlinux 安裝arduino 開發環境

最近開始接觸arduino開發板
在archlinux 上開發arduino 也相當簡單,照著wiki上面做便是:
https://wiki.archlinux.org/index.php/arduino
先用yaourt 裝arduino
然後把一般使用者加到uucp 跟lock 群組裡面,讓使用者可以取用serial 的權限:
# gpasswd -a $USER uucp
# gpasswd -a $USER lock
再來登出登入就設定完了,打開arduino 就有介面可以使用了。

另外有遇到一個問題,因為手上拿到的arduino 使用的是Intel Edison的板子,因此打開arduino 之後,要用它的board manager 或中譯板子管理員安裝Intel Edison 的板子。
而安裝時會出問題,大致的錯誤訊息會類似這樣:
Setting it up...find: invalid mode ‘+111’
/tmp/tmp.ioGoYaYhZu/relocate_sdk.sh /home/yodalee/.arduino15/packages/Intel/tools/core2-32-poky-linux/1.6.2+1.0/i686/relocate_sdk.sh
SDK could not be set up. Relocate script failed. Abort!

然後如果不理它直接去arduino 裡面編譯看看的話,會出現類似這樣的錯誤:
$HOME/.arduino15/packages/Intel/tools/core2-32-poky-linux/1.6.2+1.0/i686/sysroots/x86_64-pokysdk-linux/usr/bin/i586-poky-linux/i586-poky-linux-g++: No such file or directory

總之在家目錄的超深的地方,某個g++ 檔案找不到,但如果你去它所寫的路徑看一下,它確確實實的就在那裡,直接在shell 下執行會得到一樣的結果。
第一次遇到這個問題是在管工作站的時候,某個EDA 公司給的執行檔,也是怎麼跑都跑不起來,當下完全就是見鬼了,檔案就在那裡linux 你是跟我開玩笑嗎?後來那個EDA 好像是給錯版本,忘了是32 bits 的工作站給了64 bits 執行檔,還是64 bits 工作站給了32 bits 執行檔,總之後來強者我同事解掉了。

解法一(不完整,請用解法二)
如果用file 去看那個執行檔就會看出一點端倪,裡面有這句:
interpreter /opt/poky-edison/1.6.1/sysroots/core2-32-poky-linux/lib/ld-linux.so.2
意即這個執行檔需要這個dynamic linker來執行,而安裝時,這個路徑並沒弄好,因為在這個安裝包($HOME/.arduino/packages/Intel/tools/core2-32-poky-linux/1.6.2+1.0/i686)下面有找到這個檔案sysroots/core2-32-poky-linux/lib/ld-linux.so.2
因此解法就是在opt下面,用softlink連結1.6.1到i686 下面,編譯就能跑得起來了。
上述工作站的問題也是類似,大概是忘了裝32/64 bits 的libc,在ubuntu的話應該要補裝libc6-i386 lib32stdc++6 等。

解法二:
這個才是正常的解法,在i686 資料夾下,會找到安裝時執行的script:install_script.sh,實際執行的話也會報錯,它在呼叫relocate_sdk.sh裡面執行relocate_sdk.py後會出錯,原因是給relocate_sdk.py 參數太少了。
後來查到真正的原因跟解法:
http://askubuntu.com/questions/764715/unable-to-install-intel-i586-library-intel-galileo-gen-2-in-arduino-ide-on-ubu

把install_script.sh 裡面的
executable_files=$($SUDO_EXEC find "$native_sysroot" -type f -perm +111 -exec printf "\"%s\" " {} \; )
的+111 改成 /111就行了,就是find 出錯導致executable_files 沒找到檔案,relocate_sdk.sh 就沒把該修改interpreter 的執行檔傳給relocate_sdk.py,最後就變成interpreter 還指向/opt 的狀況。

確實去找manpage find,會找到這行
-perm +mode
This is no longer supported (and has been deprecated since 2005). Use -perm /mode instead.
所以大概是這個script 太久沒維護了吧。

後話:
現在筆電的archlinux 自從我2013/4/24 10:26:04 灌好之後,經歷了無數程式開發,無論系統程式、機器學習、Android、Arduino,從來沒讓我失望過,archlinux果然是不敗開發神機。
至於我怎麼知道這個時間,是參考這個:
http://unix.stackexchange.com/questions/9971/how-do-i-find-how-long-ago-a-linux-system-was-installed
用tune2fs 去偵測例如root 或boot 分割區的創立時間,大致就是系統灌好的時間了。

2016年9月16日 星期五

不為人知的gdb 使用方式系統-gdb pretty printer auto load

前言:
最近因為jserv 大神的關係,看了下面這部Become a GDB Power User
https://www.youtube.com/watch?v=713ay4bZUrw
覺得裡面還不少生猛的用法之前都不會用,決定把它整理一下,寫個系列文:

上回我們提到了gdb 的pretty printer,現在我們就來看個範例:寫Rust 用的Rust-gdb。

其實Rust-gdb 跟gdb 本質上沒什麼不同,裡面將python pretty printer 的路徑加到gdb source 的路徑,然後執行gdb…
gdb -d "$GDB_PYTHON_MODULE_DIRECTORY" \
-iex "add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY" \
"$@"
這樣感覺什麼都沒加進去呀,事實上,rust 是透過比較隱晦的機制,在編譯的時候將要引入的資料插在執行檔的 .debug_gdb_scripts 裡面,這個section 會包含null-terminated 的條目(entry)指明gdb 要載入哪些script,每個script 的開頭會有一個byte 指明這是哪一種entry,拿我之前寫的racer 來試試,把它的section 印出來:
$ readelf -S racer
[17] .debug_gdb_script PROGBITS         0000000000743af8  00743af8
     0000000000000022  0000000000000000 AMS       0     0     1
然後我們把這個區段給印出來:
$ readelf -x .debug_gdb_scripts racer
「.debug_gdb_scripts」區段的十六進位傾印:
0x00743af8 01676462 5f6c6f61 645f7275 73745f70 .gdb_load_rust_p
0x00743b08 72657474 795f7072 696e7465 72732e70 retty_printers.p
0x00743b18 7900                                y.

開頭的01,指明它是在script in python file,內容則指明要載入gdb_load_rust_pretty_printers.py,一般我覺得是用不著這麼搞工啦,畢竟要用這招就要在編譯器上面動手腳,大概在自幹語言的時候用上這招比較方便,相關文件可見這兩個連結:
https://sourceware.org/gdb/onlinedocs/gdb/Auto_002dloading-extensions.html#Auto_002dloading-extensions
https://sourceware.org/gdb/onlinedocs/gdb/dotdebug_005fgdb_005fscripts-section.html#dotdebug_005fgdb_005fscripts-section

rust-gdb 所用的方法,如這份文件str_lookup_function之後的部分所示:
https://sourceware.org/gdb/onlinedocs/gdb/Writing-a-Pretty_002dPrinter.html#Writing-a-Pretty_002dPrinter

把pretty printer 寫完之後,首先要先寫一個gdb 的lookup_function,這個function 會負責回傳適當的pretty printer,不然就回傳None。
文件建議這個pretty printer 放到單獨的python package 裡面,如果這個pretty_printer是針對某個函式庫寫的,那更建議package 的名字要加上函式庫的版本號,這樣gdb 能載入不同版本的pretty printer;最後在auto load 的script 中import 這個pretty printer,用gdb.pretty_printers.append ,把這個lookup_function插入gdb的objfile 中即可。

Rust-gdb 就是這樣做的:
auto load 的script gdb_load_rust_pretty_printers.py 中,引入gdb_rust_pretty_printing,然後呼叫它的register_printers,把rust_pretty_printer_lookup_function插入目前的objfile 中。
一個lookup_function 會接受一個gdb Value,我們可以從中取得它的type的tag,從tag 中辨別它是什麼型別的資料,藉此回傳適當的pretty printer。

我用之前的QString 做了個簡單的範例,首先我們要改造一下我們的qstring.py,加上lookup function 跟register_printer,lookup function 在型別為QString 時會將這個值傳入我們寫的QStringPrinter,並回傳它:
def qstring_lookup_function(val):
  lookup_tag = val.type.tag
  if lookup_tag is None:
    return None
  regex = re.compile("^QString$")
  if regex.match(lookup_tag):
    return QStringPrinter(val)
  return None

def register_printers(objfile):
  objfile.pretty_printers.append(qstring_lookup_function)
接著我們寫一個autoload script : hello-gdb.py,雖然我們用到了gdb,但gdb 內的python 會自動引入這個gdb 這個module,所以不引入也沒關係:
import qstring
qstring.register_printers(gdb.current_objfile())
另外要注意的是,一定要使用自動載入的方式,因為gdb.current_objfile() 只有自動載入的時候才會設定為當前的objfile。
之所以叫hello-gdb.py,是因為我們採用另一個機制:objfile-gdb.py的檔案會被自動載入,我執行檔名叫hello ,hello-gdb.py會在gdb 開啟時自動載入,其他有用到的dynamic library ,檔名叫libXXX-gdb.py也可以用類似的方法載入。
https://sourceware.org/gdb/onlinedocs/gdb/objfile_002dgdbdotext-file.html#objfile_002dgdbdotext-file

當然故事不是這樣就完了,首先hello-gdb.py會用到qstring.py,所以我們必須把qstring的位置加到PYTHONPATH 變數之中;第二gdb 不會隨意自動載入第三方的script,以免造成危害(雖然說到底能有啥危害我也不知道……),因此我們必須在gdb 開始自動載入script 前,把這個script 加到安全名單之中;因此,我們的gdb 呼叫會變成下列兩者擇一,也就跟rust-gdb 相差無幾了:
PYTHONPATH="$PYTHONPATH:/tmp" gdb -iex "set auto-load safe-path /tmp" hello
PYTHONPATH="$PYTHONPATH:/tmp" gdb -iex "add-auto-load-safe-path /tmp/hello-gdb.py" hello

大概的故事就是這樣了,Rust-gdb 當然是lookup function 那邊搞了很多,把型別什麼共同的部分獨立出來,以便支援gdb 跟lldb。
一般的lookup function 就不用寫這麼多了。
又說回來,要寫到大型的lookup function 應該也只有開發語言跟函式庫的時候需要,一般人更用不到這個功能,所以這篇比起上篇其實更是廢文一篇(蓋章,感謝大家又貢獻兩分鐘的時間給小弟的blog (炸。

2016年9月14日 星期三

不為人知的gdb 使用方式系統-gdb pretty printer

前言:
最近因為jserv 大神的關係,看了下面這部Become a GDB Power User
https://www.youtube.com/watch?v=713ay4bZUrw
覺得裡面還不少生猛的用法之前都不會用,決定把它整理一下,寫個系列文。

這篇來介紹 gdb 的pretty printer:
https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html#Pretty-Printing

假設我們有一個客製的struct 或是class,例如Qt4 的QString,如果我們用原本gdb 的print 去印出QString的話:
QString s("Hello World");
(gdb) p s
$2 = {static null = {<No data fields>}, static shared_null = {ref = {_q_value = 2}, alloc = 0, size = 0, data = 0x7ffff7dd70fa <QString::shared_null+26>, clean = 0,
simpletext = 0, righttoleft = 0, asciiCache = 0, capacity = 0, reserved = 0, array = {0}}, static shared_empty = {ref = {_q_value = 1}, alloc = 0, size = 0,
data = 0x7ffff7dd70da <QString::shared_empty+26>, clean = 0, simpletext = 0, righttoleft = 0, asciiCache = 0, capacity = 0, reserved = 0, array = {0}},
d = 0x603dc0, static codecForCStrings = 0x0}

因為QString 是個結構的關係,我們無法單純印個char array ,反而會印出裡面所有的資訊,它真的資訊是存在那個 d 裡面,要真的印字串就要從那個d 裡面去印。
之前在寫Qt 專案的時候有遇到類似的問題,那時有查到一個printqstring 的自訂函式,把下面這段加到 ~/.gdbinit 裡面:
define printqstring
  printf "(QString)0x%x (length=%i): \"",&$arg0,$arg0.d->size
  set $i=0
  while $i < $arg0.d->size
    set $c=$arg0.d->data[$i++]
    if $c < 32 || $c > 127 
      printf "\\u0x%04x", $c
    else
      printf "%c", (char)$c
    end 
  end
  printf "\"\n"
end

就可以在debug 時使用printqstring來印出QString
(gdb) printqstring s
(QString)0xffffe650 (length=12): "Hello World!"

當然我們可以用gdb 的pretty printer 來做到類似的事,而且不需要自訂函式,單純用p s 也能做到一樣的效果。
gdb v7 開始加入對python的支援,可以讓我們透過python和gdb 互動,在python 之中import gdb 之後,就可以利用一連串定義完善的介面去跟gdb 互動,詳細上用法還不少,這裡我們只先試著寫個pretty_printer。
Python 的pretty printer 就是實作一個class,至少要實作__init__跟to_string() 兩個介面,其他的函式可以參考:
https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing-API.html#Pretty-Printing-API
之後使用gdb.printing的RegexpCollectionPrettyPrinter,產生一個叫QString 的printer,利用add_printer,只要型態名稱符合^QString$ 的物件,就用QStringPrinter來處理,最後用regeister_pretty_printer把這個RegexpCollectionPrettyPrinter 塞進去:
# qstring.py
import gdb.printing

class QStringPrinter(object):
  def __init__(self, val):
    self.val = val
  def to_string(self):
    return "I'm Qstring"
  def display_hint(self):
    return 'string'

pp = gdb.printing.RegexpCollectionPrettyPrinter('QString')
pp.add_printer('QString', '^QString$', QStringPrinter)
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp)
進到gdb 之後,首先source qstring.py ,接著去print 一個QString 的話,gdb 就會呼叫我們寫的QStringPrinter 的to_string 來處理,就會得到垃圾訊息:I'm QString。

to_string 回傳的規則如下:
python 的integer, float, boolean, string 都是gdb 可處理的(可轉換為gdb.Value),會由gdb 直接印出;回傳None的話則不會印東西。
傳回其他的gdb.Value,那gdb 會接續處理這個value,處理中也會呼叫其他註冊的pretty printer;真的無法處理,gdb就會丟出exception。
另外我們可以實作display_hint 的介面,告知gdb 要如何處理這個值的輸出,這會影響到gdb 如何接續處理這個值;這裡我們表示這個輸出是個字串。

上面只是舉例,要印出同樣的結果,我們的to_string 要稍微改寫:
addr = self.val.address
size = self.val['d']['size']
i = 0 
content = []
while i < length:
  c = self.val['d']['data'][i]
  if c > 127:
    content.append("\\u%x" % (int(c)))
  else:
    content.append(chr(c))
  i = i + 1 
return '(QString)%s (length=%d): "%s"' % (addr, size, "".join(content))
當然我在查這個的時候,發現有另一個人的實作生猛得多:
http://nikosams.blogspot.tw/2009/10/gdb-qt-pretty-printers.html
dataAsCharPointer = self.val['d']['data'].cast(gdb.lookup_type("char").pointer())
content = dataAsCharPointer.string(encoding='utf-16', length=size*2)
它直接用cast 得到一個新的gdb.Value(),型態為char pointer;再呼叫string()方法用utf-16 的方式encoding,就把字串給拿出來了,更多用法可以參考文件,這裡暫時不詳細說明:
https://sourceware.org/gdb/onlinedocs/gdb/Values-From-Inferior.html

不過,這樣的pretty printer 其實有個問題的,因為它寫死了一定會去讀取self.val['d']裡面的值,但這個值未必是有初始化的,這時使用pretty printer 就會出錯:
<error reading variable: Cannot access memory at address 0xa>

其實要用pretty printer 的話,在.gdbinit 裡面加上 set print pretty on 就是個不錯的開始,光這樣就能讓一些輸出漂亮很多(其實也就是塞在一行裡跟自動換行的差別XDDD),相對開了這個print 也會佔掉比較多行數。
一般除非是客製的資料結構又有大量debug 的需求,才會需要客製化pretty printer,像是gdb 內應該已內建C++ std 的pretty printer,Qt 的開發套件應該也有提供Qt 的;寫rust時所用的rust-gdb也只是對gdb 進行擴展,在開始時先引入設定檔,裡面也有Rust 的pretty printer。
# Run GDB with the additional arguments that load the pretty printers
PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" gdb \
  -d "$GDB_PYTHON_MODULE_DIRECTORY" \
  -iex "add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY" \
  "$@"

但一般人似乎不太用得到這個功能,所以這篇其實是廢文一篇(蓋章,感謝大家貢獻兩分鐘的時間給小弟的blog (炸。

2016年9月5日 星期一

使用 ctags 增強vim 的功能

vim 搭配 ctags 是一款生猛的工具組,可以快速在trace 專案尋找定義和實作,大幅增加vim 瀏覽程式碼的效率。
安裝方法,首先要安裝ctags ,archlinux 的話是ctags,ubuntu 的話是exuberant-ctags,其他的就…自己找。
在專案的根目錄中使用:
ctags -R

產生tags 檔,在瀏覽原始碼的時候,就能用:
Ctrl + ] 跳到該名稱的定義
Ctrl + t 跳回到剛離開的位置

另外在搜尋的時候,找到了一個taglist 的替代品tagbar,可以使用Vundle 安裝:
https://github.com/majutsushi/tagbar
在vimrc 裡面加上:
Plugin 'majutsushi/tagbar'
map <F12> :TagbarToggle<CR>
就能用F12 開關Tagbar 的視窗,第一眼看來還不錯,比taglist 還要漂亮跟清楚很多,據說相對taglist 對Cpp 的支援也更好;雖然以個人之前的經驗,taglist 沒有想像中的好用…也可能是我不會用吧。

有關Vundle 的相關資訊,請參考之前的文章:
http://yodalee.blogspot.tw/2015/03/vundle-vim.html

其實ctags 上使用一直有個問題,導致我之前都不太使用它:一般稍大一點專案都不會是一層,而是程式碼分到樹狀的資料夾中,用ctags -R 只會在根目錄上產生tags 檔,而通常寫code 的時候都不會在根目標上作業,否則要開檔的時候光打目錄就飽了;但如此一來vim 就抓不到tags 檔了。
後來查了一下vim wiki,發現只需要在.vimrc 裡面加上一行文就可以解決這個問題……
http://vim.wikia.com/wiki/Single_tags_file_for_a_source_tree
set tags=tags;
這樣vim 就會一路往上找tags 檔。

ps. 這樣就能修掉也太詭異了吧…