1. download latest opencv package from http://opencv.org/
$ wget https://github.com/Itseez/opencv/archive/3.0.0.zip
$ wget https://github.com/Itseez/opencv/archive/3.0.0.zip
break foo
commands
if x>0
continue
else
printf "x is %d\n",x
end
end
(gdb) set $x = malloc(strlen("foobar") + 1)(gdb) call strcpy($x, "foobar")(gdb) break a_leg if strcmp(foo, $x) == 0
可以直接使用下列函數 (Convenient functions)
$_memeq(buf1,buf2,length)Returns one if the length bytes at the addresses given by buf1 and buf2 are equal. Otherwise it returns zero.
$_regex(str,regex)Returns one if the string str matches the regular expression regex. Otherwise it returns zero. The syntax of the regular expression is that specified byPython's regular expression support.
$_streq(str1,str2)Returns one if the strings str1 and str2 are equal. Otherwise it returns zero.
也可以自行實作 strcmp
(gdb) define strcmp>py print cmp(gdb.execute("output $arg0", to_string=True).strip('"'), $arg1)>end(gdb) strcmp $x "hello"0
針對舊版本的 gdb,例如我所使用的 gdb 7.1,並不支援上述函數,因此需要自行實作一個 conevenient function,範例如下:
py
class MyStrcmp (gdb.Function):
"""My Own Strcmp"""
def __init__ (self):
super (MyStrcmp, self).__init__ ("mystrcmp")
def invoke (self, arg0, arg1):
print "input '" + arg0.string() + "' and '" + arg1.string() + "'"
if arg0.string() == arg1.string() :
print "equal"
return 0
else:
print "not equal"
return 1
MyStrcmp()
end
用法:
print $mystrcmp("hello", "hello")
set var $_isEq=0
# Yes! GDB_STRCMP, below, is a gdb function.
# Function that provides strcmp-like functionality for gdb script;
# this function will be used to match the password string provided in command line argument
# with the string argument of strcmp in program
define GDB_STRCMP
set var $_i=0
set var $_c1= *(unsigned char *) ($arg0 + $_i)
set var $_c2= *(unsigned char *) ($arg1 + $_i)
while ( ($_c1 != 0x0) && ($_c2 != 0x0) && ($_c1 == $_c2) )
#printf "\n i=%d, addr1=%x(%d,%c), addr2=%x(%d,%c)", $_i, ($arg0 + $_i),$_c1, $_c1, ($arg1 + $_i), $_c2,$_c2
set $_i++
set $_c1= *(unsigned char *) ($arg0 + $_i)
set $_c2= *(unsigned char *) ($arg1 + $_i)
#while end
end
if( $_c1 == $_c2)
set $_isEq=1
else
set $_isEq=0
end
#GDB_STRCMP end
end
Reference:
編譯perf的方法可參考此篇文章。使用 "perf top" 可以得知觀察系統當前的狀態,找到最耗時的函數。
"perf top"原文說明如下:
The default sampling event is cycles and default order is descending number of samples per symbol, thus perf top shows the functions where most of the time is spent. By default, perf top operates in processor-wide mode, monitoring all online CPUs at both user and kernel levels. It is possible to monitor only a subset of the CPUS using the -C option.
下圖為"perf top"的執行結果,由圖中可得知 libc-2.10.1.so 內的 memcpy 函數是整個系統最耗時的部分,因此我將針對此函數進行分析,評估是否有優化的空間。
首先需要先檢視 libc-2.10.1對memcpy的實作方式,glibc 原始碼可至 http://ftp.gnu.org/gnu/glibc/ 下載。我分別下載了 libc-2.10.1 和 libc-2.21 的程式碼並進行比較。其主要差別在於libc-2.10.1使用C語言撰寫,而 libc-2.21將memcpy則會使用ARM的組合語言。
- 比較 glibc-2.10.1\string\memcpy.c 與 glibc-2.21\string\memcpy.c,其內容幾乎一樣。
- 但 glibc-2.21 針對 memcpy 新增了 ARM 的組語版本 (\glibc-2.21\sysdeps\arm\memcpy.S),當編譯ARM版本的 libc 時,便會選擇編譯組語版本的 memcpy。
合理的推論,在 ARM 架構下, \arm\memcpy.S 的運作效能應該優於 \string\memcpy.c
為了簡單,我並沒有使用 glibc-2.21 的 memcpy,我直接從 android source code中取出對應的 memcpy.S,用來練習置換 libc memcpy,原始程式可至 github 取得。
方法一:直接 link 自行改寫的 memcpy library,用法如下:
$(CC) memcpy_test.o memcpy.o -o memcpy_test
$ arm-none-linux-gnueabi-readelf -s ./memcpy_test
110: 00008520 0 FUNC GLOBAL DEFAULT 12 memcpy
125: 000083f0 0 FUNC GLOBAL DEFAULT UND malloc@@GLIBC_2.4
方法二:透過設定 LD_PRELOAD 可切換動態連結時所尋找的 library 順序
$(CC) memcpy_test.o -o memcpy_test
$ arm-none-linux-gnueabi-readelf -s ./memcpy_test
104: 000083f8 0 FUNC GLOBAL DEFAULT UND memcpy@@GLIBC_2.4
110: 00008404 0 FUNC GLOBAL DEFAULT UND malloc@@GLIBC_2.4
$ export LD_PRELOAD=/tmp/test/mem_practice/libmymemcpy.so
$ ./memcpy_test (使用自行編譯的 memcpy)
$ export LD_PRELOAD=
$ ./memcpy_test (使用 libc 的 memcpy)
可透過下列命令切換不同的 memcpy 並測試效能
$ export LD_PRELOAD=/tmp/test/mem_practice/libmymemcpy.so
$ ./perf bench mem all
$ export LD_PRELOAD=4. 針對整個系統,更換 user space 所使用的 memcpy
$ ./perf bench mem all
若確定修改過後的 memcpy library 效能的確較佳,則可以修改 /etc/ld.so.preload,讓系統每次在作 dynamic link 時,總是先尋找自行撰寫的 memcpy library.
ld.so.preload 的內容舉例如下:
/lib/libmymemcpy.so
1 # cat /proc/cpuinfo2 Processor : ARMv6-compatible processor rev 5 (v6l)
3 BogoMIPS : 526.25
4 Features : swp half fastmult edsp java
5 CPU implementer : 0x41
6 CPU architecture: 6TEJ
7 CPU variant : 0x1
8 CPU part : 0xb36
9 CPU revision : 5
10
11 Hardware : Coconut12 Revision : 13ec301113 Serial : 0000000000000000
1 $ arm-none-linux-gnueabi-gcc -Q --help=target
2 The following options are target specific:
3 -falign-arrays [disabled]
4 -mabi=
5 -mabort-on-noreturn [disabled]
6 -mapcs [disabled]
7 -mapcs-float [disabled]
8 -mapcs-frame [disabled]
9 -mapcs-reentrant [disabled]
10 -mapcs-stack-check [disabled]
11 -march= armv5te
12 -marm [enabled]
13 -mbig-endian [disabled]
14 -mcallee-super-interworking [disabled]
15 -mcaller-super-interworking [disabled]
16 -mcirrus-fix-invalid-insns [disabled]
17 -mcpu=
18 -mfix-cortex-m3-ldrd [enabled]
19 -mfix-janus-2cc [disabled]
20 -mfloat-abi=
21 -mfp16-format=
22 -mfp=
23 -mfpe [disabled]
24 -mfpe=
25 -mfpu=
26 -mglibc [enabled]
27 -mhard-float [disabled]
28 -mlittle-endian [enabled]
29 -mlong-calls [disabled]
30 -mlow-irq-latency [disabled]
31 -mmarvell-div [disabled]
32 -mpic-register=
33 -mpoke-function-name [disabled]
34 -msched-prolog [enabled]
35 -msingle-pic-base [disabled]
36 -msoft-float [disabled]
37 -mstructure-size-boundary=
38 -mthumb [disabled]
39 -mthumb-interwork [enabled]
40 -mtp=
41 -mtpcs-frame [disabled]
42 -mtpcs-leaf-frame [disabled]
43 -mtune=
44 -muclibc [disabled]
45 -mvectorize-with-neon-quad [disabled]
46 -mword-relocations [disabled]
47 -mwords-little-endian [disabled]
很明顯,若使用預設值 armv5te進行編譯,則 arm 架構不符。3. 如何確認現在正在使用的 gdbserver 其適用的arm架構呢?
$arm-none-linux-gnueabi-readelf -A ./gdbserver
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "5TE"
Tag_CPU_arch: v5TE
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-1
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align8_needed: Yes
Tag_ABI_align8_preserved: Yes, except leaf SP
Tag_ABI_enum_size: int
~/gdb/gdb-7.9/gdb/gdbserver$./configure --build=i686-pc-linux-gnu --host=arm-none-linux-gnueabi --target=arm-none-linux-gnueabi CROSS_COMPILE=arm-none-linux-gnueabi- CFLAGS='-g -O2 -march=armv6 -mtune=arm1136j-s'
編譯方式可參考此篇 。是否正確編譯成功,可透過 readelf 來檢視,如下:
&arm-none-linux-gnueabi-readelf -A ./gdbserver
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "6"
Tag_CPU_arch: v6
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-1
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align8_needed: Yes
Tag_ABI_align8_preserved: Yes, except leaf SP
Tag_ABI_enum_size: int
主要是編譯成機器語言時,可能會使用不同的instruction set或call convention,可能造成執行時的錯誤。其中差異可以參考此篇討論。6. 編譯 gdb
編譯方式如下
$./configure --target=arm-none-linux-gnueabi
$ make
不過這樣編譯出來的 gdb,在進行遠端除錯時,會出現以下錯誤訊息
"warning: Can not parse XML target description; XML support was disabled at compile time"
手動安裝 expat,重新編譯gdb便可解決,步驟如下:
可從此處下載原始碼 http://sourceforge.net/projects/expat/files/latest/download
$ tar zxvf expat-2.1.0.tar.gz
$ cd expat-2.1.0
$ ./configure
$ make;make install
另外支援 python script 會方便許多,因此我個人偏好使用下列這個編譯方式
$ sudo apt-get install python2.7-dev
$./configure --target=arm-none-linux-gnueabi --with-expat --with-python
$ make
註:若編譯的是 gdb-7.1,其 python 支援的功能是有限制的
http://stackoverflow.com/questions/8986589/how-to-get-output-from-gdb-execute-in-pythongdb-gdb-7-1
Build termcap
./configure --build=i686-pc-linux-gnu --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf CROSS_COMPILE=arm-linux-gnueabihf- --prefix=/home/albert/tools/termcap
Build gdb
./configure --build=i686-pc-linux-gnu --host=arm-none-linux-gnueabi --target=arm-none-linux-gnueabi CROSS_COMPILE=arm-none-linux-gnueabi- CFLAGS='-g -O2 -I/home/albert/tools/termcap/include' LDFLAGS='-static -L/home/albert/tools/termcap/lib' CPPFLAGS='-I/home/albert/tools/termcap/include'