作者 | 炎尋
?過去一年,ARMS基于eBPF技術(shù)打造了Kubernetes監(jiān)控,提供多語言無侵入的應(yīng)用性能,系統(tǒng)性能,網(wǎng)絡(luò)性能觀測能力,驗(yàn)證了eBPF技術(shù)的有效性。eBPF技術(shù)和生態(tài)發(fā)展很好,未來前景廣大,作為該技術(shù)的實(shí)踐者,本文目標(biāo)是通過回答7個核心問題介紹eBPF技術(shù)本身,為大家解開eBPF的面紗。
eBPF是什么?
eBPF是一個能夠在內(nèi)核運(yùn)行沙箱程序的技術(shù),提供了一種在內(nèi)核事件和用戶程序事件發(fā)生時安全注入代碼的機(jī)制,使得非內(nèi)核開發(fā)人員也可以對內(nèi)核進(jìn)行控制。隨著內(nèi)核的發(fā)展,eBPF 逐步從最初的數(shù)據(jù)包過濾擴(kuò)展到了網(wǎng)絡(luò)、內(nèi)核、安全、跟蹤等,而且它的功能特性還在快速發(fā)展中,早期的 BPF 被稱為經(jīng)典 BPF,簡稱cBPF,正是這種功能擴(kuò)展,使得現(xiàn)在的BPF被稱為擴(kuò)展BPF,簡稱eBPF。
eBPF的應(yīng)用場景是什么?
網(wǎng)絡(luò)優(yōu)化
eBPF兼具高性能和高可擴(kuò)展特性,使得其成為網(wǎng)絡(luò)方案中網(wǎng)絡(luò)包處理的優(yōu)選方案:
- 高性能
JIT編譯器提供近乎內(nèi)核本地代碼的執(zhí)行效率。
- 高可擴(kuò)展
在內(nèi)核的上下文里,可以快速地增加協(xié)議解析和路由策略。
故障診斷
eBPF通過kprobe,tracepoints跟蹤機(jī)制兼具內(nèi)核和用戶的跟蹤能力,這種端到端的跟蹤能力可以快速進(jìn)行故障診斷,與此同時eBPF支持以更加高效的方式透出profiling的統(tǒng)計數(shù)據(jù),而不需要像傳統(tǒng)系統(tǒng)需要將大量的采樣數(shù)據(jù)透出,使得持續(xù)地實(shí)時profiling成為可能。
?
安全控制
eBPF可以看到所有系統(tǒng)調(diào)用,所有網(wǎng)絡(luò)數(shù)據(jù)包和socket網(wǎng)絡(luò)操作,一體化結(jié)合進(jìn)程上下文跟蹤,網(wǎng)絡(luò)操作級別過濾,系統(tǒng)調(diào)用過濾,可以更好地提供安全控制。
性能監(jiān)控
相比于傳統(tǒng)的系統(tǒng)監(jiān)控組件比如sar,只能提供靜態(tài)的counters和gauges,eBPF支持可編程地動態(tài)收集和邊緣計算聚合自定義的指標(biāo)和事件,極大地提升了性能監(jiān)控的效率和想象空間。
eBPF為什么會出現(xiàn)?
eBPF的出現(xiàn)本質(zhì)上是為了解決內(nèi)核迭代速度慢和系統(tǒng)需求快速變化的矛盾,在eBPF領(lǐng)域常用的一個例子是eBPF相對于Linux Kernel類似于Javascript相對于HTML,突出的是可編程性。一般來說可編程性的支持通常會帶來一些新的問題,比如內(nèi)核模塊其實(shí)也是為了解決這個問題,但是他沒有提供很好的邊界,導(dǎo)致內(nèi)核模塊會影響內(nèi)核本身的穩(wěn)定性,在不同的內(nèi)核版本需要做適配等。eBPF采用以下策略,使得其成為一種安全高效地內(nèi)核可編程技術(shù):
- 安全
eBPF 程序必須被驗(yàn)證器校驗(yàn)通過后才能執(zhí)行,且不能包含無法到達(dá)的指令;eBPF 程序不能隨意調(diào)用內(nèi)核函數(shù),只能調(diào)用在 API 中定義的輔助函數(shù);eBPF 程序??臻g最多只有 512 字節(jié),想要更大的存儲,就必須要借助映射存儲。
- 高效
借助即時編譯器(JIT),且因?yàn)?eBPF 指令依然運(yùn)行在內(nèi)核中,無需向用戶態(tài)復(fù)制數(shù)據(jù),大大提高了事件處理的效率。
- 標(biāo)準(zhǔn)
通過BPF Helpers,BTF,PERF MAP提供標(biāo)準(zhǔn)的接口和數(shù)據(jù)模型供開發(fā)者使用。
- 功能強(qiáng)大
eBPF 不僅擴(kuò)展了寄存器的數(shù)量,引入了全新的 BPF 映射存儲,還在 4.x 內(nèi)核中將原本單一的數(shù)據(jù)包過濾事件逐步擴(kuò)展到了內(nèi)核態(tài)函數(shù)、用戶態(tài)函數(shù)、跟蹤點(diǎn)、性能事件(perf_events)以及安全控制等領(lǐng)域。
eBPF怎么用?
5個步驟
1.使用 C 語言開發(fā)一個 eBPF 程序;
即插樁點(diǎn)觸發(fā)事件時要調(diào)用的eBPF沙箱程序,該程序會在內(nèi)核態(tài)運(yùn)行。
?2.借助 LLVM 把 eBPF 程序編譯成 BPF 字節(jié)碼;
eBPF 程序編譯成 BPF 字節(jié)碼,用于后續(xù)在eBPF虛擬機(jī)內(nèi)驗(yàn)證并運(yùn)行。
?3.通過 bpf 系統(tǒng)調(diào)用,把 BPF 字節(jié)碼提交給內(nèi)核;
在用戶態(tài)通過bpf系統(tǒng),將BPF字節(jié)碼加載到內(nèi)核。
?4.內(nèi)核驗(yàn)證并運(yùn)行 BPF 字節(jié)碼,并把相應(yīng)的狀態(tài)保存到 BPF 映射中;
內(nèi)核驗(yàn)證BPF字節(jié)碼安全,并且確保對應(yīng)事件發(fā)生時調(diào)用正確的eBPF程序,如果有狀態(tài)需要保存,則寫入對應(yīng)BPF映射中,比如監(jiān)控數(shù)據(jù)就可以寫到BPF映射中。?
5.用戶程序通過 BPF 映射查詢 BPF 字節(jié)碼的運(yùn)行狀態(tài)。
用戶態(tài)通過查詢BPF映射的內(nèi)容,獲取字節(jié)碼運(yùn)行的狀態(tài),比如獲取抓取到的監(jiān)控數(shù)據(jù)。
?一個完整的 eBPF 程序,通常包含用戶態(tài)和內(nèi)核態(tài)兩部分:用戶態(tài)程序需要通過 BPF 系統(tǒng)調(diào)用跟內(nèi)核進(jìn)行交互,進(jìn)而完成 eBPF 程序加載、事件掛載以及映射創(chuàng)建和更新等任務(wù);而在內(nèi)核態(tài)中,eBPF 程序也不能任意調(diào)用內(nèi)核函數(shù),而是需要通過 BPF 輔助函數(shù)完成所需的任務(wù)。尤其是在訪問內(nèi)存地址的時候,必須要借助 bpf_probe_read 系列函數(shù)讀取內(nèi)存數(shù)據(jù),以確保內(nèi)存的安全和高效訪問。在 eBPF 程序需要大塊存儲時,我們還需要根據(jù)應(yīng)用場景,引入特定類型的 BPF 映射,并借助它向用戶空間的程序提供運(yùn)行狀態(tài)的數(shù)據(jù)。?
eBPF程序分類和使用場景
bpftool feature probe | grep program_type
以上命令可以查看系統(tǒng)支持的eBPF程序類型,一般有如下類型:
eBPF program_type socket_filter is available
eBPF program_type kprobe is available
eBPF program_type sched_cls is available
eBPF program_type sched_act is available
eBPF program_type tracepoint is available
eBPF program_type xdp is available
eBPF program_type perf_event is available
eBPF program_type cgroup_skb is available
eBPF program_type cgroup_sock is available
eBPF program_type lwt_in is available
eBPF program_type lwt_out is available
eBPF program_type lwt_xmit is available
eBPF program_type sock_ops is available
eBPF program_type sk_skb is available
eBPF program_type cgroup_device is available
eBPF program_type sk_msg is available
eBPF program_type raw_tracepoint is available
eBPF program_type cgroup_sock_addr is available
eBPF program_type lwt_seg6local is available
eBPF program_type lirc_mode2 is NOT available
eBPF program_type sk_reuseport is available
eBPF program_type flow_dissector is available
eBPF program_type cgroup_sysctl is available
eBPF program_type raw_tracepoint_writable is available
eBPF program_type cgroup_sockopt is available
eBPF program_type tracing is available
eBPF program_type struct_ops is available
eBPF program_type ext is available
eBPF program_type lsm is available
具體可參考https://elixir.bootlin.com/linux/v5.13/source/include/linux/bpf_types.h
主要是分為3大使用場景:
- 跟蹤
tracepoint, kprobe, perf_event等,主要用于從系統(tǒng)中提取跟蹤信息,進(jìn)而為監(jiān)控、排錯、性能優(yōu)化等提供數(shù)據(jù)支撐。
- 網(wǎng)絡(luò)
xdp, sock_ops, cgroup_sock_addr , sk_msg等,主要用于對網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)行過濾和處理,進(jìn)而實(shí)現(xiàn)網(wǎng)絡(luò)的觀測、過濾、流量控制以及性能優(yōu)化等各種豐富的功能,這里可以丟包,重定向。
cilium基本用了所有的hook點(diǎn)。
- 安全和其他
lsm,用于安全,其他還有flow_dissector, lwt_in都是一些不怎么常用的,不再贅述。
eBPF的最佳實(shí)踐是什么?
尋找內(nèi)核的插樁點(diǎn)
從前面可以看出來eBPF程序本身并不困難,困難的是為其尋找合適的事件源來觸發(fā)運(yùn)行。對于監(jiān)控和診斷領(lǐng)域來說,跟蹤類eBPF程序的事件源包含3類:內(nèi)核函數(shù)(kprobe)、內(nèi)核跟蹤點(diǎn)(tracepoint)或性能事件(perf_event)。此時有2個問題需要回答:
1.內(nèi)核中都有哪些內(nèi)核函數(shù)、內(nèi)核跟蹤點(diǎn)或性能事件?
- 使用調(diào)試信息獲取內(nèi)核函數(shù)、內(nèi)核跟蹤點(diǎn)
sudo ls /sys/kernel/debug/tracing/events
- 使用bpftrace獲取內(nèi)核函數(shù)、內(nèi)核跟蹤點(diǎn)
# 查詢所有內(nèi)核插樁和跟蹤點(diǎn)
sudo bpftrace -l
# 使用通配符查詢所有的系統(tǒng)調(diào)用跟蹤點(diǎn)
sudo bpftrace -l 'tracepoint:syscalls:*'
# 使用通配符查詢所有名字包含"open"的跟蹤點(diǎn)
sudo bpftrace -l '*open*'
- 使用perf list獲取性能事件
sudo perf list tracepoint
2.對于內(nèi)核函數(shù)和內(nèi)核跟蹤點(diǎn),在需要跟蹤它們的傳入?yún)?shù)和返回值的時候,又該如何查詢這些數(shù)據(jù)結(jié)構(gòu)的定義格式呢?
- 使用調(diào)試信息獲取
sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/format
- 使用bpftrace獲取
具體如何使用以上信息,請參考bcc。
尋找應(yīng)用的插樁點(diǎn)
1.如何查詢用戶進(jìn)程的跟蹤點(diǎn)?
- 靜態(tài)編譯語言通過-g編譯選項保留調(diào)試信息,應(yīng)用程序二進(jìn)制會包含DWARF(Debugging With Attributed Record Format),有了調(diào)試信息,可以通過 readelf、objdump、nm 等工具,查詢可用于跟蹤的函數(shù)、變量等符號列表
# 查詢符號表
readelf -Ws /usr/lib/x86_64-linux-gnu/libc.so.6
# 查詢USDT信息
readelf -n /usr/lib/x86_64-linux-gnu/libc.so.6
- 使用bpftrace
# 查詢uprobe
bpftrace -l 'uprobe:/usr/lib/x86_64-linux-gnu/libc.so.6:*'
# 查詢USDT
bpftrace -l 'usdt:/usr/lib/x86_64-linux-gnu/libc.so.6:*'
uprobe 是基于文件的。當(dāng)文件中的某個函數(shù)被跟蹤時,除非對進(jìn)程 PID 進(jìn)行了過濾,默認(rèn)所有使用到這個文件的進(jìn)程都會被插樁。
上面說的是靜態(tài)編譯語言,他和內(nèi)核的跟蹤類似,應(yīng)用程序的符號信息可以存放在 ELF 二進(jìn)制文件中,也可以以單獨(dú)文件的形式,放到調(diào)試文件中;而內(nèi)核的符號信息除了可以存放到內(nèi)核二進(jìn)制文件中之外,還會以 /proc/kallsyms 和 /sys/kernel/debug 等形式暴露到用戶空間。
對于非靜態(tài)編譯語言來說,主要是兩種:
- 解釋型語言
使用類似編譯型語言應(yīng)用程序的跟蹤點(diǎn)查詢方法,查詢它們在解釋器層面的 uprobe 和 USDT 跟蹤點(diǎn),如何將解釋器層面的行為和應(yīng)用行為關(guān)聯(lián)需要相關(guān)語言的專家來分析。
- 即時編譯型語言
這類語言的應(yīng)用源代碼會先編譯為字節(jié)碼,再由即時編譯器(JIT)編譯為機(jī)器碼執(zhí)行,還會有大量的優(yōu)化,跟蹤難度很大,同解釋型編程語言類似,uprobe 和 USDT 跟蹤只能用在即時編譯器上,從即時編譯器的跟蹤點(diǎn)參數(shù)里面獲取最終應(yīng)用程序的函數(shù)信息。找出即時編譯器的跟蹤點(diǎn)同應(yīng)用程序運(yùn)行之間的關(guān)系需要相關(guān)語言的專家來分析。
可以參考BCC的應(yīng)用程序跟蹤,用戶進(jìn)程的跟蹤,本質(zhì)上是通過斷點(diǎn)去執(zhí)行 uprobe 處理程序。雖然內(nèi)核社區(qū)已經(jīng)對 BPF 做了很多的性能調(diào)優(yōu),跟蹤用戶態(tài)函數(shù)(特別是鎖爭用、內(nèi)存分配之類的高頻函數(shù))還是有可能帶來很大的性能開銷。因此,我們在使用 uprobe 時,應(yīng)該盡量避免跟蹤高頻函數(shù)。
具體如何使用以上信息,請參考:https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#events--arguments
關(guān)聯(lián)問題與插樁點(diǎn)
一個理想的狀態(tài)是所有問題都清楚應(yīng)當(dāng)觀察那些插樁點(diǎn),但是這個要求技術(shù)人員對端到端的軟件棧細(xì)節(jié)都了解十分透徹,一個更加合理的方法是二八法則,將軟件棧數(shù)據(jù)流的最核心的80%脈絡(luò)抓住,保障出現(xiàn)問題一定會在這個脈絡(luò)被發(fā)現(xiàn)即可。此時再使用內(nèi)核棧和用戶棧來查看具體的調(diào)用棧即可發(fā)現(xiàn)核心問題,比如說發(fā)現(xiàn)了網(wǎng)絡(luò)在丟包,但是不知道為什么丟,此時我們知道網(wǎng)絡(luò)丟包一定會調(diào)用kfree_skb內(nèi)核函數(shù),那么我們可以通過:
sudo bpftrace -e 'kprobe:kfree_skb /comm=="<your comm>"/ {printf("kstack: %s\n", kstack);}'
發(fā)現(xiàn)該函數(shù)的調(diào)用棧:
kstack: kfree_skb+1 udpv6_destroy_sock+66 sk_common_release+34 udp_lib_close+9 inet_release+75 inet6_release+49 __sock_release+66 sock_close+21 __fput+159 ____fput+14 task_work_run+103 exit_to_user_mode_loop+411 exit_to_user_mode_prepare+187 syscall_exit_to_user_mode+23 do_syscall_64+110 entry_SYSCALL_64_after_hwframe+68
那么就可以回溯上面的函數(shù),看看他們具體是哪一行在什么條件下調(diào)用的,就能夠定位到問題。這個方法不僅可以定位問題,也可以用于加深對內(nèi)核調(diào)用的理解,比如:
可以查看所有網(wǎng)絡(luò)相關(guān)的跟蹤點(diǎn)及其調(diào)用棧。
eBPF的實(shí)現(xiàn)原理是什么?
5個模塊
eBPF在內(nèi)核主要由5個模塊協(xié)作:
1.BPF Verifier(驗(yàn)證器)
確保 eBPF 程序的安全。驗(yàn)證器會將待執(zhí)行的指令創(chuàng)建為一個有向無環(huán)圖(DAG),確保程序中不包含不可達(dá)指令;接著再模擬指令的執(zhí)行過程,確保不會執(zhí)行無效指令,這里通過和個別同學(xué)了解到,這里的驗(yàn)證器并無法保證100%的安全,所以對于所有BPF程序,都還需要嚴(yán)格的監(jiān)控和評審。
?2.BPF JIT
將 eBPF 字節(jié)碼編譯成本地機(jī)器指令,以便更高效地在內(nèi)核中執(zhí)行。
?3.多個 64 位寄存器、一個程序計數(shù)器和一個 512 字節(jié)的棧組成的存儲模塊
用于控制eBPF程序的運(yùn)行,保存棧數(shù)據(jù),入?yún)⑴c出參。
?4.BPF Helpers(輔助函數(shù))
提供了一系列用于 eBPF 程序與內(nèi)核其他模塊進(jìn)行交互的函數(shù)。這些函數(shù)并不是任意一個 eBPF 程序都可以調(diào)用的,具體可用的函數(shù)集由 BPF 程序類型決定。注意,eBPF里面所有對入?yún)ⅲ鰠⒌男薷亩急仨毞螧PF規(guī)范,除了本地變量的變更,其他變化都應(yīng)當(dāng)使用BPF Helpers完成,如果BPF Helpers不支持,則無法修改。
通過以上命令可以看到不同類型的eBPF程序可以運(yùn)行哪些BPF Helpers。
?5.BPF Map & context
用于提供大塊的存儲,這些存儲可被用戶空間程序用來進(jìn)行訪問,進(jìn)而控制 eBPF 程序的運(yùn)行狀態(tài)。
bpftool feature probe
通過以上命令可以看到系統(tǒng)支持哪些類型的map。
3個動作
先說下重要的系統(tǒng)調(diào)用bpf:
int bpf(int cmd, union bpf_attr *attr, unsigned int size);
這里cmd是關(guān)鍵,attr是cmd的參數(shù),size是參數(shù)大小,所以關(guān)鍵是看cmd有哪些
// 5.11內(nèi)核
enum bpf_cmd {
BPF_MAP_CREATE,
BPF_MAP_LOOKUP_ELEM,
BPF_MAP_UPDATE_ELEM,
BPF_MAP_DELETE_ELEM,
BPF_MAP_GET_NEXT_KEY,
BPF_PROG_LOAD,
BPF_OBJ_PIN,
BPF_OBJ_GET,
BPF_PROG_ATTACH,
BPF_PROG_DETACH,
BPF_PROG_TEST_RUN,
BPF_PROG_GET_NEXT_ID,
BPF_MAP_GET_NEXT_ID,
BPF_PROG_GET_FD_BY_ID,
BPF_MAP_GET_FD_BY_ID,
BPF_OBJ_GET_INFO_BY_FD,
BPF_PROG_QUERY,
BPF_RAW_TRACEPOINT_OPEN,
BPF_BTF_LOAD,
BPF_BTF_GET_FD_BY_ID,
BPF_TASK_FD_QUERY,
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
BPF_MAP_FREEZE,
BPF_BTF_GET_NEXT_ID,
BPF_MAP_LOOKUP_BATCH,
BPF_MAP_LOOKUP_AND_DELETE_BATCH,
BPF_MAP_UPDATE_BATCH,
BPF_MAP_DELETE_BATCH,
BPF_LINK_CREATE,
BPF_LINK_UPDATE,
BPF_LINK_GET_FD_BY_ID,
BPF_LINK_GET_NEXT_ID,
BPF_ENABLE_STATS,
BPF_ITER_CREATE,
BPF_LINK_DETACH,
BPF_PROG_BIND_MAP,
};
最核心的就是PROG,MAP相關(guān)的cmd,就是程序加載和映射處理。
?1.程序加載
調(diào)用BPF_PROG_LOAD cmd,會將BPF程序加載到內(nèi)核,但eBPF 程序并不像常規(guī)的線程那樣,啟動后就一直運(yùn)行在那里,它需要事件觸發(fā)后才會執(zhí)行。這些事件包括系統(tǒng)調(diào)用、內(nèi)核跟蹤點(diǎn)、內(nèi)核函數(shù)和用戶態(tài)函數(shù)的調(diào)用退出、網(wǎng)絡(luò)事件,等等,所以需要第2個動作。
?2.綁定事件
b.attach_kprobe(event="xxx", fn_name="yyy")
以上就是將特定的事件綁定到特定的BPF函數(shù),實(shí)際實(shí)現(xiàn)原理如下:
(1)借助 bpf 系統(tǒng)調(diào)用,加載 BPF 程序之后,會記住返回的文件描述符;
(2)通過attach操作知道對應(yīng)函數(shù)類型的事件編號;
(3)根據(jù)attach的返回值調(diào)用 perf_event_open 創(chuàng)建性能監(jiān)控事件;
(4)通過 ioctl 的 PERF_EVENT_IOC_SET_BPF 命令,將 BPF 程序綁定到性能監(jiān)控事件。
?3.映射操作
通過MAP相關(guān)的cmd,控制MAP增刪,然后用戶態(tài)基于該MAP與內(nèi)核狀態(tài)進(jìn)行交互。
eBPF的發(fā)展現(xiàn)狀?
內(nèi)核支持
建議>=4.14
生態(tài)
eBPF的生態(tài)自下而上的情況如下:
1.基礎(chǔ)設(shè)施
支持eBPF基礎(chǔ)能力的發(fā)展。
- Linux Kernal
- LLVM
2.開發(fā)工具集
主要是用于加載,編譯,調(diào)試eBPF程序,不同語言有不同的開發(fā)工具集:
- Go
https://github.com/cilium/ebpf
https://github.com/aquasecurity/libbpfgo
- C/C++
https://github.com/libbpf/libbpf
?3、eBPF應(yīng)用
- bcc(https://github.com/iovisor/bcc)
提供一套開發(fā)工具和腳本。
- bpftrace(https://github.com/iovisor/bpftrace)
基于bcc,提供一個腳本語言。
- cilium(https://github.com/cilium/cilium)
網(wǎng)絡(luò)優(yōu)化和安全
- Falco(https://github.com/falcosecurity/falco)
網(wǎng)絡(luò)安全
- Katran(https://github.com/facebookincubator/katran)
高性能4層負(fù)載均衡
- Hubble(https://github.com/cilium/hubble)
可觀測
- Kindling(https://github.com/CloudDectective-Harmonycloud/kindling)
可觀測
- Pixie(https://github.com/pixie-io/pixie)
可觀測
- kubectl trace(https://github.com/iovisor/kubectl-trace)
調(diào)度bpftrace腳本
- L3AF(https://github.com/l3af-project/l3afd)
分布式環(huán)境下啟動和管理eBPF程序的平臺
- ply(https://github.com/iovisor/ply)
動態(tài)linux trace
- Tracee(https://github.com/aquasecurity/tracee)
Linux運(yùn)行時安全監(jiān)測
4、跟蹤生態(tài)的網(wǎng)站
- https://ebpf.io/projects
- https://github.com/zoidbergwill/awesome-ebpf
寫在最后
用好eBPF的前提是對軟件棧的理解
通過上面的介紹,相信大家對eBPF已經(jīng)有了足夠的理解,eBPF提供的只是一個框架和機(jī)制,核心還是需要用eBPF的人對軟件棧的理解,找到合適的插樁點(diǎn),能夠和應(yīng)用問題進(jìn)行關(guān)聯(lián)。
eBPF的殺手锏是全覆蓋,無侵入,可編程
1.全覆蓋
內(nèi)核,應(yīng)用程序插樁點(diǎn)全覆蓋。
?2.無侵入
不需要修改任何被hook的代碼。
3.可編程
動態(tài)下發(fā)eBPF程序,邊緣動態(tài)執(zhí)行指令,動態(tài)聚合分析。