1.其實答案很簡單,只要安裝xcode or 像我一樣安裝third party tool “itools” 然後移到application的地方,選擇你想要觀看的程式名稱,即可以取出相關的log
2.
url :别用symbolicatecrash来解析crash log了 - Wonderffee's Blog
若 binary 是別人產生, crash log 也是別人產生時, 可以照以下的方式對回 symbols:
- 取得產生 crash log 對應 app binary 的 archive 目錄下的 dSYMs 目錄。
- 在命令列下輸入: $ mdimport dSYMs。
- 將 crash log 存成 xxx.crash (副檔名必須是 .crash)。
- 打開 XCode 的 Oraganizer, 點選 Library 下的 Devices Logs, 點選 Import, 選擇 xxx.crash。
結果會顯示在畫面右側。
2014年2月24日星期一
lldb to gdb command
官方連結: LLDB to GDB Command Map
在這備忘自己常用的指令:
- return EXPR -> thread return EXPR
顯示 step out 後的傳回值
出處:
#/usr/bin/env pythonimport lldb # Put _this_ file into ~/Library/lldb/thread_return.py# Put the following line into ~/.lldbinit# command script import ~/Library/lldb/thread_return.pydef __lldb_init_module(debugger, internal_dict):debugger.HandleCommand('command script add -f thread_return.thread_return thread_return')def thread_return(debugger, command, result, internal_dict):'''Prints the return value of the last function you stepped out from. This is very useful if the return was a complex expression. This lldb command prevents you from needing to create a temporary variable just to inspect the return value'''# If anyone knows a way of doing this without using needing to script lldb in python... target =debugger.GetSelectedTarget() process = target.GetProcess() thread = process.GetSelectedThread()print>> result, str(thread.return_value)return lldb.thread.return_value
2014年2月21日星期五
用 vim tab 批次處理多個檔案
最近才發覺 vim 有強大的批次處理功能, 相見恨晚啊。
在命令列同時開啟多個檔案到多個 tab
vim -p module/*.h
開啟 module 目錄下全部 .h 檔, 用一個 tab 編輯一個檔案。
需配合 tabpagemax 使用 (set tabpagemax=1000), 預設 tabpagemax=10, 不太夠用。
在 vim 內開啟多個檔案到多個 tab
:args module/*.h :argdo :tabe
效果同上一個作法。但 argdo 為了加速讀檔會先關掉 syntax, 要自己加指令開啟 syntax:
:args module/*.h | execute 'argdo tabe %' | syntax on
在 vim 內更改全部 tab 內容並存檔
:tabdo :% s/xxx/ooo/:tabdo :w
第一行對全部 tab 逐一執行 :% s/xxx/ooo/, 第二行則是逐一執行 :w。一但有 tab 無法執行指令, 就會停下來。
2014年2月19日星期三
git 的入門文件
記錄一下我學 git 的過程。這些文件看起來都滿不錯的, 應該也適用沒接觸過 DVCS 的人 (我先前有用 mercurial 的經驗)。
Pro Git
先在網路查些基礎指令 (介紹愈短愈好), 還有抄別人的 git config 簡化成自己的版本, 就這樣上工一陣子。
然後需要 merge branch 時, 看這本第三章講 branch 的部份, 有許多圖示說明 git 內部資料的變化, 一目了然。看不懂的時候就回頭翻第二章, 滿容易上手的。
Git Tutorials and Training | Atlassian
平時要用什麼指令就 google 一下, 再點進 stackoverflow 看別人的說明。這樣瞎用了一陣子後, 回來快速掃一遍這裡的說明, 才比較有系統地記下常用指令。
Btw, 以前學 mercurial 的時候是先有系統地掃一遍常用指令, 再開始用。這回學 git 反過來, 覺得這樣學也不錯。或許基礎比較紮實後, 學類似的新東西時, 採取要用再補的方式會比較有效率。
2014年2月9日星期日
從 third-party library 的 console 錯誤訊息找出更多線索
最近遇到一個不錯的小例子, 可以比較完整地記錄用到的相關指令。更多關於 gdb 的說明, 見 gdb 初步心得。
問題
Ubuntu 下執行程式後看到 "Gtk-WARNING **: cannot open display:", 然後程式就掛了。
解決過程
為了獲取更多線索, 我想找出是那段程式輸出這個錯誤訊息。由 "Gtk-WARNING" 可知是 Gtk+ 函式庫輸出的錯誤訊息。所以得先取得 Gtk+ 的 debug symbol 和原始碼:
$ dpkg -l | grep gtk ... ii libgtk2.0-0...
找出已安裝的 Gtk+ 函式庫叫做 libgtk2.0, 接著找出它對應的 debug symbol package 和原始碼:
$ aptitude search libgtk2.0 i libgtk2.0-0- GTK+ graphical user interface library ... i libgtk2.0-0-dbg - GTK+ libraries and debugging symbols ... i libgtk2.0-dev - development files for the GTK+ library ...
然後安裝 debug symbol, 還有取得 Gtk+ 的原始碼:
$ aptitude install libgtk2.0-0-dbg $ apt-get source libgtk2.0-dev
從取得的原始碼找看看有沒有 "cannot open display":
$ cd gtk+2.0-2.24.10/ $ grep "cannot open display"-R ../gtk/gtkmain.c: g_warning ("cannot open display: %s", display_name_arg ? display_name_arg :"");./gdk/gdk.c: g_warning ("cannot open display: %s", display_name ? display_name :"");./.pc/071_no_offscreen_widgets_grabbing.patch/gtk/gtkmain.c: g_warning ("cannot open display: %s", display_name_arg ? display_name_arg :"");./.pc/100_overlay_scrollbar_loading.patch/gtk/gtkmain.c: g_warning ("cannot open display: %s", display_name_arg ? display_name_arg :"");./ChangeLog.pre-2-12:* gtk/gtkmain.c:(gtk_init):Fix"cannot open display" error message
讀一下字串出現相關的位置, 找出函式 gtk_init_check 和 gdk_init_check。看來沒有找錯方向, 於是可以用 cgdb 執行程式, 進入 gdb 後輸入:
(gdb) b gtk_init_check (gdb) b gdk_init_check (gdb) directory /my/path/to/gtk+2.0-2.24.10/(gdb)set substitute-path /.../gtk+2.0-2.24.10/ /my/path/to/gtk+2.0-2.24.10/(gdb) r
- directory 用來載入 Gtk+ 原始碼。debug symbol package 裡沒有含這部份。
- 編譯封裝 Gtk+ 的路徑十之八九和我放 Gtk+ 原始碼的路徑不同, 所以要用 set substitute-path 替換路徑, gdb 才知道如何顯示對應的原始碼。上面的 "..." 是執行 backtrace 時看到的路徑, 這裡簡化用 "..." 表示。
再來就在中斷點附近輸出收到的值, 看看 backtrace, 看看附近的原始碼, 獲得更多線索後就解決問題了。原來是我忘了呼叫 setenv("DISPLAY", ":0"), gdk_init_check() 發現無法產生 GdkDisplay, 所以就掛了。
2014年1月30日星期四
初次使用 paper prototyping 做使用性測試
雖然時常看到有人推薦用紙筆畫 app 介面做使用性測試, 但是要自己動手畫重覆的東西, 實在是提不起勁。前陣子聽阿修介紹 Paper Prototype, 也有同事動手畫了幾頁, 實際跟著流程走一次, 感覺還不錯。剛好我有在 iPad 裡裝 POP, 當場用 POP 結合同事做的紙張雛型, 互動效果出乎意料的好。
最近想幫爸媽做一款「待做事項」的軟體, 順便練習從頭開發一款 app 要經過的流程。中午花了大概一小時畫草稿加上用 POP 制作雛型, 過程滿順暢的。下面是部份原稿和 POP Android 版的畫面。
和爸媽分別測試後有找到一些小問題, 不過確定大方向應該可行。後來找我哥測試, 原以為應該會更順暢, 但是因為我哥使用 iPhone, 我是針對 Android 畫的草圖, 結果不容易融入情境。再加上我哥不是我的目標對象, 主要操作流程和他的期望不同, 也影響測試的流暢度。
歸納一下目前對使用 POP 做 paper prototyping 的看法。
目的:
- 做使用性測試。了解使用者是否了解如何操作。功能是否有滿足使用者的需求。
注意事項:
- 需要篩選目標使用者。
- 測試前要做情境描述, 使用者才能進入狀況 (看著草圖想像要做的事)。
- 盡量觀察使用者的反應, 少開口提醒。
優點:
- 制作速度快, 適合早期討論, 甚至能在開會中當場做。
- 易於專注在操作流程, 不會討論配色、字型、畫面排版等細節。
- 任何人都可以製作, 工程師和設計師以外的人也可藉此展示自己的想法。
限制:
- 只適合能融入情境的使用者。也許可透過幾次體驗後克服。
- 還是有可能插題討論圖示, 所以也不能畫得太糟。
總結來說, 現在我覺得用 POP 做 paper prototyping 滿快的, 但若是只用紙張互動, 一來我做得比較慢, 二來使用者也比較難聯想。我自己不適合用純紙張版本的 paper prototyping。
附帶一提, 做得過程中要自己用紙筆畫出概要, 才留意到一些 Android UI 的小細節。還有實際測試流程不像影片裡那樣順。看了再多資料, 還是得親身走一遍才行。
2014年1月27日星期一
C++ 下 thread-safe 的 lazy initialization
在 《clang 避免 non-local static 物件初始化順序的方法》提到可用 static local variable, 然後用 method 傳回的方式避免產生 global static variable (藉此避免不同編譯單元的初始化問題)。以下是一個例子:
conststructPoint* center(){staticPoint* s_center =CreateCenterPoint();return s_center;}
但是, 在 multi-thread 的環境下, s_center 可能被初始化兩次。假設 CreateCenterPoint() 會傳回不同的值, 或是外界可以改變取得的 Point, 有兩份 Point 會造成問題。
好消息是:
- gcc 預設有幫 local static variable 加入 thread-safe 的保護。
- 若 compiler 有正確實作 C++11, 在 C++11 的規範下, 有保證靜態變數的初始化是 thread-safe 的。
所以情況比想像中的安全。
不過, 其它 lazy initialization 的實作方式有可能出錯。更一般化的 lazy initialization 會用 Double-Checked Lock Pattern, 但是這個作法有不易察覺的漏洞。
直覺的作法如下:
Singleton*Singleton::instance(){if(pInstance ==0){// 1st testLocklock;if(pInstance ==0){// 2nd test pInstance =newSingleton;}}return pInstance;}
注意初始化 singleton 是三個步驟組成的:
- 配置一塊新記憶體
- 初始化新記憶體
- 將新記憶體的位置指向目標指標 (即 pInstance)
compiler 有可能更動三者的順序。最壞的情況下, thread A 執行了 1, 3, 還沒執行 2, 這時 thread B 發覺 pInstance 不是 0, 於是回傳尚未初始化的 pInstance。解法是要使用 memory barrier。或是在 C++11 後更可用跨平台的解法。詳情見 Double-Checked Locking Is Fixed In C++11
另外, Java 1.4 以前 Double-Checked Locking 也有問題。Java 1.5 後多了 volatile 表示「取出最新的值」, 才有辦法修正此問題。Effective Java 2/e Item 66 "Synchronize access to shared mutable data" 和 Item 71 "Use lazy initialization judiciously" 有詳細的討論。
2014年1月20日星期一
尋找 memory error 的強力工具: Address Sanitizer (ASan)
官網有很詳細的介紹, ASan 可以偵測出以下的問題:
- Use after free (dangling pointer dereference)
- Heap buffer overflow
- Stack buffer overflow
- Global buffer overflow
- Use after return
都是出錯時很難察覺, 之後會造成奇異的行為, 或是讓程式掛在莫明奇妙的地方。
clang 3.1 和 gcc 4.8 開始內建 ASan 的功能。號稱執行速度平均慢兩倍, 比起 Valgrind 的 20 倍, 實在相當誘人。這裡有說明 ASan 怎麼做的。這樣在 Linux 上也有不錯的 memory error detector 了。不知什麼時候能追上 OS X 的腳步。
除了 Chromium 有用之外, Firefox 也有使用。
clang 避免 non-local static 物件初始化順序的方法
C++ 沒有定義 non-local static 物件在不同編譯單元 (translation unit) 之間初始化的順序, 所以要極力避免 non-local static 物件相互間的存取。
今天試用 clang 編譯程式, 發覺它有個不錯的選項: -Wglobal-constructors。用這選項編譯, 遇到有 non-local static 物件會依賴其它函式 (包含 constructor) 設值時, 會輸出 warning。
這裡引用 Address Sanitizer 提供的例子:
$ cat a.cc #include<stdio.h>externint extern_global;staticint __attribute__((noinline)) read_extern_global(){return extern_global;}int x = read_extern_global()+1;int main(){ printf("%d\n", x);return0;} $ cat b.cc int foo();int foo(){return42;}int extern_global = foo(); $ clang++ a.cc b.cc &&./a.out1 $ clang++ b.cc a.cc &&./a.out43 $ clang++ a.cc b.cc -Weverything a.cc:6:5: warning: declaration requires a global constructor [-Wglobal-constructors]int x = read_extern_global()+1;^~~~~~~~~~~~~~~~~~~~~~~~~1 warning generated. b.cc:3:5: warning: declaration requires a global constructor [-Wglobal-constructors]int extern_global = foo();^~~~~~1 warning generated.
由上可知, 編譯 a.cc 和 b.cc 的順序不同, 輸出的結果不同。加上 -Weverything 後, -Wglobal-constructors 有在第一時間抓出有問題的部份。附帶一提, clang 的錯誤訊息不止有標示錯誤的位置, 而且還是彩色的!
解決這個 warning 的方法, 和 Effective C++ Item 4 的說法一樣, 就是改用函式傳回 local static method, 這樣就會依執行的順序在執行期間初始化。不過實際情況稍微複雜了一點, 後述。
clang 另有一個參數 -Wexit-time-destructors, 會找出在結束程式時執行 destructor 的物件。雖然 destructor 執行的順序有明確的定義 (和初始化的順序相反), 不過開發者八成沒有考慮週全, 很容易在一連串 destructor 執行中用到已執行完 destructor 的物件。這個問題和 non-local static 物件初始化一樣棘手。
clang 的解決方案一樣單純: 「本來無一物, 何處惹塵埃。」統統不準用, 就不會出亂子。
$ cat p.cc structPoint{Point(){}~Point(){}int x, y;};conststructPoint& center();conststructPoint& center(){staticPoint s_center;return s_center;} $ clang++ p.cc -c -Weverything p.cc:12:16: warning: declaration requires an exit-time destructor [-Wexit-time-destructors]staticPoint s_center;^1 warning generated.
那要怎麼解決這個 warning 呢? 就是 new 一個物件, 並且不要釋放它。若懶得修改已經存取它的程式, 可以這麼做:
conststructPoint& center(){staticPoint& s_center =*newPoint();return s_center;}
在這兩個 warning 的夾擊下會少掉很多難以察覺的錯誤, 不過寫程式時也會有一點點不便。比方說需要用到常數字串時, 不能直接寫
const std::string kMyString ="...";
得改用
constchar* kMyString ="...";
對於用 std::string 做為函式參數或 STL container 的物件, 得付出一點生成 std::string 的成本。
2014年1月5日星期日
使用 CSS position 組合 div 的佈局小技巧
這篇談的方法在舊的瀏覽器大概會出包, 沒有實際研究。跨瀏覽器的議題太複雜, 希望在我不得不面對這議題前, 跨瀏覽器的痛苦可以減少很多 (大概要等 Windows XP 絕跡吧)。
以下圖為例, 這篇討論兩個使用 CSS position 的小技巧。
使用 relative position 重疊版型
上圖的書本和內文, 實際上是兩個 div 組成的, 還原後如下圖:
組合的方式如下:
HTML
<divclass="wrapper"><divclass="content"></div><divclass="book"></div></div>CSS
.wrapper { height:750px; overflow: hidden;}.content { height:500px; position: relaltive; z-index:1;}.book { height:700px; width:500px; position: relative; top:-600px;}數據是我大概填的, 示意用。
幾個要點:
- .book 用 position: relative 從原有的位置往上移到 .content 做為背景。
- 為了讓 .content 在上方顯示, 設 z-index: 1。由於 z-index 只有在 non-static position 下才有效, 所以改一下 position。
- .book 使用 position: relative 往上移後, 仍會在原位置留下空間, 就像 visibility: hidden 一樣。為了避免占用原位置的空間, 限製 .wrapper 的 height。
缺點是必須限制高度, 不像一般網頁彈性地隨內文增加而自動增加高度。
使用 absolute position 疊出下方的書緣
下方展開後, 中間長這個樣子, 我隱藏了左側一個 div, 方便看拆開的 div。
HTML
<divclass="page-container-bottom"><divclass="page-bottom page-bottom-left"><divclass="page-bottom-inner page-bottom-left-inner"></div></div><divclass="page-bottom page-bottom-right"><divclass="page-bottom-inner page-bottom-right-inner"></div></div></div>CSS
.page-bottom { height:30px; position: absolute; bottom:0; background-color:#8F8F8F;}.page-bottom-left { border-top-right-radius:90px30px; width:50%;}.page-bottom-inner { height:15px; width:100%; bottom:0px; position: absolute; background-color:#FFF;}.page-bottom-left-inner { border-top-right-radius:90px15px;}CSS 部份只列出左半邊, 右半邊作法一樣, 只是改用 border-top-left-radius。
幾個要點:
- 左右各一個 div, 外層的 div ( .page-bottom ) 用 position: absolute + bottom: 0 做到貼齊下緣的效果。注意使用 position: absolute 的 tag 會跟著上層第一個使用 non-static position 的 tag, 在這個例子裡, 是前面提過的 div.book。
- 內層的 div ( .page-bottom-inner ) 用一樣的方式貼齊底部, 但是 height 比較矮, 且圓角弧度比較小。兩者疊出書緣的效果。
用 position: absolute 貼齊底步, 做起來出呼意料地容易。