0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ART虛擬機(jī)method tracing技術(shù)解析

Linux閱碼場 ? 來源:內(nèi)核工匠 ? 2023-04-28 09:37 ? 次閱讀

一、method tracing介紹

概述

這個(gè)是谷歌提供的對java的函數(shù)級trace工具,和systrace只支持打點(diǎn)不同,method tracing能支持到函數(shù),看到具體的函數(shù)執(zhí)行時(shí)間,準(zhǔn)確的分析出來執(zhí)行的時(shí)間短板。

99f6dfc0-e4eb-11ed-ab56-dac502259ad0.png

1.生成trace的方式

sampling方式:

sampling方式采用sample任務(wù),定期抓取各個(gè)線程的調(diào)用棧,采集精度和采集的頻次正相關(guān),同時(shí)由于java stack采集的時(shí)候需要做suspend,因此還是有一部分的效率損失。

9a32dc82-e4eb-11ed-ab56-dac502259ad0.png

我們可以看到,原生單次采集使用的是suspendall,而不是對threadlist上的線程逐個(gè)做getStackTrace,因此效率損失會比較嚴(yán)重。

trace方式:

通過在執(zhí)行流程插入enter-exit來觀測:

9a4cb346-e4eb-11ed-ab56-dac502259ad0.png

相比于sample 方式,trace可以準(zhǔn)確的獲取到每個(gè)函數(shù)的進(jìn)入和退出時(shí)間,精度可以非常高。

由于art虛擬機(jī)執(zhí)行特點(diǎn),這個(gè)方案相較于sample方式復(fù)雜度要高不少,下文會著重介紹trace方式的實(shí)現(xiàn)原理

2.trace啟動流程

我們從trace方式的啟動入口開始看起

9a5f9d9e-e4eb-11ed-ab56-dac502259ad0.png

幾個(gè)關(guān)鍵的流程分別是

1.停用掉JIT GC,這個(gè)是防止stub方式替換之后,因?yàn)镴IT GC引起的重新指定執(zhí)行方式,釋放JIT code和entry之間存在競爭。

2.進(jìn)行suspend all,這是因?yàn)楹罄m(xù)真正開啟trace的時(shí)候,會對所有的函數(shù)入口做重新指定,必然要對整個(gè)java世界進(jìn)行停頓,保證安全性。

3.注冊listener

然后進(jìn)入EnableMethodTracing,真正發(fā)起tracing的核心流程。

9a81d5b2-e4eb-11ed-ab56-dac502259ad0.png

根據(jù)是否要回切解釋執(zhí)行,有兩種不同的處理方式。

9a927a84-e4eb-11ed-ab56-dac502259ad0.png

具體內(nèi)部流程有兩個(gè)關(guān)鍵的處理:

1.構(gòu)造一個(gè)InstallStubsClassVisitor,這個(gè)的作用是遍歷所有類,然后對每個(gè)類做執(zhí)行方法入口的重定向,也就是stub回填。

2.對各個(gè)線程的當(dāng)前棧做一下處理,主要是植入exit frame。為什么exit point要單獨(dú)處理,我們后文詳細(xì)介紹,這個(gè)地方谷歌采用了一個(gè)非常trick的方式。

9ab28e78-e4eb-11ed-ab56-dac502259ad0.png

接下來我們繼續(xù)看InstallStubsClassVisitor遍歷class替換入口的處理:

9ac50814-e4eb-11ed-ab56-dac502259ad0.png

真正的核心處理流程其實(shí)是下述:

9adc1176-e4eb-11ed-ab56-dac502259ad0.png

如果是解釋執(zhí)行方式,則把入口都換成GetQuickToInterpreterBridge

如果是stub方式,則換成了GetQuickInstrumentationEntryPoint

3.trace采集的分類

從前面的代碼流程中,我們能發(fā)現(xiàn),分成了兩個(gè)類型。

采集的方式分類

interpretor only:這是最簡單粗暴的方式,直接強(qiáng)制整個(gè)系統(tǒng)回退到解釋執(zhí)行。

stubs方式:這個(gè)方式是希望提升tracing開啟之后的性能表現(xiàn),因此在支持解釋執(zhí)行的基礎(chǔ)上,對JIT和AOT的函數(shù),也做了特殊處理進(jìn)行支持,而不需要強(qiáng)制回退到解釋執(zhí)行。相比純解釋執(zhí)行,這部分的技術(shù)細(xì)節(jié)更豐富,使用了一些“奇技淫巧”,本文后續(xù)著重介紹stub對JIT和AOT支持的方式。

trace執(zhí)行主要是在函數(shù)進(jìn)出的地方植入enter-exit對來實(shí)現(xiàn)對函數(shù)執(zhí)行流程的打點(diǎn)。

因?yàn)橐谝粋€(gè)java 方法的入口和出口植入事件的記錄,所以trace的實(shí)現(xiàn)就和虛擬機(jī)的執(zhí)行方式強(qiáng)相關(guān),我們先簡單介紹下虛擬機(jī)的幾種執(zhí)行方式。

虛擬機(jī)的執(zhí)行方式

解釋執(zhí)行:解釋執(zhí)行ART能夠全程介入java函數(shù)的執(zhí)行,這就包括了函數(shù)的入棧和出棧,因此設(shè)置觀測點(diǎn)非常容易,直接在虛擬機(jī)執(zhí)行流程中增加enter/exit埋點(diǎn)即可。

JIT:經(jīng)過JIT編譯的dex code其實(shí)target已經(jīng)是asm了,這個(gè)時(shí)候的java函數(shù)調(diào)用和arm64的native函數(shù)是非常類似的。

AOT:同JIT,區(qū)別在AOT是提前構(gòu)建而JIT是運(yùn)行時(shí)構(gòu)建的。

我們看到啟動階段的實(shí)現(xiàn),是直接插入了enter,那真正的函數(shù)入口是怎么路由處理的,這里面其實(shí)由于虛擬機(jī)設(shè)計(jì)的特殊性,直接插入wrapper有一些問題,具體的下文先補(bǔ)充一些虛擬機(jī)的相關(guān)知識,然后結(jié)合這些背景知識慢慢道來。

二、背景補(bǔ)充

要知道enter和exit的具體植入和運(yùn)行原理,我們先補(bǔ)充一點(diǎn)art虛擬機(jī)的知識。

1.java函數(shù)入口

每個(gè)java方法,在虛擬機(jī)層面都維持著一個(gè)ArtMethod數(shù)據(jù)結(jié)構(gòu),每次調(diào)用一個(gè)方法,實(shí)際上是通過ArtMethod找到真正的入口,然后進(jìn)行調(diào)用的。

java動態(tài)性的方式也是通過:

object->class->art method ->entrypoint來實(shí)現(xiàn)的

我們每次對一個(gè)對象call function,實(shí)際上就是找到對象的類型,類型里面回填了真正的artmethod,然后查找到正確的入口。

這個(gè)布局我們在看替換stub的整體流程的時(shí)候就發(fā)現(xiàn)了,替換stub就是沿著遍歷class-遍歷method的方式來完成的執(zhí)行入口重定向。

在只有一個(gè)入口可以插入的情況下,我們很容易想到做一個(gè)wrapper,在wrapper中調(diào)用art_method同時(shí)完成跟蹤:

9af2d226-e4eb-11ed-ab56-dac502259ad0.png

圖示中的stack frame 1 2 3就是對應(yīng)了我們棧上的棧幀,可以看到如果要使用wrapper方式,會在caller和真正的執(zhí)行函數(shù)之間引入一個(gè)新的wrapper棧幀,我們結(jié)合下面一個(gè)點(diǎn),就會發(fā)現(xiàn)問題。

2.walkstack

在anr,拋出異常的時(shí)候,都會對java調(diào)用棧進(jìn)行遍歷,此種遍歷的邏輯主要在walkstack中完成的,這個(gè)如果加入了wrapper,會導(dǎo)致穿透的情況變得復(fù)雜如下圖:

9b07e184-e4eb-11ed-ab56-dac502259ad0.png

這種棧結(jié)構(gòu)要兼容起來就非常的痛苦,在已有的JNI-解釋,JNI-quick,quik-quik,quik-解釋之上每種都要考慮棧內(nèi)有wrapper的場景。

總結(jié)

通過上述的虛擬機(jī)的特征有如下兩個(gè)問題:

1.art_method的入口只有一個(gè)掛載點(diǎn),JIT和AOT處理后的java函數(shù)調(diào)用方式也并不能提供exit事件的記錄時(shí)機(jī)。

2.最好不要導(dǎo)致stack結(jié)構(gòu)發(fā)生變化,否則在進(jìn)行棧遍歷的時(shí)候會帶來非常大的兼容負(fù)擔(dān)。

1和2看似是矛盾的,因?yàn)槌R?guī)的手段,只有一個(gè)函數(shù)入口的話,需要使用wrapper,但是如果使用wrapper函數(shù),棧結(jié)構(gòu)就會發(fā)生改變。這個(gè)矛盾android使用了一個(gè)非常巧妙的方法解決,我們下文就對stub的解決方法做個(gè)詳細(xì)的介紹。

三、stub技術(shù)原理探究

因?yàn)閖it和odex執(zhí)行的對象實(shí)際上都是匯編,我們在匯編中調(diào)用一個(gè)函數(shù),實(shí)際上只能insert一個(gè)entrypoint,那出棧如何實(shí)現(xiàn)呢?

此處其實(shí)就是使用了arm64的calling conversion偷雞,我們先看下替換的函數(shù)art_quick_instrumentation_entry,這個(gè)函數(shù)是純匯編寫的,我們看下匯編的核心處理:

9b1afd64-e4eb-11ed-ab56-dac502259ad0.png

匯編中使用bl指令調(diào)用了artInstrumentationMethodEntryFromCode(BL指令在函數(shù)結(jié)束后,ret會回到此處,而BR則是直接基于當(dāng)前的contexts做跳轉(zhuǎn),ret后就回到caller了),在artInstrumentationMethodEntryFromCode中主要做了三個(gè)事情

1.抓取并且查詢到了真實(shí)java函數(shù)的入口地址

9b2f6f6a-e4eb-11ed-ab56-dac502259ad0.png

2.記錄enter事件

3.記錄返回地址的PC(LR寄存器)

artInstrumentationMethodEntryFromCode通過x0把真正java方法的入口返回,然后art_quick_instrumentation_entry做了如下兩個(gè)事情:

1.把x30設(shè)置為art_quick_instrumentation_exit的入口地址(adr x30, 0x21a6a0)

2.通過BR跳轉(zhuǎn)到獲取的java方法入口(br x16)

這樣,在真正的被調(diào)函數(shù)完成之后ret,就會定向到exit的匯編上下文中:

9b4002d0-e4eb-11ed-ab56-dac502259ad0.png

在exit函數(shù)里面

1.記錄了出棧事件

2.還原了caller PC

通過改寫棧上位置(str x0, [sp, #504]),然后restore的時(shí)候(ldp x29, x30, [sp, #496]),就自然讀到目標(biāo)lr了,同時(shí)這樣不會有寄存器污染的問題

還原lr之后,直接使用br指令跳轉(zhuǎn)到caller原始的位置。

以上就是android利用arm callingconversion實(shí)現(xiàn)的exit植入。

總結(jié)

如下圖所示,android通過篡改調(diào)用前的lr,結(jié)合BL和BR指令的不同ret方式,完成了單入口,在破壞棧結(jié)構(gòu)的情況下,記錄了enter和exit事件對。

9b56f526-e4eb-11ed-ab56-dac502259ad0.png

四、安卓最新的演進(jìn)

1.演進(jìn)概述

因?yàn)閺?fù)雜度和對jit的沖突,導(dǎo)致了不太好

目前谷歌在最新的安卓版本做出了重大的更新:

1.關(guān)閉了對odex的支持

2.在jit code生成的時(shí)候,如果開啟了tracing,會生成出帶有enter和exit的code,直接在code gen層面支持。

3.對于stub方式,不做全量的替換,使能trace的時(shí)候整個(gè)系統(tǒng)回退到解釋執(zhí)行,然后清理jit cache,新的jit函數(shù)會直接生成帶有enter和exit的code

2.谷歌最新變更相關(guān)合入:

1.jit code中直接生成enter/exit hook調(diào)用

https://cs.android.com/android/_/android/platform/art/+/5097f83c4719a76fdfab1044ab745273841aca45

2.instrument替換掉trace odex的支持

https://cs.android.com/android/_/android/platform/art/+/890b19bd625be5d0e4a876e3eb11b8b893fb0c13

相關(guān)引用

method trace概述/舉例:https://juejin.cn/post/7107137302043820039

谷歌method trace介紹:https://developer.android.com/studio/profile/generate-trace-logs?hl=zh-cn

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 谷歌
    +關(guān)注

    關(guān)注

    27

    文章

    6168

    瀏覽量

    105393
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4331

    瀏覽量

    62622
  • Method
    +關(guān)注

    關(guān)注

    0

    文章

    9

    瀏覽量

    7266
  • 虛擬機(jī)
    +關(guān)注

    關(guān)注

    1

    文章

    917

    瀏覽量

    28202
  • ART
    ART
    +關(guān)注

    關(guān)注

    0

    文章

    26

    瀏覽量

    10486

原文標(biāo)題:ART虛擬機(jī)method tracing技術(shù)解析

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    什么是虛擬機(jī)?虛擬機(jī)真的那么好用嗎?

    在日新月異的科技世界中,虛擬技術(shù)如同一座橋梁,連接著現(xiàn)實(shí)與數(shù)字的鴻溝,為我們打開了全新的計(jì)算維度。虛擬機(jī),這一概念,自其誕生以來,就以其獨(dú)特的魅力和強(qiáng)大的功能,深深地影響了軟件開發(fā)、系統(tǒng)測試和云
    的頭像 發(fā)表于 07-06 08:05 ?463次閱讀
    什么是<b class='flag-5'>虛擬機(jī)</b>?<b class='flag-5'>虛擬機(jī)</b>真的那么好用嗎?

    有關(guān)虛擬機(jī)虛擬技術(shù)的幾點(diǎn)詮注

    虛擬機(jī)虛擬技術(shù)給計(jì)算機(jī)應(yīng)用注入了新的研究與開發(fā)點(diǎn),同時(shí)也存在諸多不利因素。本文綜述了虛擬機(jī)虛擬
    發(fā)表于 06-22 18:04 ?36次下載

    虛擬機(jī)虛擬技術(shù)

    虛擬機(jī)虛擬技術(shù)給計(jì)算機(jī)應(yīng)用注入了新的研究與開發(fā)點(diǎn),同時(shí)也存在諸多不利因素。本文綜述了虛擬機(jī)虛擬
    發(fā)表于 09-07 10:15 ?13次下載

    基于虛擬機(jī)技術(shù)的DSC仿真系統(tǒng)設(shè)計(jì)

    提出了基于虛擬機(jī)技術(shù)的DCS仿真系統(tǒng)的實(shí)現(xiàn)方式,描述了虛擬控制器的具體實(shí)現(xiàn)方法及虛擬機(jī)技術(shù)的其他應(yīng)用。
    發(fā)表于 12-03 17:26 ?26次下載
    基于<b class='flag-5'>虛擬機(jī)</b><b class='flag-5'>技術(shù)</b>的DSC仿真系統(tǒng)設(shè)計(jì)

    基于虛擬機(jī)技術(shù)的DCS仿真系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    提出了基于虛擬機(jī)技術(shù)的DCS仿真系統(tǒng)的實(shí)現(xiàn)方式,描述了虛擬控制器的具體實(shí)現(xiàn)方法及虛擬機(jī)技術(shù)的其他應(yīng)用。
    發(fā)表于 01-16 15:04 ?2182次閱讀
    基于<b class='flag-5'>虛擬機(jī)</b><b class='flag-5'>技術(shù)</b>的DCS仿真系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    什么是區(qū)塊鏈虛擬機(jī)和普通虛擬機(jī)有啥區(qū)別

    區(qū)塊鏈技術(shù)領(lǐng)域基礎(chǔ)設(shè)施——虛擬機(jī),是實(shí)現(xiàn)智能合約系統(tǒng)最為關(guān)鍵和核心的技術(shù)。智能合約不僅是業(yè)務(wù)邏輯的載體,同時(shí)又扎扎實(shí)實(shí)地落在了技術(shù)實(shí)現(xiàn)的層面。由此可見,
    發(fā)表于 03-04 10:50 ?4957次閱讀

    虛擬機(jī):QEMU虛擬機(jī)和主機(jī)無線網(wǎng)絡(luò)通訊設(shè)置

    虛擬機(jī):QEMU虛擬機(jī)和主機(jī)無線網(wǎng)絡(luò)通訊設(shè)置
    的頭像 發(fā)表于 06-22 10:19 ?5452次閱讀
    <b class='flag-5'>虛擬機(jī)</b>:QEMU<b class='flag-5'>虛擬機(jī)</b>和主機(jī)無線網(wǎng)絡(luò)通訊設(shè)置

    KVM虛擬機(jī)管理和基本使用

    KVM — 全稱是基于內(nèi)核的虛擬機(jī)(Kernel-based Virtual Machine)是一個(gè)開源軟件,基于內(nèi)核的虛擬技術(shù),實(shí)際是嵌入系統(tǒng)的一個(gè)虛擬化模塊,通過優(yōu)化內(nèi)核來使用
    的頭像 發(fā)表于 02-07 09:20 ?1297次閱讀

    虛擬機(jī)技術(shù)合集1

    惡意代碼編寫者經(jīng)常使用反虛擬機(jī)技術(shù)逃避分析,這種技術(shù)可以檢測自己是否運(yùn)行在虛擬機(jī)中。如果惡意代碼探測到自己在虛擬機(jī)中運(yùn)行,它會執(zhí)行與其本身行
    的頭像 發(fā)表于 02-14 13:45 ?1268次閱讀

    虛擬機(jī)技術(shù)合集2

    惡意代碼編寫者經(jīng)常使用反虛擬機(jī)技術(shù)逃避分析,這種技術(shù)可以檢測自己是否運(yùn)行在虛擬機(jī)中。如果惡意代碼探測到自己在虛擬機(jī)中運(yùn)行,它會執(zhí)行與其本身行
    的頭像 發(fā)表于 02-14 13:45 ?685次閱讀
    反<b class='flag-5'>虛擬機(jī)</b><b class='flag-5'>技術(shù)</b>合集2

    虛擬機(jī)技術(shù)合集3

    惡意代碼編寫者經(jīng)常使用反虛擬機(jī)技術(shù)逃避分析,這種技術(shù)可以檢測自己是否運(yùn)行在虛擬機(jī)中。如果惡意代碼探測到自己在虛擬機(jī)中運(yùn)行,它會執(zhí)行與其本身行
    的頭像 發(fā)表于 02-14 13:45 ?654次閱讀
    反<b class='flag-5'>虛擬機(jī)</b><b class='flag-5'>技術(shù)</b>合集3

    虛擬機(jī)技術(shù)合集4

    惡意代碼編寫者經(jīng)常使用反虛擬機(jī)技術(shù)逃避分析,這種技術(shù)可以檢測自己是否運(yùn)行在虛擬機(jī)中。如果惡意代碼探測到自己在虛擬機(jī)中運(yùn)行,它會執(zhí)行與其本身行
    的頭像 發(fā)表于 02-14 13:46 ?1032次閱讀
    反<b class='flag-5'>虛擬機(jī)</b><b class='flag-5'>技術(shù)</b>合集4

    Docker與虛擬機(jī)的區(qū)別

    Docker和虛擬機(jī)是兩種不同的虛擬技術(shù),它們在實(shí)現(xiàn)方式、資源消耗、運(yùn)行性能等方面存在許多差異。本文將會詳細(xì)介紹它們的區(qū)別。 一、實(shí)現(xiàn)方式 1.1 虛擬機(jī)
    的頭像 發(fā)表于 11-23 09:37 ?9796次閱讀

    怎么安裝linux虛擬機(jī)

    在計(jì)算機(jī)領(lǐng)域,虛擬機(jī)是一種軟件程序,它允許在主操作系統(tǒng)上運(yùn)行多個(gè)虛擬操作系統(tǒng)。Linux虛擬機(jī)在開發(fā)、測試和學(xué)習(xí)等環(huán)境中得到廣泛應(yīng)用。本文將詳細(xì)介紹如何安裝Linux虛擬機(jī),并提供一個(gè)
    的頭像 發(fā)表于 11-23 10:50 ?1109次閱讀

    虛擬機(jī)ubuntu怎么聯(lián)網(wǎng)

    虛擬機(jī)ubuntu怎么聯(lián)網(wǎng)? 虛擬機(jī)(Virtual Machine)是運(yùn)行在物理機(jī)(Host Machine)上的虛擬操作系統(tǒng)環(huán)境。在虛擬機(jī)
    的頭像 發(fā)表于 12-27 16:51 ?983次閱讀