++++ 伺服器列表| * 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 * [[http://lists.cs.uiuc.edu/pipermail/llvmdev/2008-April/013548.html]] * 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 1. benchmark 放在 itanium 下的 /data/Benchmarks。svn repo 放在 itanium 下的 /data/svn 目前使用工作站為: i7, i71, i722, ps3。 ====== Quick Start ====== ===== Build QEMU ===== 請見 [[QEMU]]。 ===== Build LLVM ===== 請見 [[LLVM]]。 [[http://llvm.org/docs/CodeGenerator.html#targetfeatures|Target Feature Matrix]] ===== SPEC ===== 閱讀 [[http://www.spec.org/cpu2006/docs/|SPEC CPU2006 Documentation]]、[[http://www.spec.org/cpu2006/docs/config.html|SPEC CPU2006 Config Files]] 和 [[http://www.spec.org/cpu2006/docs/runspec.html|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 - 中止測量C-z $ pstree -p | grep runspec -A2 -B2 $ killall 若要同時執行多個 benchmark,請手動並將其至於背景執行。 ====== Binary Translator ====== ===== LnQ ===== LLVM 2.8 svn。LLVM-GCC 2.6。 請見 [[http://itanium.iis.sinica.edu.tw/virtualization/doku.php?id=lnq|LnQ]]。 ==== 建置 LLVM ==== 目前使用 LLVM 2.8 svn 版本,較 LLVM 2.8 Release 舊。停用斷言只是臨時作法。 $ 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 ==== 加上 --enable-debug 可以進行除錯。 lnq-llvm-2.8 必須對 LLVM 2.8 Release 上補丁。 - 設定 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 讀取內容 ==== 開發手冊 ==== [[LnQ developer handbook|LnQ developer handbook]] ===== QEMU Hybrid ===== 請下載 LLVM 的 Release 版本。$llvm_prefix 為自訂安裝路徑。 請見 [[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 ====== 請見 [[http://itanium.iis.sinica.edu.tw/virtualization/doku.php?id=dbo|Annotaion Framwork & Binary Optimizer]]。 ===== 建置系統 ===== 系統依賴的函式庫 Boost 和 libelf 在 I7、i71 和 i722 上皆已安裝。 有多個 libelf 版本實作同一套 API。目前使用的是 [[http://www.mr511.de/software/english.html|GNU libelf]]。 perfmon 已不再維護。建議改用 perf。 ==== 建置 Annotaion Framwork & Binary Optimizer & LnQ-DBO ==== LnQ 必須加以修改以便和 DBO 協作。LnQ-DBO 位在 /data/svn/llvm-qemu/qemu/src/branches/lnq-dbo。DBO 位在 /data/svn/chenwj/dbo/trunk。 $ 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 === == 補丁 == [[http://www.linuxtutorialblog.com/post/introduction-using-diff-and-patch-tutorial|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 } ====== 除錯 ====== 只有 run 才能傳入命令行參數。 lnq-dbo 依序開啟 llvm translator 和 dbo 以便判別哪裡出錯。編譯 lnq-dbo 和 dbo 要加上 --enable-debug。 $ 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 上述方法不適用於 libdbo.so,但可以 LD_PRELOAD 其它的 .so。可能的解法請見 [[http://stackoverflow.com/questions/4703763/how-to-run-gdb-with-ld-preload|How to run gdb with LD_PRELOAD?]]。 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。 上述方法無效! 請見 [[http://sourceware.org/gdb/current/onlinedocs/gdb/Starting.html|set exec-wrapper]] 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 ====== 計劃網站 ====== * [[http://grid01.csie.ntu.edu.tw/parallel/|NTU Parallel Lab]] * [[http://itanium.iis.sinica.edu.tw/dokuwiki/doku.php|Sinica IIS Parallel Lab]] * [[http://itanium.iis.sinica.edu.tw/virtualization/doku.php|Virtualization Wiki]] * [[http://itanium.iis.sinica.edu.tw/~chenwj/source/|Source Browser]] ====== 外部連結 ====== * [[http://wiki.qemu.org/Main_Page|QEMU]] * [[http://qemu.sourcearchive.com|QEMU Source]] * [[http://llvm.org/|LLVM]] * [[http://www.boost.org/|Boost C++ Libraries]] * [[http://sourceforge.net/projects/elftoolchain/|ELF Tool Chain]] * [[http://perfmon2.sourceforge.net/|perfmon2]] * [[https://perf.wiki.kernel.org/index.php/Main_Page|perf]] * [[http://code.google.com/p/gpuocelot/|gpuocelot]] * [[http://www.gnu.org/software/glpk/|GLPK (GNU Linear Programming Kit)]] * [[wp>Dynamic loading]] * [[wp>Binary translation]] * [[wp>Virtual machine]] * [[google>__libc_start_main]] * [[http://download.savannah.gnu.org/releases/qemu/]] ====== 註腳 ===== ~~REFNOTES~~