2012年8月17日 星期五

typeid in c++11

在 C++11 裡面把 typeid 拔掉, 取而代之的是 decltype

所以如果 code 有到的話

目前過渡期解法就在 Compile Flag 加個 `-Dtypeof=decltype` 吧

範例:
g++ -std=c++0x -Dtypeof=decltype foo.cpp
g++ -std=c++11 -Dtypeof=decltype foo.cpp



但如果 compiler 跟你抱怨 rvalue 不能轉 T& 的話...就用下面這有點邪門的解法吧


首先先在 code 頂端或著某個一定會 include 的 header 檔加入以下的 code
template<class T> struct unref     { typedef T type; };
template<class T> struct unref<T&> { typedef T type; };

然後編譯選項換成下面這樣
g++ -std=c++0x -Dtypeof\(x\)="unref<decltype(x)>::type" foo.cpp
g++ -std=c++11 -Dtypeof\(x\)="unref<decltype(x)>::type" foo.cpp

c++ 真的是越來越難學易用了XD...

參考

How can I use decltype to get the type of a reference? (from Stackoverflow)

git diff 使用 GUI 工具 diffmerge

在 git 使用時 git diff 是個很常會使用指令

但 git diff 是在 console 底下, 在某些情況下可能會有些吃力


這時候如果有 GUI 工具可以輔助的話效果會不錯


上面的圖示就是使用 diffmerge 的畫面, 左邊那調紅色代表兩邊不一樣的地方
左邊是舊有的右邊則是目前的, 並且右邊的視窗可以編輯

設定

而要使用 diffmerge 當 difftool 的話輸入以下指令設定
git config --global diff.tool diffmerge
git config --global difftool.diffmerge.cmd 'diffmerge "$LOCAL" "$REMOTE"'
git config --global merge.tool diffmerge
git config --global mergetool.diffmerge.cmd 'diffmerge --merge --result="$MERGED" "$LOCAL" "$(if test -f "$BASE"; then echo "$BASE"; else echo "$LOCAL"; fi)" "$REMOTE"'
git config --global mergetool.diffmerge.trustExitCode true

而之後使用 git difftool 或 git mergetool 則可呼叫出 diffmerge!

參考資料

Using DiffMerge as your Git visual merge and diff tool

2012年8月15日 星期三

GCC : Control Flow Graph

趁著記憶新鮮的時候先來紀錄一下

每個 Compiler 裡面的重要基礎設施之一就是 Control Flow Graph, 也就是 Basic Block 及其 Edge, 而這邊大概介紹一下 GCC 內部的資料結構及走訪方式, 主要參考來源是 GCC Source Code 及 GCC Internal.
GCC 的 Basic Block 在實作上要比較注意的一點是 GIMPLE 及 RTL 都會使用同樣得資料結構來表示, 但 RTL 的部份照慣例一樣是 expand 完後才會有, 而且同一個時間點只會是 GIMPLE RTL

詳細的程式碼則可到 gcc/basic-block.h 裡面撈

1. Basic Block

在整個 CFG 中有兩個特別的 Block 一個是 ENTRY_BLOCK, 一個是 EXIT_BLOCK 他們分別可由 ENTRY_BLOCK_PTR EXIT_BLOCK_PTR 取得

在 basic_block 這個 struct 中有 next_bb 及 prev_bb 兩個欄位, 但沒事請直接別碰這兩個傢伙, 要走訪所有的 Basic Block 或要找兒子/父親的話下面有一些範例程式碼

走訪所有 Basic Block

basic_block bb;

FOR_EACH_BB (bb) {
  /* do something */
}

要注意的一點是有個東西叫 FOR_ALL_BB, 差別在於他會走到 ENTRY_BLOCK 跟 EXIT_BLOCK 而 FOR_EACH_BB 不會
basic_block bb;

FOR_ALL_BB (bb) {
  /* do something */
}

找父親

basic_block bb;
edge e;
edge_iterator ei;
FOR_EACH_EDGE(e, ei, bb->preds){
  /*
   * get src by `e->src`
   * get dest by `e->dest`
   */
}


找兒子


basic_block bb;
edge e;
edge_iterator ei;
FOR_EACH_EDGE(e, ei, bb->succs){
  /*
   * get src by `e->src`
   * get dest by `e->dest`
   */
}

走訪該 BB 的所有 RTL INSN

rtx insn;
basic_block bb;
FOR_BB_INSNS(bb, insn) {
  /* do something with insn */
}

走訪該 BB 的所有 GIMPLE stmt 及 PHI node


basic_block bb;
gimple_stmt_iterator si;

for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) {
  gimple phi = gsi_stmt (si);
  print_gimple_stmt (dump_file, phi, 0, TDF_SLIM);
}
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) {
  gimple stmt = gsi_stmt (si);
  print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}


2. Edges

Edge flag 如下
以下是從 basic-block.h 那邊抽出來的, 跟 GCC Internal 描述的是不太一樣的東西
  1. EDGE_FALLTHRU : 這個 edge 不會產生額外的 jump, 代表 src/dest 之後一定會放在一起
  2. EDGE_ABNORMAL : Indirect jump 或著是 Exception Handling 的 Edge
  3. EDGE_ABNORMAL_CALL : 呼叫一個可能會發生 Exception 的函數或著是 No return 的函數(例如呼叫 exit 或 abort)
  4. EDGE_EH : 處理 Exception Handling 的 Edge
  5. EDGE_FAKE : 他是一個不是 edge 的 edge! 拿來給 profiling 時用的
  6. EDGE_DFS_BACK : 是一個 back edge
  7. EDGE_CAN_FALLTHRU : 可以弄成 EDGE_FALLTHRU
  8. EDGE_IRREDUCIBLE_LOOP : irreducible loop 裡面的 edge
  9. EDGE_SIBCALL : 摳到 sibcall
  10. EDGE_LOOP_EXIT : 迴圈的終點
  11. EDGE_TRUE_VALUE : 當 Conditional Branch 的 Condition 為 True 時的目的地
  12. EDGE_FALSE_VALUE : 當 Conditional Branch 的 Condition 為 False 時的目的地
  13. EDGE_EXECUTABLE : 作 SSA-CCP 最佳化的時候用的, 其他時候請不要亂用
  14. EDGE_CROSSING : Cold code 與 Hot code 交界點
  15. EDGE_PRESERVE : 這個 Edge 不准砍!!!
  16. EDGE_ALL_FLAGS : 代表所有 flag
  17. EDGE_COMPLEX : EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_EH | EDGE_PRESERVE, 很複雜就對了!

2012年6月28日 星期四

GCC RTL INSN 簡介

簡介


在 GCC Backend 中的 IR 主要使用 RTL , 當作一些 Target Dependency 的一些相關操作時常會需要直接的對 RTL 去做一些檢查或掃描, 而在這篇文章概略性的介紹了 RTL INSN 的種類及樣貌, 以供入門

RTL INSN



RTL INSN 所用的內部的結構為 Doubly-Linked Lists
由一個又一個的 INSN 所串列而成
其中 insn 分為下列幾種
  1. insn: 一般的指令, 非 branch/jump/call 的指令
  2. jump_insn: 跳躍指令, 會造成任何 control flow 轉移的指令類型, 包含 conditional/unconditional jump, 及 indirect jump.
  3. call_insn: 任何呼叫函數的指令
  4. debug_insn: 塞 debug info 的
  5. note: 就 note 阿
  6. barrier: 任何 control flow 都不應該穿越這傢伙, 隔絕各個 Basic Block 東東
  7. code_label: 在 c 裡面給 goto 用的 label 會變成這種東西
當然詳細的還是到 GCC Internal 會寫的比較清楚 http://gcc.gnu.org/onlinedocs/gccint/Insns.html
接著來看一下實際的 insn 長怎樣
(insn 21 20 22 6 
      (set (reg:SI 59 [ D.1238 ])
           (mem/c/i:SI (plus:SI (reg/f:SI 54 virtual-stack-vars)
                                (const_int -8 [0xfffffffffffffff8])) [0 sum+0 S4 A32]))
      /home/kito/sum.c:7 -1
      (nil))

大致上就是 Lisp-style 的樣子
在 gcc/rtl.def 檔案裡可以找到 insn 的定義 DEF_RTL_EXPR(INSN, "insn", "iuuBeiie", RTX_INSN)
由這串我們來解讀整個是在幹嘛的

攤開成一個一行來看, 並且參照上面定義 : "iuuBeiie", 一道 insn 總共由八個東西所組成
(insn 21
      20
      22
      6 
      (set (reg:SI 59 [ D.1238 ])
           (mem/c/i:SI (plus:SI (reg/f:SI 54 virtual-stack-vars)
                                (const_int -8 [0xfffffffffffffff8])) [0 sum+0 S4 A32]))
      /home/kito/sum.c:7
      -1
      (nil))


後面 # 後面為註解
(insn 21 # 1. i
      20 # 2. u
      22 # 3. u
      6  # 4. B
      (set (reg:SI 59 [ D.1238 ])
           (mem/c/i:SI (plus:SI (reg/f:SI 54 virtual-stack-vars)
                                (const_int -8 [0xfffffffffffffff8])) [0 sum+0 S4 A32])) # 5. e
      /home/kito/sum.c:7 # i
      -1 # 6. i
      (nil)) # 7. e
  1. i 代表該 INSN 的 uid , 也就是唯一的編號
  2. u 上一道 INSN 的 uid
  3. u 下一道 INSN 的 uid
  4. B Basic Block 的編號
  5. e 這道 INSN 的主要內容, 例如這上面那道指令所描述的是從記憶體讀取一個值到暫存器中
  6. i 此 INSN 相對應回原始碼的位置
  7. i 放 RTL pattern Name
  8. e 塞 REG_NOTES 資訊, 主要是暫存器相關資訊如 live or kill 等等

相關存取函數可以從 rtl.h 中找到
/* ACCESS MACROS for particular fields of insns.  */

/* Holds a unique number for each insn.
   These are not necessarily sequentially increasing.  */
#define INSN_UID(INSN)  XINT (INSN, 0)

/* Chain insns together in sequence.  */
#define PREV_INSN(INSN) XEXP (INSN, 1)
#define NEXT_INSN(INSN) XEXP (INSN, 2)

#define BLOCK_FOR_INSN(INSN) XBBDEF (INSN, 3)

/* The body of an insn.  */
#define PATTERN(INSN)   XEXP (INSN, 4)

#define INSN_LOCATOR(INSN) XINT (INSN, 5)

相關判斷函數也可以從 rtl.h 中找到
/* Predicate yielding nonzero iff X is a label insn.  */
#define LABEL_P(X) (GET_CODE (X) == CODE_LABEL)

/* Predicate yielding nonzero iff X is a jump insn.  */
#define JUMP_P(X) (GET_CODE (X) == JUMP_INSN)

/* Predicate yielding nonzero iff X is a call insn.  */
#define CALL_P(X) (GET_CODE (X) == CALL_INSN)

/* Predicate yielding nonzero iff X is an insn that cannot jump.  */
#define NONJUMP_INSN_P(X) (GET_CODE (X) == INSN)

/* Predicate yielding nonzero iff X is a debug note/insn.  */
#define DEBUG_INSN_P(X) (GET_CODE (X) == DEBUG_INSN)

/* Predicate yielding nonzero iff X is an insn that is not a debug insn.  */
#define NONDEBUG_INSN_P(X) (INSN_P (X) && !DEBUG_INSN_P (X))

/* Nonzero if DEBUG_INSN_P may possibly hold.  */
#define MAY_HAVE_DEBUG_INSNS MAY_HAVE_DEBUG_STMTS

/* Predicate yielding nonzero iff X is a real insn.  */
#define INSN_P(X) \
  (NONJUMP_INSN_P (X) || DEBUG_INSN_P (X) || JUMP_P (X) || CALL_P (X))

/* Predicate yielding nonzero iff X is a note insn.  */
#define NOTE_P(X) (GET_CODE (X) == NOTE)

/* Predicate yielding nonzero iff X is a barrier insn.  */
#define BARRIER_P(X) (GET_CODE (X) == BARRIER)
其中要注意的部份是 INSN_P 所濾出來的不是 INSN 而是 INSN, DEBUG_INSN, JUMP_INSN 及 CALL_INSN
要單純的 INSN 要用 NONJUMP_INSN_P 來判斷


小結


對於 GCC Backend 的 Porting 來說 RTL 相關部份是相當重要的基礎知識之一,
如果真的不幸的踏入 GCC 屠龍之道則相關文件一定要先熟讀幾次
以及 rtl.h 也一定要仔細的看一遍, 避免發生悲劇...

2012年6月3日 星期日

GCC : Predicates vs Constraints


順手先把一些東西紀錄一下

在 GCC 的 md 中, 寫那堆 define_insn, define_expand, ...等的 pattern 中

很重要的一環是要寫好 match_operand 中的 Predicates 跟 Constraints

例如以下拿 arm 的 movsi 的 pattern 來說

(define_insn "*arm_movsi_insn"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rk,r,r,r,rk,m")
       (match_operand:SI 1 "general_operand"      "rk, I,K,j,mi,rk"))]
  "TARGET_ARM && ! TARGET_IWMMXT
   && !(TARGET_HARD_FLOAT && TARGET_VFP)
   && (   register_operand (operands[0], SImode)
       || register_operand (operands[1], SImode))"
  "@
   mov%?\\t%0, %1
   mov%?\\t%0, %1
   mvn%?\\t%0, #%B1
   movw%?\\t%0, %1
   ldr%?\\t%0, %1"
  [(set_attr "type" "*,*,*,*,load1")
   (set_attr "insn" "mov,mov,mvn,mov,*")
   (set_attr "predicable" "yes")
   (set_attr "pool_range" "*,*,*,*,4096")
   (set_attr "neg_pool_range" "*,*,*,*,4084")]
)

其中 nonimmediate_operand 及 general_operand 是 Predicates 的部份

Constraints 則是後面那串 "=rk,r,r,r,rk,m" 及 "rk, I,K,j,mi,rk"

而這兩者之間的差別在於一個指定大類別, 一個小類別

可以想像成 Predicates 是大篩子, Constraints 是小篩子

因此在 Insn Pattern Match 時, GCC 會先透過大篩子

來分類每個 RTL Insn 會適用於哪個 Insn Pattern

然後再想辦法透過任何可能的方法讓他符合任何一個 Constraints

(其中方法包含 split, expand 或著是神奇的 reload

所以若要將 mov pattern 拆解成 mov, store, load 三個 pattern 的話

就必須將 Predicates 的部份作適度的調整,

則可避免  Insn Pattern Match 時統統掉入 mov pattern

值得注意的是, GCC 內部是會由上到下掃描各式 pattern,

所以若大篩子不夠嚴謹造成某個 RTL Insn 可 match 多個 pattern 時,

則會採用第一個 match 到的 pattern,

相對於 md 來說就是比較前面定義的 pattern 會先被 match 到

而以上部份就是大致 gcc 在 md 中的 Predicates 與 Constraints 的大略運作方式.

2012年4月17日 星期二

Android C Library: Bionic 成長計畫

今年 OSDC 2012 所分享的 take, 內容主要包含對於 Bionic C Library 的一些改進, 一般而言, 看到 C Library 人們直覺可能會想到 printf 等等標準函數庫所提供的函式,但事實上除此之外 C Library 也通常會附贈許多額外功能, 例如 Dynamic Linker 以及一些 Profiling 的支援, 以一般 Linux 常見的 Glibc 來說 其中就包含了 Dynamic Linker, gprof 及 sprof 等額外功能 而在 Bionic 中在這部份而言相對貧乏, 雖然有包含 Dynamic Linker 但其實作相對 Glibc 少上許多功能, 因此我們所改進的方向是往補強功能的部份發展, 而在主題當中主要包含下列幾個部份
  1. GNU-Style Hash 支援
  2. Prelink 的改善(更多細節可參照Android Prelinker : Apriori)
  3. Aprof(更多細節可參照)
  4. Checkpoint 簡介
其中 Checkpoint 的部份與 C Library 較無直接關聯, 但事實上 Checkpoint 過程中會與 C Library 及 Dynamic Linker 實作有相當大的依存性, 因此我們的長期目標是將 Checkpoint 功能整合進 Bionic 裡面, 以期更簡潔快速的 Checkpoint 功能實作


2012年2月1日 星期三

Aprof : Android Profiler - A profiling tool for android native code


目前 Android 在 profiling tool 這邊大致上有 traceview 及 oprofile 可用, 但 traceview 是針對於一般的 DVM 程式, 而 oprofile 又相對的較複雜, 且缺乏類似 gprof 的簡易工具來觀測效能

而事實上已經有人開始注意到此事並提出方法, 最直接的方式是實作 mcount 函數並透過 gcc -pg 選項來使用, 如此一來變不須更動太多部份,例如 android-ndk-profiler 便是採用此方式, Aprof 也將尋此方式來進行擴充, 但我們所採用的方式並非單純將 gprof 功能實作, 而是進一步擴充並且為 Android 系統量身打造.

Aprof 簡介


有鑑於目前 android 平台上對於 native code 的 profiler 較於缺乏, 因此我們提出實作一個為 Android 量身訂作的 Profiler, 取名為 aprof, 主要功能與 gprof 相仿, 提供 Call Graph 與 Time Sample Info.

在 aprof 設計時的幾個主要目標:

  1. 採用舊有 gprof 的 mcount 界面, 避免修改 toolchain
  2. 能夠支援 Shared Library
  3. 可以對 JNI 的 Shared Library 進行 Profiling

在 aprof 中主要也是透過 mcount 函數及 gcc -pg 選項來使用, 但在這邊我們所採取的方式是與 gprof 輸出格式不相容, 主要原因在於 gprof 輸出格式無法支援 shared library 的 profiling, 若沿用原本格式將造成功能上的限制.

Aprof 使用方法


若要使用 Aprof 來 profile 執行檔的話只要在 Android.mk 加入以下的選項開啟, 對於一般的 static library 也是採用相同方式即可

LOCAL_ENABLE_APROF := true


但要注意的是若是要使用 Aprof 來 profile Shared Library 的話主要分為兩種情況使用

針對一般 Non-JNI 使用的 Shared Library 採用與一般執行檔的開啟方式即可, 但要執行檔也開啟 Aprof 時才會啟動 profiling

若是對於 JNI 使用的 Shared Library 則在 Android.mk 加入以下的選項, 其 profiling 資訊將會於 Activity 於 Life Cycle 中的 stop 時寫入檔案, 值得注意的是若程式已經開啟 Aprof 則會以 Non-JNI aprof mode 來進行 profiling

LOCAL_ENABLE_APROF_JNI := true


在開啟 Aprof 並且重新建置程式後, 便可開始準備 profiling, 開始的方式則是執行一次該程式, 在開啟 profiling 的情況下會比未開啟的情況下慢上許多, 在執行完成程式後變可以到 /sdcard/ 裡面檢查 profiling 的輸出, 預設檔名是 $(progname).aprof

接著 profiling file 撈回 Host 端, 也就是使用 adb 去下載, 方式可參考以下指令:

adb pull /sdcard/$(progname).aprof
 
# 例如進行 Profiling 的程式是 foo
adb pull /sdcard/foo.aprof

然後使用 aprof 來讀取 profiling file 的資訊, 使用方式如下:


# aprof $(prog_file) $(prof_file)
aprof foo foo.aprof

下面則是範例輸出:

  %      cumulative     self                 self       total
 time     time          time      calls    ms/call    ms/call   name
 99.52       2170       2140    2178309          0          0   fib
  0.00       2170          0          1          0        217   main
  0.48          0         30          0          0          0   <libc.so>
 
Call graph (explanation follows)
 
-------------------------------------------------------------
Image           : foo
Cumulative time : 2170 ms
Self time       : 2140 ms
  Function  % time  cumulative        self       Count  Call by
 fib                      2170        2140
            100.00        2170           0           1  main
            100.00        2170        2140     2178308  fib
 main                     2170           0
            100.00        2170           0           1  <libc.so>


上半部份主要是顯示各個函數所佔用的時間與百分比, cumulative time 表示包含子函數的時間, 而 self time 則表示該函數所有消耗時間, 例如 a() call b(), a() 花了 1 sec, b() 花了 2 sec, 則 a() 的 cumulative time 為 3 sec, self time 為 2 sec, calls 代表該函數總共被呼叫的次數, self ms/call 及 total ms/call 分別代表每次呼叫約花費多少時間.

但在上面的例子中可以發現有 的這個符號, 主要原因是 aprof 沒有載入該 shared library 的符號資訊, 我們可以透過 -L 來指定 Shared Library 的搜索路徑, 一般搜索路徑設置在 Android 的目錄下的 out/target/product/panda/symbols/system/lib (以 panda board 為例)即可.

# aprof $(prog_file) $(prof_file) -L$(lib_path)
aprof foo foo.aprof -L out/target/product/panda/symbols/system/lib

一般而言在載入了符號後資訊會較為詳細, 但仍有些不足的地方, 例如在 calls 的部份若該 Shared Library 建置時並無開啟 aprof 則無法得到此部份的資訊, 也因此 cumulative time 部份會無法計算, 不過 Self time 的部份在有了 symbol 資訊後則可正確定位.

  %      cumulative     self                 self       total
 time     time          time      calls    ms/call    ms/call   name
 99.52       2170       2140    2178309          0          0   fib
  0.00       2170          0          1          0        217   main
  0.32          0         20          0          0          0   write
  0.16          0         10          0          0          0   memcpy
 
 
Call graph (explanation follows)
 
-------------------------------------------------------------
Image           : foo
Cumulative time : 2170 ms
Self time       : 2140 ms
  Function  % time  cumulative        self       Count  Call by
 fib                      2170        2140
            100.00        2170           0           1  main
            100.00        2170        2140     2178308  fib
 main                     2170           0
            100.00        2170           0           1  __libc_init
-------------------------------------------------------------
Image           : foo
Cumulative time : 2170 ms
Self time       : 30 ms
 write                       0          20
 memcpy                      0          10

接下來的部份則是介紹對於將結果視覺化呈現的方式, aprof 支援 dot 格式的輸出, 可透過 -d 來輸出 dot 格式, 可透過 pipeline 到 dot (通常包含於 graphviz 套件)直接將其結果輸出成圖檔, 使用方式參考下列指令

# aprof $(prog_file) $(prof_file) -L$(lib_path)
aprof foo foo.aprof -L out/target/product/panda/symbols/system/lib -d | dot -Tpng -o foo.aprof.png

在這邊附上一個 busybox 的 md5sum 的 aprof 輸出圖檔供參考:

圖 1. md5sum 的 Call Graph 輸出


Aprof 的使用方式及輸出資訊大致介紹到這個部份.

Aprof 實作


Aprof 為了能夠支援較完整的 Shared Library Profiling 機制, 所以有一大部分是實作在 linker 中, 這樣的作法主要是參考自 sprof 的實作方式, 而在這邊為何要重新打造一個 Profiler 主要有兩個原因:

  1. sprof 實作於 glibc 的 dynamic linker, 其授權是採用 GPL, 因此無法直接採用
  2. 整合 gprof 與 sprof, 並為 Android 系統的需求設計

為何分 JNI 及 Non-JNI mode ?

對於 Shared Library , aprof 提供兩種模式可進行 Profiling, 兩者之間的行為差別在於 JNI mode 下會各個 Shared Library 會分別 Profiling , 分別輸出檔案, 並且最重要的是執行檔不需要開啟 Aprof 也可進行 Profiling.

non-JNI mode 則是必須執行檔開啟 Aprof 時才會進行 Profiling, 這樣的設計主要是因為避免其他 Link 到該 Shared Library 的執行檔皆受到無差別的效能攻擊.

JNI mode 主要存在的意義就在於如其命名一樣, 針對 JNI 的 Shared Library 來使用, 在作為 JNI 的 Shared Library 通常是被 DVM (app_process / zygote)所開啟, 而要等到其執行結束則會有點困難, 故在這邊的設計是採用於 Activity 於 Life Cycle 中的 stop 時輸出 Profiling File.

另外需要注意的是若用在 JNI 上的話, 必須確保該應用程式有寫入的權限, 也就是記得在 AndroidManifest.xml 中加入 android.permission.WRITE_EXTERNAL_STORAG


AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.android.simplejni">
    <application android:label="Simple JNI">
        <activity android:name="SimpleJNI">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

目前狀態

若使用 AOSP JB 或 ICS 的話 Aprof 僅需要再使用以下 patch 即可
Aprof 也支援及 Gingerbread, 但如果你使用的是 Linaro, CyanogenMod (包含其延伸) 或 Gingerbread, 則需要額外多加入這幾個 patch 才可正常運作: