++++ 伺服器列表|
- itanium.iis.sinica.edu.tw
- sparc.iis.sinica.edu.tw
- tesla.iis.sinica.edu.tw
- opteron.iis.sinica.edu.tw
- xeon.iis.sinica.edu.tw
- i7.iis.sinica.edu.tw
- i722.iis.sinica.edu.tw
- i71.iis.sinica.edu.tw
- ps3.iis.sinica.edu.tw
- opteron48.iis.sinica.edu.tw
++++
- Dynamically Translating x86 to LLVM using QEMU
- xeon, tesla - Intel(R) Xeon(R) CPU E5410 - Core microarchitecture
http://en.wikipedia.org/wiki/Xeon#Quad-Core_and_Multi-Core_Xeon
- i71 - Intel(R) Xeon(R) CPU X5550 - Nehalem microarchitecture
http://en.wikipedia.org/wiki/Xeon#Nehalem-based_Xeon
- i7, i722 - Intel(R) Core(TM) i7 CPU 975 - Nehalem microarchitecture
http://en.wikipedia.org/wiki/List_of_Intel_Core_i7_microprocessors#.22Bloomfield.22_.2845_nm.29
Quick Start
Build QEMU
請見 QEMU。
Build LLVM
SPEC
閱讀 SPEC CPU2006 Documentation、SPEC CPU2006 Config Files 和 The 'runspec' Command。
–nobuild
可以避免重新編譯。
- 在 /data/Benchmarks/SPEC/CPU2006_v1.1_x86/config 底下生成一個設定檔。
$ cp Example-linux64-amd64-gcc43.cfg chenwj-i71-example.cfg
- 新增
qemu = /path/to/qemu-i38 curdir = /tmp/chenwj/spec # 測量結果放置處 output_root = ${curdir}/spec_results # summit 可以加上額外的指令及參數,預設為 $command submit = $qemu $command
- 建立軟連結,方便修改設定檔
$ cd /tmp/chenwj/spec $ ln -s /data/Benchmarks/SPEC/CPU2006_v1.1_x86/config/chenwj-i71-example.cfg . $ cd /data/Benchmarks/SPEC/CPU2006_v1.1_x86 # 這樣才抓得到 runspec $ . shrc $ cd /tmp/chenwj/spec $ runspec --config=chenwj-i71-example.cfg --iterations=1 --size=test mcf
- 中止測量Ctrl+Z
$ pstree -p | grep runspec -A2 -B2 $ killall
若要同時執行多個 benchmark,請手動並將其至於背景執行。
Binary Translator
LnQ
請見 LnQ。
建置 LLVM
$ svn co svn+ssh://user@itanium.iis.sinica.edu.tw/data/svn/llvm-qemu/llvm/src/llvm $ mkdir build; cd build $ ../llvm/configure --prefix=$INSTALL --enable-optimized --enable-assertions=no $ make install
建置 LLVM-GCC
$ wget http://llvm.org/releases/2.6/llvm-gcc-4.2-2.6-x86_64-linux.tar.gz $ tar xvf llvm-gcc-4.2-2.6-x86_64-linux.tar.gz
建置 LnQ
- 設定 PATH
$ export PATH=$PATH:/path/to/llvm:/path/to/llvm-gcc/
- 下載 LnQ
# data/svn/llvm-qemu/qemu/src/branches/lnq-llvm-2.8/ $ svn co svn+ssh://user@itanium.iis.sinica.edu.tw/data/svn/llvm-qemu/qemu/src/branches/lnq $ mkdir build; cd build # --extra-cflags='-DLNQ_EDGE_PROFILE' 開啟 edge profile,此資訊可用來建立 CFG # qemu-i386 -d edge_count -logfile filename $ ../lnq/configure --prefix=/path/to/install --target-list=i386-linux-user --enable-llvm-translator $ make install
- 設定 LLVM IR 函式庫的路徑
$ cp i386-linux-user/lnq-ir-lib.bc /path/to/lnq-ir-lib.bc $ export LNQ_IR_LIB=/path/to/lnq-ir-lib.bc
- qemu/src/trunk/target-i386/llvm-translator.h qemu/src/trunk/target-i386/llvm-translator.cpp
- lnq-ir-lib.bc 為 LLVM bitcode,可用 llvm-dis 讀取內容
開發手冊
QEMU Hybrid
請見 Hybrid QEMU。
- 簽出 Hybrid QEMU。
# file:///data/svn/llvm-qemu/hybrid-qemu/branches/ $ svn co svn+ssh://user@itanium.iis.sinica.edu.tw/data/svn/llvm-qemu/hybrid-qemu/branches/user-mode $ svn co svn+ssh://user@itanium.iis.sinica.edu.tw/data/svn/llvm-qemu/hybrid-qemu/branches/system-mode
- 建置 LLVM。
$ wget http://llvm.org/releases/2.8/llvm-2.8.tgz; tar vxf llvm-2.8.tgz $ cd llvm-2.8; patch -p1 < ../system-mode/patch/llvm-2.8.patch; cd .. $ mkdir build; cd build $ ../llvm-2.8/configure --prefix=${INSTALL} --enable-optimized $ make install
- 建置 LLVM-GCC。
$ wget http://llvm.org/releases/2.8/llvm-gcc4.2-2.8-x86_64-linux.tar.bz2 $ tar xvf llvm-gcc4.2-2.8-x86_64-linux.tar.bz2
- 設定 PATH。
$ export PATH=$PATH:/path/to/llvm:/path/to/llvm-gcc
- 建置 QEMU-Hybrid。
$ cd build $ ../system-mode/configure --prefix=$INSTALL --enable-debug --enable-llvm=hybrids --target-list=i386-linux-user $ make install
Annotaion Framwork & Binary Optimizer
建置系統
建置 Annotaion Framwork & Binary Optimizer & LnQ-DBO
$ svn co svn+ssh://user@itanium.iis.sinica.edu.tw/data/svn/chenwj/chenwj-top/trunk chenwj-top $ cd chenwj-top; $ . ./bootstrap.sh all
手動建置 lnq-dbo。
$ svn co file:///data/svn/llvm-qemu/qemu/src/branches/lnq-dbo $ mkdir build; cd build $ ../lnq-dbo/configure --prefix=$INSTALL --target-list=i386-linux-user --enable-llvm-translator --enable-dbo $ make install
開發手冊
修改 QEMU
補丁
Introduction: Using diff and patch (tutorial)
$ diff -ruN qemu-0.11.0/linux-user/syscall.c qemu-dbo/linux-user/syscall.c > patch-linux-user-syscall.c
$ wget http://download.savannah.gnu.org/releases/qemu/qemu-0.11.0.tar.gz; tar xvf qemu-0.11.0.tar.gz $ cd qemu-0.11.0 $ cp example/patch/ice_shared_lib_tk.h $ patch < example/patch/patch-exec-c $ patch < example/patch/patch-translate-all-c $ patch -p1 < example/patch/patch-linux-user-main.c $ patch -p1 < example/patch/patch-linux-user-syscall-c
修改內容
- 新增 ice_shared_lib_tk.h
- exe.c
#include "ice_shared_lib_tk.h" TranslationBlock *tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags) { phys_pc = get_phys_addr_code(env, pc); tb = tb_alloc(pc); // QEMU 的 code cache 已滿,flush code cache。 // TK_del_trace 清空 guest addr <-> host addr 的 AVLTree。 if (!tb) { /* flush must be done */ tb_flush(env); /* cannot fail at this point */ tb = tb_alloc(pc); /* Don't forget to invalidate previous TB info. */ tb_invalidated_flag = 1; /* For ICE Binary Optimizer */ TK_del_trace((void*)0, (void*)0, (void*)0, (void*)0); } }
- translate-all.c
#include "ice_shared_lib_tk.h" int cpu_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr) { // 將 guest addr <-> host addr 填入 AVLTree TK_add_trace((void*)tb->pc, (void*)(tb->pc+tb->size), (void*)tb->tc_ptr, (void*)((unsigned long)(tb->tc_ptr)+gen_code_size)); #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { qemu_log("OUT: [size=%d]\n", *gen_code_size_ptr); log_disas(tb->tc_ptr, *gen_code_size_ptr); qemu_log("\n"); qemu_log_flush(); } #endif return 0; }
- linux-user/main.c
#include "ice_shared_lib_tk.h" guest_thread_start_fp TK_guest_thread_start; /* call TK_guest_thread_start(...) in the beginning of a created thread. */ guest_thread_end_fp TK_guest_thread_end; /* call guest_thread_start(...) in the end of a created thread. */ add_trace_fp TK_add_trace; /* call TK_add_trace(...) when bulding a new trace. */ del_trace_fp TK_del_trace; /* call TK_del_trace(...) when deleting a trace. */ setup_optimizer_fp TK_setup_optimizer; /* The two following functions are not used as far. They do not need their functionalities now. */ int TK_regen_trace(int opt_f, int n_gcode, void* og_s_addr, void* og_e_addr, int* n_trace, void** g_s_addr, void** g_e_addr, void** h_s_addr, void** h_e_addr){return -1;} /* simulator must provide regen_trace(...) to generate the trace for the indicated guest code fragments. */ void* TK_get_guest_CFG(){return NULL;} /* simulator must provide get_guest_CFG(...) to return the information about guest CFG */ qemu_end_fp TK_qemu_end; void cpu_loop(CPUX86State *env) { // 啟動 dbo 做為另一隻執行緒 TK_guest_thread_start(GET_THREAD_ID); for(;;) { } } int main(int argc, char **argv, char **envp) { if (argc <= 1) usage(); // 取得 __libc_start_main 註冊的 DBO 函式。init_optimizer 請見 DBO 中的 shared_lib.h。 init_optimizer(argc, argv, &TK_guest_thread_start, &TK_add_trace, &TK_del_trace, &TK_setup_optimizer, &TK_guest_thread_end, &TK_qemu_end); TK_setup_optimizer(TK_regen_trace, TK_get_guest_CFG); qemu_cache_utils_init(envp); }
- linux-user/syscall.c
#include "ice_shared_lib_tk.h" abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) { switch(num) { case TARGET_NR_exit: TK_guest_thread_end(GET_THREAD_ID); case TARGET_NR_exit_group: TK_guest_thread_end(GET_THREAD_ID); } }
DBO 執行流程
- ice_shared_lib_s.cpp
// 避免 C++ Name mangling 機制破壞函式名 __libc_start_main #ifdef __cplusplus extern "C" { void __libc_start_main( PARAMS_START_MAIN ) { // 複製原本的命令行參數 for( i = 1; i < argc; i++ ) argv[i] = ubp_av[i]; // 註冊 DBO 相關的函式 argv[i + _OPTIMIZER_ACTIVE_F] = ubp_av[0]; argv[i + _GUEST_THREAD_START_P] = ( char* )guest_thread_start; argv[i + _ADD_TRACE_P] = ( char* )add_trace; argv[i + _DEL_TRACE_P] = ( char* )del_trace; argv[i + _GUEST_THREAD_END_P] = ( char* )guest_thread_end; // 取得真正的入口函式 real_libc_start_main = ( void( * )( PARAMS_START_MAIN ) )dlsym( RTLD_NEXT, "__libc_start_main" ); } } #endif /* __cplusplus */
- perfmon event buffer overflow 時的處理流程: guest_thread_start → thread_monitor → monitor_thread_start( ev_p, ut_id, buff_ov_handle ) → buff_ov_handle → process_buffer
由 LnQ 傳遞函式指針給 DBO
前述皆是由 DBO 傳遞函式指針給 LnQ。如果要由 LnQ 傳遞函式指針給 DBO,則有底下檔案需要修改。假設 LnQ 要傳遞 gen_trace 給 DBO。目前做法是利用 LnQ_pass_gen_trace。
LnQ_pass_gen_trace( void (*gen_trace_fp)(void* trace, void* back_jump) )
DBO
- shared_lib.h
// 在 argv 的索引 #define _PASS_GEN_TRACE_P 7 // 定義 gen_trace 函式指針型別 typedef void (*gen_trace_fp)(void* trace, void* back_jump); // 定義 LnQ 用來傳遞 gen_trace_fp 的函式指針型別 typedef void ( *lnq_pass_gen_trace_fp )(gen_trace_fp gt_fp);
- ice_shared_lib_s.h
void lnq_pass_gen_trace(gen_trace_fp gt_fp);
- ice_shared_lib_s.cpp
// 存放 gen_trace 函式指針的全域變數 gen_trace_fp gen_trace_p; __libc_start_main( PARAMS_START_MAIN ) { // malloc 大小隨傳遞的函式指針個數改變! argv = ( char** )malloc(( argc + 8 ) * sizeof( void* ) ); // 傳遞函式指針給 LnQ,LnQ 再透過此函式傳遞 gen_trace 的函式指針 argv[i + _PASS_GEN_TRACE_P] = ( char* )lnq_pass_gen_trace; real_libc_start_main = ( void( * )( PARAMS_START_MAIN ) )dlsym( RTLD_NEXT, "__libc_start_main" ); } // 此函式要傳給 LnQ,LnQ 再透過此函式傳遞 gen_trace 的函式指針 // 將該函式指針存放至先前的全域變數 void lnq_pass_gen_trace(gen_trace_fp gt_fp) { gen_trace_p = gt_fp; }
LnQ
- ice_shared_lib_tk.h
// 在 argv 的索引 #define _PASS_GEN_TRACE_P 7 // 定義總共要傳遞的函式指針個數 #define optimize_fun_num PASS_GEN_TRACE_P // 定義用來傳遞 gen_trace_fp 的函式指針型別 typedef void (*lnq_pass_gen_trace_fp)(void (*gen_trace_fp)(void* trace, void* back_jump)); // 預設的函式指針 static void void_f6(void (*gen_trace_fp)(void* trace, void* back_jump)) { #ifdef _MSG_FOR_ARGMENT_PASSING //fprintf(stderr, " .f5. "); #endif //return 0; } // LnQ 會呼叫此函式傳遞所需的函式指針 static inline int init_optimizer( int argc, char** argv, lnq_pass_gen_trace_fp* lnq_pass_gen_trace_p) { if( 0 != strcmp( _ACTIVE_STRING, argv[0] ) ) { // 如果有問題,傳入預設的函式指針 *lnq_pass_gen_trace_p = void_f6; } else { // 索引隨函式指針個數而有所變動! for( i = 1; i < 8; i++ ) if( NULL == argv[argc + i] ) { // 如果有問題,傳入預設的函式指針 *lnq_pass_gen_trace_p = void_f6; return -2; } // 取得 DBO 傳遞的函式指針 *lnq_pass_gen_trace_p = ( lnq_pass_gen_trace_fp )argv[argc + _PASS_GEN_TRACE_P]; } } // 供 linux-user/main.c 呼叫 LnQ_pass_gen_trace 以便傳遞 gen_trace 的函式指針 extern lnq_pass_gen_trace_fp LnQ_pass_gen_trace;
- linux-user/main.c
#ifdef CONFIG_USE_DBO #include "ice_shared_lib_tk.h" lnq_pass_gen_trace_fp LNQ_pass_gen_trace; #endif int main(int argc, char **argv, char **envp) { #ifdef CONFIG_USE_DBO // LnQ 透過 init_optimizer 得到負責傳遞 gen_trace 的函式指針 LnQ_pass_gen_trace init_optimizer(argc, argv, &TK_guest_thread_start, &TK_add_trace, &TK_del_trace, &TK_guest_thread_end, &LnQ_pass_gen_trace); // LnQ 可以如此傳遞 gen_trace 函式指針給 DBO // LnQ_pass_gen_trace(&gen_trace); #endif }
除錯
$ gdb qemu-i386 (gdb) r bzip2_base.i386-m32-gcc44 input.source 1
$ gdb (gdb) set environment LD_PRELOAD=/path/to/libdbo.so (gdb) file /path/to/qemu-i386 (gdb) run -U LD_PRELOAD bzip2_base.i386-m32-gcc44-annotated input.source 1
gdb 事實上會叫起 bash -c 執行 qemu-i386。所以 LD_PRELOAD 會影響 bash 和 qemu-i386。可以透過以下方法觀察到。
void __libc_start_main( PARAMS_START_MAIN ) { // 印出命令行參數,包括執行檔名稱 for( i = 0; i < argc; i++ ) std::cerr << ubp_av[i] << " "; std::cerr << "\n"; argv = ( char** )malloc(( argc + 8 ) * sizeof( void* ) ); //dbo.OpenELF(argv[3]); //dbo.GetCFGData("TEST"); //dbo.BuildCFG(); }
dbo 會在 entry point 開啟 argv[3] 的執行檔,這裡的命令行參數對 bash 和 qemu-i386 而言不一樣。這裡改用一隻 script 設定 LD_PRELOAD 和執行 qemu-i386,如此 LD_PRELOAD 就只會影響 qemu-i386。但是 gdb 只能執行 elf,故目前寫成 perl script 再用 perlcc 將其編譯成 elf。
set exec-wrapper env 'LD_PRELOAD=/path/to/libdbo.so' file /path/to/qemu-i386 # 設定中斷點 break guest_thread_start run -U LD_PRELOAD bzip2_base.i386-m32-gcc44-annotated input.source 1