目錄

DTrace Tail-Call Optimization 問題

目錄

正文

我在 DTrace 的時候,有時候會看到以下奇怪的 log,有奇怪的回傳值:

  0  52547 casper_mpo_socket_check_poll_t:return casper_mpo_socket_check_poll_t returned 0
  0  52549 casper_mpo_socket_check_receive_t:return casper_mpo_socket_check_receive_t returned 0
  0  52625 casper_mpo_vnode_check_lookup:return casper_mpo_vnode_check_lookup returned 0
  0  52625 casper_mpo_vnode_check_lookup:return casper_mpo_vnode_check_lookup returned 0
  0  52631 casper_mpo_vnode_check_open:return casper_mpo_vnode_check_open returned -105548670090024
  0  52681 casper_check_allowed_open:return casper_check_allowed_open returned 0
  0  52659 casper_mpo_vnode_check_stat_t:return casper_mpo_vnode_check_stat_t returned 0
  0  52635 casper_mpo_vnode_check_read_t:return casper_mpo_vnode_check_read_t returned 0

這是因為我的程式碼是這樣寫:

int casper_mpo_vnode_check_open(...) {
    // ... 一些檢查 ...
    return casper_check_allowed_open(...); 
}

編譯器(如 Clang/LLVM)發現 casper_mpo_vnode_check_open 在呼叫完 casper_check_allowed_open 後就沒事做了。為了優化效能:

  1. 它不會使用 call 指令(這會產生新的 Stack Frame)
  2. 它會直接使用 jmp 指令跳到下一個函數
  3. 它會直接重複使用目前的 Stack Frame

DTrace 的 fbt (Function Boundary Tracing) 依賴於函數的進入與退出指令(入口的 push %rbp 和出口的 ret):

  1. 因為使用了 jmp 而不是 ret,casper_mpo_vnode_check_open 的 fbt:::return 探針可能根本不會在正確的時間點觸發,或者是在執行 jmp 之前就觸發了
  2. 當 DTrace 試圖抓取 %rax 時,被呼叫的子函數(casper_check_allowed_open)可能還沒把真正的結果(例如 0)放進去。此時 %rax 裡裝的是上一個指令殘留的記憶體位址或指標,轉換成十進位就變成了你看到的奇怪負數