2016年5月13日 星期五

在X86機器上debug ARM 執行檔

以後有可能會用到,寫這篇純粹做個記錄。

故事是這樣子的,最近閒來無事研究一下傳說中 jserv 大神的amacc,有些地方實在看不出程式執行至此時一些變數的值為何,這時我們就要用gdb 了
不過amacc 是用 arm-linux-gnueabihf-gcc 編出來的arm 執行檔,我們host gdb 是X86 在執行時就會報錯:可執行檔格式錯誤
如果用arm-linux-gnueabihf-gdb 呢:它會寫 Don't know how to run.

我們需要用到server-client 的架構,server 端在實體target 上可以用 gdbserver,這會需要對gdb-server, gdb 特別編譯;gdb configure target 為arm-linux-gnueabi,gdbserver configure host 為arm-linux-gnueabi。
如這篇所述:
https://sourceware.org/gdb/wiki/BuildingCrossGDBandGDBserver

我們可以用qemu 的 debug 來代替gdbserver:
首先執行qemu ,指定debug 的port 為9453:
qemu-arm -L /usr/arm-linux-gnueabihf -g 9453 amacc tests/shift.c

在另外一個終端機,打開arm-linux-gnueabihf-gdb,這是已經configure target為arm-linux-gnueabi 的gdb:
arm-linux-gnueabihf-gdb ./amacc tests/shift.c

在gdb 裡面連接remote target:
target remote localhost:9453

再來就能用c 開始跑了,happy debug。

相關參考:
http://kezeodsnx.pixnet.net/blog/post/31901130-gdbserver-remote-debug-%E6%B8%AC%E8%A9%A6

2016年5月10日 星期二

使用Rust generic 實作九九乘法表

之前看到一些瘋狂的程式設計師,用C++的template實作九九乘法表,大體的概念就是利用編譯器展開template ,並設定特化的template 作為終止條件,像這個:
用 C++ template 實作九九乘法表

我們可以用 Rust 的generic type 做到類似的事情,概念是一樣的
試過用Rust 的Macro 來寫,這樣會遇到問題,如下如果我們接到一個table,對它展開:
macro_rules! node {
  ( $x:expr, $y:expr ) =>; {
    println!("{}*{}={}", $x, $y, $x*$y);
  }
}

這樣一個node 是沒問題的,但在進行row 展開時,macro 並不像template 一樣會對特化優先匹配,因此
macro_rules! row {
  ( $x:expr, 1) =>; {
    node!($x, $y)
  };
  ( $x:expr, $y:expr ) =>; {
    row!($x, $y-1)
    node!($x, $y)
  };
}
是沒有用的,編譯時仍會在展開 row! 那行發出警告recursion limit reached。

用generic 的function 來寫,其實就一般般,我們定義Node, Row, Table function,使用型別T 作為generic型別,Table 在row 不為一的時候會特化為row 少一的 Table 和Row;Row 在col 不為一的時候會特化為col 少一的Row 和Node,反之則只特化為Row 跟Node。
為了特化時的運算,我們對型別 T至少要有下面那一大排的要求:

  • Copy: 可複製,否則類似下面的row 被傳入Row 跟Node function 中,use moved value
  • PartialOrd: 可比較大小,用在 col > T::one()
  • One: T 有定義 one(),對大多數的Rust primitive type,都有定義Zero 跟One 兩種屬性,可以用在Add 跟 Mul,即Add T::zero() 其值不變,Mul T::one() 其值不變,不知道為啥Rust 核心對這部分好像會大改,用了它們也要在程式加上#![feature(zero_one)]才會動
  • Sub<T, Output=T>: T可以減T 得到一個T,為了下面的 col-T::one(),
  • Mul<T, Output=T>: T可以乘T 得到一個T,為了最裡面的 row * col = (row * col)
完整的Row 程式為:
fn Row(row: T, col: T) {
  where T: Copy+PartialOrd+One+Sub+Mul+Debug {
  if col > T::one() {
    Row(row, col-T::one());
  }
  Node(row, col);
  print!("\t");
}
Table function 類似,只是判斷對象變成row;Node function 則是印出結果。主程式就呼叫 Table(9u32, 9u32) 就能看見漂亮的九九乘法表了。

怎麼說呢,感覺跟C++ template 的寫法還是有點不同的感覺呢…

2016年5月9日 星期一

Modern C

本書的全文在網路就下載得到,目前也只有英文網路資源,沒有出實體書和翻譯本的樣子:
http://icube-icps.unistra.fr/img_auth.php/d/db/ModernC.pdf

從書名和它的序言,作者旨在對 C language 有個基礎的介紹,由於C 的<簡單>,能讓程式設計師快速寫出可以動的程式,例如Hello World,反倒使人忽略了存在C 背後種種的議題;同時 C經歷多次標準改進,和當年的 K&R C 已經頗有出入,本書從 level 0 - 4,從簡單到複雜再次檢視C 語言中的概念。

架構其實沒差太多,從控制結構、資料型別、array, struct, enum 等等,書中不時列出一些 <Rule>做為重點提示,像是建議、警告,和一般C 語言的書比較不同是,它會去討論一些背後的概念,例如unsigned int 的值是如何得來;comma operator 回傳最後一個expr 的定義不小心會讓你debug 超久;各種資料型別極限值與轉換時數字的變換…

雖然個人寫C 也有一段時間,不過重新細看書內的介紹,還是會發現一些之前沒想過的陷阱,有些章節例如第九章是C Coding Style,可能會引發宗教戰爭(X,覺得已經純熟的章節可以看過去就好。

最後的 Level 4: Ambition 可能是我目前看過C language 相關最有野心的一章,大多數 C lang 的書只專注在「把C 講好」這件事,Ambition 這章跟現行的C 語言無關,而是提出作者的見解:如何修改標準「讓C 更好」。
如果只是想更親近C 語言的人,可以明正言順的略過這章,畢竟這章節需要對C 標準、編譯-器實作與程式最佳化有更多認識後,才能理解箇中大意與作者意圖,不然只是看著書中列出一段C standard,說應該改成怎樣怎樣,應該不用五分鐘就可以安然入眠了,個人最後也是看得一頭霧水QAQ

要我說對這本書的整體心得,該說隨著C/C++ 標準進化的同時,更適合的寫法也推陳出新,絕對避免的寫法也所在多有,但很無奈的為了相容之前的標準,過去的用法會一直留在那裡,等待天真不知情的新手程式設計師去踩雷然後~~~~~~~~EXPLOSION~~~~~~~~
學習C/C++的問題並不是學不會什麼生猛功能,而是要在各種實作的方法中學習:「怎麼用比較<適合>的方法來實作,避免哪些有問題的寫法,比較好的方法和概念是什麼?為何如此」
像是避免用scanf 造成overflow 的bug,這需要程式設計師去學習正確的寫法;之前所寫的option type,強者我同學的註解:「C++ boost 已經收入option,之後很可能也會收入標準」
但null pointer 在C++中仍然允許,寫出Girl *gf = nullptr 讓 nullptr 把程式炸了無疑是合法的,畢竟C/C++ 都假定程式設計師是聰明人,會對自己寫的code 負責。

這本書前半部未必適合新手程式設計師,比較適合已經有些了解的人,再次檢視自己所學的內容;後半部則適合對C, OS, compiler 都有詳加研究的,栽進去與作者一同讓這款有44 年歷史的語言更加完善。

2016年5月4日 星期三

Python 參數產生unittest

最近在寫傳說中 jserv 大神的amacc,一個奉行「極致的簡」的C compiler
參與專案的第一步當然是先看issues,那時剛好看到這個:
https://github.com/jserv/amacc/issues/13
看起來不算太難,改一下code 執行流程就好了

不過首先,這個project 有點難以測試,的確make check 會嘗試編譯所有tests/*.c 檔,並且用jit 和產生elf 的方式去執行,可是卻不會通知結果是不是正確的,nested for loop 的結果也會和其他檔案混在一起,直接下指令去測又很麻煩(要用qemu,指令好長…)

覺得測試這麼麻煩,連帶著改issue 也好麻煩的感覺,因此決定不想改了先來幫這個project 加上一些便於測試的功能

想到test 就想到python unittest,設計概念是:利用python subprocess 去呼叫amacc跟arm-linux-gnueabihf-gcc 分別編譯執行檔,執行後,直接比較兩者的output,看結果是否相同,如此就能驗證amacc 的行為(這有個問題是gcc 未必遵照C standard,真正正確的答案應該要看C standard 才對,不過我們假定大多數狀況gcc會遵照C standard)

這裡遇到一個問題,在tests 裡面有許多的c files,因為對每個檔案要做的事情其實是一樣的,自己寫只會變成複製貼上,還可能貼錯內容;寫一個test 去跑所有檔案也不行,這樣等於是所有測試混在一起,test 沒過可能是任一個檔案有錯,根本看不出是誰錯了。

所幸這個解法也不困難,簡單google 就找到了:

概念是,本來的module 變成一個容器,裡面要自己寫的function 就不寫了:
class TestAmacc(unittest.TestCase):
  pass

另外寫一個共同的test generator,會傳回測試的function,我這個test function 裡面就是本來要對檔案做的事:amacc編譯、gcc 編譯、執行、比較結果,參數f為要執行的檔名:
def testGenerator(f):
  def test(self):
  # test function here
return test

最後是對每個檔案去呼叫這個generator,利用setattr 的方式,把這個function 督進先前宣告的容器當中,python 的彈性由此可見:
for dirpath, _, filenames in os.walk("tests"):
  for f in filter(lambda name: namePattern in name, filenames):
    testfile = os.path.abspath(os.path.join(dirpath, f))
    test_func = testGenerator(testfile)
    setattr(TestAmacc, 'test_%s' % (os.path.splitext(f)[0]), test_func)
unittest.main(argv=[sys.argv[0]])

如此就能自動產生test case 了,如上所示,還可以幫檔名加上filter,用參數來指定要跑什麼測試,極度方便;這裡遇到一點卡關是,unittest 預設會吃主程式的argv作為要跑的test module 名稱,argv 加上參數的話會讓unittest 炸開,必須要避免unittest.main 吃到不是給它的argv 參數以,免它找不到測試module 直接回報出錯。

2016年5月1日 星期日

退伍感言

原文寫於大兵日記最後一頁,寫作角度其實不全然是當兵,比較像是在部隊的退伍感言,本來應該是退伍當日要PO 的,不過你知道的……懶嘛日記要先打好再來打退伍感言

記得去年5月入伍,正是天氣開始轉熱之時,天氣熱得要死,新訓單位還一堆蚊子,要是有登革熱我敢打賭那裡一定大感染,那時我就對自己說:「天氣越冷越好,因為當天氣冷過開始回暖,就是我要退伍了」,雖然後來冷到2度真的是有點太冷了Orz
很多人會跟我說:「好好我要退伍了」我很懂那個感受,我剛到部一個星期就有人退了,我也跟他怎麼說過,日子就是一天一天過,回頭一看突然就要退了,這就是我對當兵這年的評語:度日如年、度年如日

很幸運當初以四分之一的神抽,抽中金門,前一位抽到東部海巡署一直高興的跟我握手,其實怎麼想金門都比東部好,回台灣搭個飛機噗茲一聲就到了,不像東部搭火車四個小時跑不掉,也沒有離島這樣的長假可以放,軍包機可以搭;抽中的連隊不需要戰備,雖然一到部就連續三個星期沒放假,但任務過後,平常有充分的時間做自己想做的事,伙食也讚
到部前吃過新訓、伙食外包的南通校、罐頭天堂前運隊,沒一個地方的伙食比這裡好,到部第一天早餐吃漢堡,中午進伙房還看到烤盤上滿滿的雞腿都快驚呆了,旁邊忘了哪位還不放過我這個菜逼八,還問:「你看啦早餐吃這樣像話嗎?」請問到部一天的單兵如何回應?這單位的伙食也太誇張了吧

當兵到現在學會:用槍、爬電線桿、在連上當行政學會記帳和財務,曾經拿了兩張紙匯出1,000,000元現金;曾經在大家面前從電線桿上摔下來;曾經在跨年時站哨跨年,看天空隨煙火一明一暗;曾經為了教召2320回營,接著還要站24-02;也曾經情緒失控對著長官大吼大叫;工作壓力太大在被輔導長念到淚灑行政室。
除了部隊的事,在放假期間也沒閒著,感謝班長慷慨借我腳踏車(well 其實班長根本沒在騎,我不騎它就變廢鐵了),它陪我度過無數次島休和洽公,騎著腳踏車環島金門一圈,每日往返部隊與市區;入伍投稿先前量測的電路,利用放假的時間在網咖修改paper,印著reference 跟稿件回營閱讀與修改,終於在3月初將完稿給投出去,只要有心(也許還有沒遇到基地XD),無論看書還是開手機練功,在部隊平時的時間其實很多,可以做到很多平常不會做的事。

當兵一年讓我經歷這些、學會這些,儘管這一年漫長而辛苦,我仍相信收穫多於付出,未來很可能不會再與部隊接觸,這段記憶仍會刻骨銘心
末尾,很高興當初抽中這裡,沒有在海巡署虛度光陰,也要謝謝9個月來大家的關照,今天我要退伍了,明天開始會努力向下個人生目標邁進