Byteman 使用指南(四)

FunTester發表於2025-02-07

本文銜接上文,講解 Byteman 注入點的功能,也就是前文提到的條件。

AT LINE

AT LINE 說明符將觸發點定位在觸發方法中第一個可執行位元組碼指令之前,其原始碼行號大於或等於說明符引數中提供的行號。如果沒有在(或之後)指定的行號處的可執行程式碼,代理將不會插入觸發點(注意,在這種情況下它不會列印錯誤,因為這可能只是表明規則不適用於這個特定類或方法)。

AT READ

AT READ 說明符後面跟著欄位名稱,定位觸發點在第一個出現的物件欄位之前,即它對應於位元組碼中的第一個 getField 指令。如果指定了型別,則 getField 指令將僅在命名欄位由名稱與提供的型別匹配的類宣告時才匹配。如果提供了計數 N,則第 N 個匹配的 getField 將被用作觸發點。注意,計數標識欄位訪問的第 N 次文字出現,而不是在特定執行路徑中的第 N 次欄位訪問。如果關鍵字 ALL 被指定代替計數,則規則將在所有匹配的 getField 呼叫中觸發。

AT READ 說明符後面跟著一個以 $ 為字首的區域性變數名稱、方法引數名稱或方法引數索引,定位觸發點在讀取相應的區域性或方法引數變數之前,即它對應於位元組碼中的 iloaddloadaload 等指令。如果提供了計數 N,則第 N 個匹配的讀取將被用作觸發點。注意,計數標識變數讀取的第 N 次文字出現,而不是在特定執行路徑中的第 N 次訪問。如果關鍵字 ALL 被指定代替計數,則規則將在每次讀取變數之前觸發。

注意,只有在觸發方法的位元組碼中包含了區域性變數表(例如,如果它已經用 -g 標誌編譯),才可以使用區域性或引數變數名稱,如 $i, $this$arg1。相比之下,總是可以使用索引符號 $0, $1 等來引用引數變數讀取操作(然而,請注意,位置 AT READ $0 將僅匹配例項方法)。

AFTER READ

AFTER READ 規範與 AT READ 規範相同,只是它將觸發點定位在 getField 或變數讀取操作之後。

AT WRITE, AFTER WRITE

AT WRITEAFTER WRITE 說明符與相應的 READ 說明符相同,只是它們對應於原始碼中對命名欄位或命名變數的賦值,即它們識別 putFieldistore, dstore, 等指令。

注意,位置 AT WRITE $0 或等效位置 AT WRITE $this 永遠不會匹配任何候選觸發方法,因為例項方法呼叫的目標物件永遠不會被分配。

同樣,對於給定的區域性變數,位置 AT WRITE $localvar 或等效位置 AT WRITE $localvar 1 識別區域性變數初始化後立即的位置,即它被視為指定為 AFTER WRITE $localvar 。這是必要的,因為變數在初始化後才有範圍。這也確保了在規則正文中可以安全地訪問已寫入的區域性變數。注意,當觸發程式碼使用相關的除錯選項編譯時,代理能夠將觸發點範圍內的區域性變數作為引數傳遞給觸發呼叫,使它們作為預設繫結可用。規則可以引用範圍內的變數(包括方法接收者和引數),透過在它們的象徵名稱前加上 $ 字元,例如 $this, $arg1, $i 等。

代理還編譯了圍繞觸發呼叫的異常處理程式程式碼,以處理規則處理過程中可能發生的異常。這不是為了處理規則執行引擎檢測到的錯誤(它們應該全部被內部捕獲和處理)。異常是從執行引擎丟擲的,以改變觸發方法的控制流。通常,在從觸發呼叫返回後,觸發執行緒繼續執行原始方法程式碼。然而,規則可以使用返回和丟擲內建動作來指定從觸發方法執行早期返回或異常丟擲。規則語言實現透過在觸發呼叫下方丟擲其自己的私有內部異常來實現這一點。編譯到觸發方法中的處理程式程式碼會捕獲這些內部異常,然後返回給呼叫者或遞迴丟擲執行時或應用程式特定的異常。這避免了觸發方法主體中剩餘程式碼的正常執行。如果觸發點還有其它觸發呼叫待處理,則這些也會被繞過。

AT INVOKE, AFTER INVOKE 呼叫時、呼叫後

AT INVOKEAFTER INVOKE 說明符類似於 READWRITE 說明符,只不過它們將觸發方法內的方法或建構函式的呼叫標識為觸發點。該方法可以使用裸方法名稱來標識,或者該名稱可以由可能是包限定的型別或描述符限定。描述符由括號內以逗號分隔的型別名稱列表組成。型別名稱標識方法引數的型別,並且可以使用包限定符作為字首,並使用陣列括號對作為字尾。

AT NEW, AFTER NEW 建立前後

AT NEWAFTER NEW 說明符標識目標方法中 new 操作建立 Java 物件類或陣列類的位置。在分配物件或陣列之前會觸發 AT NEW 規則。建立並初始化物件或陣列後會觸發 AFTER NEW 規則。

NEW 觸發器位置的選擇可以透過提供各種可選引數、型別名稱、一對或多對方括號以及整數計數或關鍵字 ALL 來限制。這些引數都可以獨立指定,並且它們各自用於為可以考慮將規則注入到目標方法中的點選擇一組或多或少精確的匹配。

如果提供了型別名稱,則注入僅限於建立命名型別的例項(或陣列)的點。可以在不提供包限定符的情況下提供型別名稱,在這種情況下,具有共享相同非包限定名稱的型別的任何新操作都將匹配。

如果省略型別名稱,則注入可以在建立例項(或陣列)的任何點發生。

請注意,匹配時會忽略 extendsimplements 關係。例如,如果規則指定 AT NEW Foo 則即使 FooBar extends Foo 該位置也不會與操作 new Foobar 匹配。類似地,當 Foo implements IFoo 時,指定位置 AT NEW IFoo 將不會匹配。事實上,指定任何介面都是一個錯誤。新操作總是例項化特定的類,而不是介面。因此,指定介面名稱的位置永遠不會匹配。

如果包含一對或多對大括號,則注入僅限於方法中建立具有相同維數的陣列的點。因此,例如指定 AT NEW [][] 將匹配建立 2d 陣列的任何新操作,無論陣列基本型別是什麼,相比之下,指定 AT NEW int[] 將僅匹配建立 1d 陣列的新操作 int 陣列已建立。如果沒有提供大括號,則匹配將僅限於例項化 Java 物件類(即非陣列類)的新操作。

當方法中有多個候選注入點時,可以提供整數計數來選擇特定的注入點(如果未指定,則計數預設為 1)。可以提供關鍵字 ALL 以請求所有匹配注入點的注入。

AT SYNCHRONIZE, AFTER SYNCHRONIZE 同步時、同步後

AT SYNCHRONIZEAFTER SYNCHRONIZE 說明符標識目標方法中的同步塊,即它們對應於位元組碼中的 MONITORENTER 指令。請注意,AFTER SYNCHRONIZE 標識緊接著進入同步塊之後的點,而不是緊接著退出同步塊之後的點。

AT THROW 丟擲

AT THROW 說明符將觸發方法內的丟擲操作標識為觸發點。丟擲操作可以由標識所丟擲異常的詞法型別的型別名(可能是包限定的)來限定。如果提供了計數 N,則該位置指定丟擲的第 N 個文字出現。如果指定關鍵字 ALL 來代替計數,則將在所有匹配的丟擲事件發生時觸發該規則。

AT EXCEPTION EXIT 異常退出

AT EXCEPTION EXIT 說明符標識方法透過未處理的異常控制流將控制返回給其呼叫者的點。發生這種情況的原因可能是該方法本身引發了異常,也可能是因為它呼叫了引發異常的其他方法。當方法在 Java 語言中執行某些操作時也可能發生這種情況,例如取消引用空物件值或索引超出陣列末尾。

在此位置注入的規則將在異常通常傳播回撥用者的點觸發。一旦規則執行完成,異常流程通常會恢復。然而,該規則可能會透過執行 RETURN 來破壞此恢復的流程。它還可以顯式地重新丟擲原始異常或透過執行 THROW 丟擲一些新建立的異常(注意,如果後者是受檢查異常,則必須透過觸發方法將其宣告為可能的異常)。

注:當多個規則指定相同位置時,觸發器呼叫的注入順序通常遵循各自指令碼中規則的順序。例外情況是 AFTER 位置,其中注入順序與發生順序相反。

注:當位置說明符(ENTRYEXIT 除外)與重寫規則一起使用時,如果位置與相關方法匹配,則規則程式碼僅會注入到原始方法或重寫方法中。因此,例如,如果採用位置 AT READ myField 2,則該規則將僅被注入到包含兩次欄位 myField 載入的方法的實現中。與位置不匹配的方法將被忽略。

由於歷史原因,CALL 可以用作 INVOKE 的同義詞,RETURN 可以用作 EXIT 的同義詞,並且 AT LINE 說明符中的 AT 是可選的。

FunTester 原創精華

【連載】從 Java 開始效能測試

  • 混沌工程、故障測試、Web 前端
  • 服務端功能測試
  • 效能測試專題
  • Java、Groovy、Go
  • 白盒、工具、爬蟲、UI 自動化
  • 理論、感悟、影片
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。