++++ 伺服器列表|
* 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~~