java instrumentation attach模式快速入門

本文旨在簡單粗暴體驗instrumentation attach模式的玩法贞让,給讀者一個直觀的體驗,概念方面不多介紹

場景

有一個spring的http接口定義如下筋夏,每次調用返回一個隨機uuid,此處的RandomUtil采用的hutool的工具類基公。

@GetMapping("/play")
@ResponseBody
public String health() {
  return RandomUtil.simpleUUID();
}

期望通過編寫一個agent,attach到當前進程實現串改程序邏輯轰豆,每次調用都返回hello胰伍。

開發(fā)一個agent jar

入口函數

先要寫一個agentmain函數,類似我們寫helloword的main函數

public static void agentmain(String agentArgs, Instrumentation inst)  throws ClassNotFoundException, UnmodifiableClassException, InterruptedException {
  //注冊字節(jié)碼轉換邏輯
  inst.addTransformer(new PlayClassFileTransformer(), true);
  //使之生效
  inst.retransformClasses(RandomUtil.class);
  System.out.println("Agent Main Done");
}

字節(jié)碼轉換邏輯

函數內部注冊了PlayClassFileTransformer, 內部實現邏輯:

  • 如果不是RandomUtil,則返回null,表示不作替換
  • 否則替換新的字節(jié)碼內容酸休,字節(jié)碼內容來自本地提前準備好的一個class文件
public class PlayClassFileTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (!"cn/hutool/core/util/RandomUtil".equals(className)){
            return null;
        }

        return getBytesFromFile("/Users/***/code/***/instrument-play/docs/RandomUtil.class");
    }

    public static byte[] getBytesFromFile(String fileName) {
        try {
            // precondition
            File file = new File(fileName);
            InputStream is = new FileInputStream(file);
            long length = file.length();
            byte[] bytes = new byte[(int) length];

            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset <bytes.length
                && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }

            if (offset < bytes.length) {
                throw new IOException("Could not completely read file "
                    + file.getName());
            }
            is.close();
            return bytes;
        } catch (Exception e) {
            System.out.println("error occurs in _ClassTransformer!"
                + e.getClass().getName());
            return null;
        }
    }
}

注意: 此處字節(jié)碼是我提前準備好的斑司,基于hutool的源碼隨便改了一筆渗饮,把simpleUUID函數的返回值改為了hello。

retransformClasses

注冊完轉換器后宿刮,替換邏輯執(zhí)行的時機需要依賴于此互站,所以需要手動執(zhí)行這個函數,否則替換邏輯是不生效的糙置。以下引用一小段ClassFileTransformer的注釋:

the transformer will be called for every new class definition and every class redefinition.

另外,此處因為需要指定需要retransform的類型标捺,所以agent的工程里也引入了對hutool的依賴:

<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>4.1.3</version>
  <scope>provided</scope>
</dependency>

manifest文件

jar包生成后需要manifest文件,所以添加如下maven配置:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
    <archive>
      <manifestEntries>
        <Agent-Class>org.example.instrument.AgentMain</Agent-Class>
        <Can-Redefine-Classes>true</Can-Redefine-Classes>
        <Can-Retransform-Classes>true</Can-Retransform-Classes>
      </manifestEntries>
    </archive>
  </configuration>

  <executions>
    <execution>
      <goals>
        <goal>attached</goal>
      </goals>
      <phase>package</phase>
    </execution>
  </executions>
</plugin>

打包

mvn clean package

ATTACH

接下來需要把agent jar attach到目標進程上去亡容。

此處我們假設目標進程冤今,已啟動,進程號為8888,則attach的代碼如下:

public static void main(String[] args) throws InterruptedException, IOException, AgentLoadException, AgentInitializationException, AttachNotSupportedException {
    VirtualMachine vmObj = null;
    try {
        vmObj = VirtualMachine.attach("8888");
        if (vmObj != null) {
            vmObj.loadAgent("<jar path>/instrument-play-1.0-SNAPSHOT-jar-with-dependencies.jar", null);
        }
    } finally {
        if (null != vmObj) {
            vmObj.detach();
        }
    }
}

效果體驗

完整源碼

查看

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末脚囊,一起剝皮案震驚了整個濱河市桐磁,隨后出現的幾起案子,更是在濱河造成了極大的恐慌我擂,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件看峻,死亡現場離奇詭異衙吩,居然都是意外死亡,警方通過查閱死者的電腦和手機分井,發(fā)現死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門霉猛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘫辩,你說我怎么就攤上這事》パ幔” “怎么了裸影?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長轩猩。 經常有香客問我,道長均践,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任鞭铆,我火速辦了婚禮焦影,結果婚禮上封断,老公的妹妹穿的比我還像新娘。我一直安慰自己澄港,他們只是感情好柄沮,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著祖搓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拯欧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天藏姐,我揣著相機與錄音该贾,去河邊找鬼。 笑死杨蛋,一個胖子當著我的面吹牛,可吹牛的內容都是我干的逞力。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼举庶,長吁一口氣:“原來是場噩夢啊……” “哼揩抡!你這毒婦竟也來了户侥?” 一聲冷哼從身側響起捅膘,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刃泌,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體耙替,經...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年硝烂,在試婚紗的時候發(fā)現自己被綠了铜幽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滞谢。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡除抛,死狀恐怖,靈堂內的尸體忽然破棺而出橄教,到底是詐尸還是另有隱情,我是刑警寧澤护蝶,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布翩迈,位于F島的核電站,受9級特大地震影響帽馋,放射性物質發(fā)生泄漏。R本人自食惡果不足惜绽族,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一衩藤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赏表,春花似錦、人聲如沸瓢剿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至忙菠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牛欢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工傍睹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骚亿。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓熊赖,卻偏偏與公主長得像来屠,于是被迫代替她去往敵國和親震鹉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355