2016年2月3日 星期三

GNU GLOBAL / GTAG : 靜態程式碼分析的王者

為何要說 GNU GLOBAL / GTAG 是靜態程式碼分析的王者呢?

相較於 cscope / gnu cflow / ctags / etags 等類似的解決方案,它可以正確的得到巨集展開的結果,而且有 incremental 的更新,在 code base 會變動時是一大利多啊~~

http://www.gnu.org/software/global/global.html 官網,感覺是原作者捐給FSF的

http://www.gnu.org/software/global/globaldoc.html 說明文件

http://ldshyu.blogspot.tw/2013/06/coding-tag.html 中文的各tag系統比較

https://github.com/whatot/whatot-dot/blob/master/vim/global.md 中文介紹

執行 gtags 及 htags 之後,會產生 HTML 目錄,它的子目錄有以下內容:

J: #include 交互參考資料,此標頭檔在什麼檔案的什麼位置被參考到了

D: identifier 交互參考資料,此符號在什麼檔案的什麼位置被參考到了

S: html 化的原始碼

2016年2月2日 星期二

大型專案的原始碼分析--兼論 preprocessor

在分析原始碼的時候,遇到巨集定義總是令人又愛又恨;愛的是它在寫得好的時候(或讀的人狀況好的時候XD)是可以增進理解;恨是是當它寫得不好,或已經看得很累的時候,這時經常理解不能(冏rz)...

可TMD那精美的死線還是擺在那裏啊,怎辦呢?只好直面 preprocessor  了!

(先導知識: GNU: The C Preprocessor 導讀 )

方法一:改寫 Makefile 以展開所有巨集,順便得到 #include 路徑


例如,原來 Makefile 當中有以下指令:
.c.o:
@$(MKDEPDIR)
$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $(PROFILING_CFLAGS) $<


將上述指令改寫
.c.o:
@$(MKDEPDIR)
$(CC) -E $(CPPFLAGS) $(ALL_CFLAGS) $(PROFILING_CFLAGS) -o $@ $<

然後在 terminal 中,執行以下指令
rm *.o
make
rename -v s/\.o/\.i/ *.o

如此可得到副檔名為 *.i 的展開的原始碼,所有的 #include 還有巨集定義等等都被展開了。還有一些 gcc 的參數可以讓輸出比較好看,例如 -CC 可以保留註解等,就按各人需要調整囉~~

(正統的做法參見 http://www.gnu.org/software/cflow/manual/html_node/Makefiles.html#Makefiles)
(前提是你要很熟 Makefile 的語法)

為何需要這個呢?因為目前基於 tags 的原始碼分析程式,無論是 ctags, cscope, gnu cflow ,遇到 INLINE 的函數都失效,只好暫時先這樣處理…但很奇妙的 gnu global / gtags 可以正確處理 ,之後肯定要專文介紹一下

方法二:利用之前執行 make 觀察 stdout 得到的 -I 參數,以 --cpp -I 參數執行 gnu cflow


根據先前執行 make 的螢幕輸出,得到 -I 參數,以 emacs 24.5 的原始碼為例:

cflow --cpp -b --emacs -T \
 -I. -I../lib -I./../lib -I/usr/include/gtk-3.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/pixman-1 -I/usr/include/libpng12   -I/usr/include/freetype2       -I/usr/include/libxml2   -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include   -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include   -I/usr/include/freetype2   -I/usr/include/freetype2 -I/usr/include/p11-kit-1 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include \
 vm-limit.c dispnew.c frame.c scroll.c xdisp.c menu.c xmenu.c window.c charset.c coding.c category.c ccl.c character.c chartab.c bidi.c cm.c term.c terminal.c xfaces.c xterm.c xfns.c xselect.c xrdb.c xsmfns.c xsettings.c gtkutil.c emacsgtkfixed.c dbusbind.c emacs.c keyboard.c macros.c keymap.c sysdep.c buffer.c filelock.c insdel.c marker.c minibuf.c fileio.c dired.c cmds.c casetab.c casefiddle.c indent.c search.c regex.c undo.c alloc.c data.c doc.c editfns.c callint.c eval.c floatfns.c fns.c font.c print.c lread.c syntax.c unexelf.c bytecode.c process.c gnutls.c callproc.c region-cache.c sound.c atimer.c doprnt.c intervals.c textprop.c composite.c xml.c gfilenotify.c profiler.c decompress.c xfont.c ftfont.c xftfont.c ftxfont.c fontset.c fringe.c image.c xgselect.c terminfo.c lastfile.c \
 -o emacs.b.cflow

2016年1月31日 星期日

15分鐘學會 emacs (in my dreams)

http://emacs-doctor.com/learn-emacs-lisp-in-15-minutes.html

15分鐘…話是這麼說啦,但是總覺得…不太實際啊XD

不過順便自賣自誇一下 3 年前寫的 emacs 24中的lisp程式碼導航(code navigation)--Slime-style ... 這篇所介紹的 elisp-slime-nav ,搭配上 Xah 寫的 How to Edit Lisp Code with Emacs ,我覺得是很有幫助的,尤其是前者,它連你要找的 elisp 原碼是用C寫的都知道,而如果你真的給它原碼的位置,它也真的會幫你找到。真是太神啦~~


2016年1月30日 星期六

The Roots of Lisp 讀後心得

兩年前我曾經想讀這篇 The Roots of Lisp (ref. 使用perl及mozrepl操控firefox,以及repl的引入 ), 今天再細讀了一次,總算大概明白了作者想要講的東西。

資料(data)可以變成程序(procedure)

因為資料是演算的對象、結果(=>客體),如果我們可以將它變成 procedure ,那麼 procedure 就變成是有因時制宜能力的、有智慧的程式,那不就是人工智慧(AI)了嗎?以結果論的話,那就是程式產生程式。至於中間是一定要經過演算的,那麼演算的對象、結果是以資料的形式存在,如何將結果用來產生程式?那就是 The Roots of Lisp 當中的 "eval." 求值函數的作用(之一)了。

你可能會問說那 procedure 可不可以變成 data 呢?當然可以,不過那是沒有意義的,因為 procedure 是演算的主體,它本身不承載演算的對象、結果,因此演算前後它不會變動,那麼把它變成 data 是無謂的。

當然以 John McCarthy 的所處年代,他看到的東西未必和 Paul Graham 或今日的我們看到的東西是一樣的。他做的是試圖把演算"公理化",而結果就是一套演算法/公理,只要基本的 7+2 個基本算子,就可以建構出任意複雜度的演算/程式。求值函數除了求得函數值以外,還可以把資料(函數的定義)變成程序,而在執行期賦予程式高度的可變性。

所以其實到 programming 這端其實有沒有用 eval. 其實已經不重要,在整個 Lisp 的環境中, eval. 已經默默的工作了許久了…


2016年1月29日 星期五

Crash Course in Spacemacs


  1. Button 'SPC' = M-m in vanilla Emacs mode (why oh why should I spend 10 minutes googling it...)
  2. check it out: https://github.com/syl20bnr/spacemacs/blob/master/doc/QUICK_START.org
  3. ditto           : http://thume.ca/howto/2015/03/07/configuring-spacemacs-a-tutorial/

2016年1月28日 星期四

純 C 語言專案的 call graph 產生方式

參考 分析函數調用關系圖(call graph)的幾種方法 可以有個概念,目前傾向於使用 gnu cflow ,因為得到的是文字檔,既可直接進行文字註解,也可以產生圖檔,較富有彈性。安裝及使用可參考 Call Graph for C - cflow 。更詳細的說明可參考 好用的 Trace 工具 cflow 。當然,如果只有做到這樣,那 emacs 就沒有什麼好拿來說嘴的了;請安裝 cflow-mode ,直接在 call graph 中進行原始碼跳轉。直接在 emacs 中產生 cflow 的 call graph ,也只要寫個腳本就好了。

emacs的cflow-mode的快速鍵有這些:
key             binding
---             -------

TAB cflow-hide-or-show-subtree
SPC cflow-scroll-other-window
1 delete-other-windows
? cflow-get-stack
E cflow-edit-out-full
R cflow-recursion-next
b cflow-backward-same-level
d cflow-display-function-other-window
f cflow-forward-same-level
n cflow-next-line
o cflow-find-function-other-window
p cflow-previous-line
r cflow-recursion-root
s cflow-find-function
u cflow-up-level
x cflow-goto-expand
DEL cflow-scroll-other-window-down

以 emacs 24.5 的原始碼來說, call graph 是非常巨大的,建議一定要加上 -b 參數; reverse graph 的話,一樣很巨大,除了加上 -b 參數這條路之外,-d 2 也是一個選項,不過減少的檔案大小大概只有25%左右,但少了一堆資訊,看各人的需求囉~~


P.S.為了在源碼間跳轉的話,可以使用 cscope ,參考2013年的拙作: ubuntu上為emacs安裝cscope以進行code navigation

2016年1月27日 星期三

git "daily workflow"


一早起來,手上的程式還沒 push , repo 的版本已經往前狂跳了(驚),怎麼辦?

兩年前我寫過這篇:

不過時間隔太久了,我竟然要 google 才抓得到這篇文。顯然對於每日開工的流程有必要好好重新整理啊(抓頭)~~

正常來說,工作到一個預定的 milestone ,一定會主動進行 git push ,這你知、我知、獨眼龍也知,所以應該不會出現在一天開工的時候。開工時發現 repo 上的變動,大概可以分成三種:不需要 merge 、 不想 merge (喂) 、 有 conflict 需要 merge  。
  • Scenario I: Nothing serious done...(那就沒什麼好擔心的啦~~)
    • git pull origin master
  • Scenario II: Something done, wanna undo, no conflict => rollback changes (這什麼情況?就是人家做得好,直接接收啦~~)
    • git reset --hard HEAD^ 
      • 恢復到本地端最後一次 commit 前的狀態, 但修改過的檔案仍會還原
    • git pull origin master
  • Scenario III: Something done, conflicts BIG TIME !!
    • IIIa. just merge it (這什麼情況?就是人家做得好,merge的部分好解決,直接接收啦~~)
      • git pull origin master
        • merge XXXX...YYYY
    • IIIb. stash->rebase->reset (merge 會很麻煩,剛開工先不要處理這麼硬的工作,晚點再說啦~~)
      • git add .
      • git commit -m "push to stash"
        • 這個動作很重要,任何pull之前,要確保本地變動有存下來!!
      • git pull --rebase origin master
        • merge XXXX...YYYY (保留 repo 的版本,捨棄 local 的變動)
      • git reset --soft HEAD~1 
        • 恢復到本地端 commit 前的狀態, 但修改過的檔案仍不變
參考資料: