2015年8月5日 星期三

[OpenCV] How to install OpenCV in Mac OS

安裝過程摘要

1. download latest opencv package from  http://opencv.org/
$ wget https://github.com/Itseez/opencv/archive/3.0.0.zip

2015年6月1日 星期一

[Embedded] 利用 gdb script 實作 strcmp 函數

要在 gdb 內直接使用條件式中斷(Conditional breakpoint),例如:當某個字串出現時程式才進入中斷點,應該怎麼做呢?

1. 可以使用 commands 命令來設定條件式中斷
break foo
commands
if x>0
continue
else
printf "x is %d\n",x
end
end

2. 問題在於如何比較字串,以下整理網路上找到的幾種作法。若gdb版本較舊,則可能需要找尋其他方法。

方法一:使用 strcmp
(gdb) set $x = malloc(strlen("foobar") + 1)(gdb) call strcpy($x, "foobar")(gdb) break a_leg if strcmp(foo, $x) == 0

方法二:透過 python
可以直接使用下列函數 (Convenient functions) 
$_memeq(buf1buf2length)Returns one if the length bytes at the addresses given by buf1 and buf2 are equal. Otherwise it returns zero.  
$_regex(strregex)Returns one if the string str matches the regular expression regex. Otherwise it returns zero. The syntax of the regular expression is that specified by Python's regular expression support.  
$_streq(str1str2)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")

方法三:使用 gdb script 實作 strcmp
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:

  1. https://sourceware.org/gdb/onlinedocs/gdb/Break-Commands.html
  2. http://stackoverflow.com/questions/13961368/conditional-breakpoint-using-strcmp-in-gdb-on-mac-os-x-conflicts-with-objectiv
  3. https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Funs.html
  4. http://stackoverflow.com/questions/7423577/how-to-compare-a-stored-string-variable-in-gdb
  5. http://www.opensourceforu.com/2011/09/modify-function-return-value-hack-part-2/

2015年4月27日 星期一

[Embedded] 如何替換系統內預設的 memcpy 函數

最近使用 perf 替手邊的嵌入式系統進行效能分析,發現系統內最常被用到的函數是 memcpy(),若能夠優化此函數,應該可以改善系統效能吧。以下記錄整個實驗過程。

1. 系統效能分析
編譯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 函數是整個系統最耗時的部分,因此我將針對此函數進行分析,評估是否有優化的空間。

2. 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

3. 如何替換 libc 內的 memcpy
為了簡單,我並沒有使用 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=
$ ./perf bench mem all 
4. 針對整個系統,更換 user space 所使用的 memcpy
若確定修改過後的 memcpy library 效能的確較佳,則可以修改 /etc/ld.so.preload,讓系統每次在作 dynamic link 時,總是先尋找自行撰寫的 memcpy library. 
ld.so.preload 的內容舉例如下:
/lib/libmymemcpy.so

參考資料
  1. http://stackoverflow.com/questions/27171485/various-glibc-and-linux-kernel-versions-compatibility
  2. http://stackoverflow.com/questions/9107259/how-to-replace-c-standard-library-functioin
  3. http://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick
  4. http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
  5. https://perf.wiki.kernel.org/index.php/Tutorial

2015年3月28日 星期六

[Embedded] 使用 gdbserver 時,出現 armv5te 警告訊息

問題描述:使用 gdbserver 追蹤程式時,target 為 armv6 架構,但卻出現 armv5te 的警告訊息。

問題原因:我所使用的 tool chain arm-2009q3,預設會假設 target 為 armv5te架構

解決方法:

1. 先登入 Target 端 console,確認 cpu info 

1 # cat /proc/cpuinfo
2 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 : Coconut
12 Revision : 13ec3011
13 Serial : 0000000000000000

2. 確認目前 arm-none-linux-gnueabi-gcc 的預設架構

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

4 因此在重新編譯 gdbserver 時應該要指定 armv6,舉例如下:
~/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

5. 那麼 arm 架構不符會發生什麼問題呢?
主要是編譯成機器語言時,可能會使用不同的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

7. 若不使用 gdbserver,要直接編譯一個在 target system 執行的 gdb,需要先編譯 termcap,可參考下列設定

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'

Reference:
http://ftp.gnu.org/gnu/gdb/ 
https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
https://lists.debian.org/debian-arm/2011/11/msg00043.html
http://blog.csdn.net/a_ran/article/details/38404483
http://www.360doc.com/content/12/0312/23/532901_193884753.shtml
http://www.360doc.com/content/12/0312/23/532901_193884753.shtml
http://stackoverflow.com/questions/4381102/differences-between-arm-architectures-from-a-c-programmers-perspective
http://www.it.uom.gr/teaching/gcc_manuals/onlinedocs/gdb_35.html
http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-11/msg00579.html




2015年3月6日 星期五

[Embedded] 使用 gdb 時,程式因為收到 SIGTRAP 而結束的解決方法

問題描述:使用 gdb 追蹤程式時,程式因為收到 SIGTRAP 而結束。

問題現象:執行程式時,gdb 與 gdbserver分別出現下列 warning,接著當程式執行到 breakpoint 時,gdb便自行結束。

2015年3月2日 星期一

[Embedded] 在 ARM in Linux 編譯 perf

要改善系統效能,首先必須有個量測系統效能的工具,本篇紀錄在 ARM in Linux 如何手動編譯 perf 。

摘錄網路上對 perf 的說明如下:

perf (sometimes called "Perf Events"or perf tools, originally "Performance Counters for Linux", PCL) is a performance analyzing tool in Linux, available from Linux kernel version 2.6.31.

it can instrument CPU performance counters, tracepoints, kprobes, and uprobes (dynamic tracing). It is capable of lightweight profiling. It is also included in the Linux kernel, under tools/perf, and is frequently updated and enhanced.

perf 的使用範例可以參考 IBM提供的文章