0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

ART虚拟机method tracing技术解析

Linux阅码场 来源:内核工匠 2023-04-28 09:37 次阅读

一、method tracing介绍

概述

这个是谷歌提供的对java的函数级trace工具,和systrace只支持打点不同,method tracing能支持到函数,看到具体的函数执行时间,准确的分析出来执行的时间短板。

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

1.生成trace的方式

sampling方式:

sampling方式采用sample任务,定期抓取各个线程的调用栈,采集精度和采集的频次正相关,同时由于java stack采集的时候需要做suspend,因此还是有一部分的效率损失。

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

我们可以看到,原生单次采集使用的是suspendall,而不是对threadlist上的线程逐个做getStackTrace,因此效率损失会比较严重。

trace方式:

通过在执行流程插入enter-exit来观测:

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

相比于sample 方式,trace可以准确的获取到每个函数的进入和退出时间,精度可以非常高。

由于art虚拟机执行特点,这个方案相较于sample方式复杂度要高不少,下文会着重介绍trace方式的实现原理

2.trace启动流程

我们从trace方式的启动入口开始看起

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

几个关键的流程分别是

1.停用掉JIT GC,这个是防止stub方式替换之后,因为JIT GC引起的重新指定执行方式,释放JIT code和entry之间存在竞争。

2.进行suspend all,这是因为后续真正开启trace的时候,会对所有的函数入口做重新指定,必然要对整个java世界进行停顿,保证安全性。

3.注册listener

然后进入EnableMethodTracing,真正发起tracing的核心流程。

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

根据是否要回切解释执行,有两种不同的处理方式。

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

具体内部流程有两个关键的处理:

1.构造一个InstallStubsClassVisitor,这个的作用是遍历所有类,然后对每个类做执行方法入口的重定向,也就是stub回填。

2.对各个线程的当前栈做一下处理,主要是植入exit frame。为什么exit point要单独处理,我们后文详细介绍,这个地方谷歌采用了一个非常trick的方式。

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

接下来我们继续看InstallStubsClassVisitor遍历class替换入口的处理:

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

真正的核心处理流程其实是下述:

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

如果是解释执行方式,则把入口都换成GetQuickToInterpreterBridge

如果是stub方式,则换成了GetQuickInstrumentationEntryPoint

3.trace采集的分类

从前面的代码流程中,我们能发现,分成了两个类型。

采集的方式分类

interpretor only:这是最简单粗暴的方式,直接强制整个系统回退到解释执行。

stubs方式:这个方式是希望提升tracing开启之后的性能表现,因此在支持解释执行的基础上,对JIT和AOT的函数,也做了特殊处理进行支持,而不需要强制回退到解释执行。相比纯解释执行,这部分的技术细节更丰富,使用了一些“奇技淫巧”,本文后续着重介绍stub对JIT和AOT支持的方式。

trace执行主要是在函数进出的地方植入enter-exit对来实现对函数执行流程的打点。

因为要在一个java 方法的入口和出口植入事件的记录,所以trace的实现就和虚拟机的执行方式强相关,我们先简单介绍下虚拟机的几种执行方式。

虚拟机的执行方式

解释执行:解释执行ART能够全程介入java函数的执行,这就包括了函数的入栈和出栈,因此设置观测点非常容易,直接在虚拟机执行流程中增加enter/exit埋点即可。

JIT:经过JIT编译的dex code其实target已经是asm了,这个时候的java函数调用和arm64的native函数是非常类似的。

AOT:同JIT,区别在AOT是提前构建而JIT是运行时构建的。

我们看到启动阶段的实现,是直接插入了enter,那真正的函数入口是怎么路由处理的,这里面其实由于虚拟机设计的特殊性,直接插入wrapper有一些问题,具体的下文先补充一些虚拟机的相关知识,然后结合这些背景知识慢慢道来。

二、背景补充

要知道enter和exit的具体植入和运行原理,我们先补充一点art虚拟机的知识。

1.java函数入口

每个java方法,在虚拟机层面都维持着一个ArtMethod数据结构,每次调用一个方法,实际上是通过ArtMethod找到真正的入口,然后进行调用的。

java动态性的方式也是通过:

object->class->art method ->entrypoint来实现的

我们每次对一个对象call function,实际上就是找到对象的类型,类型里面回填了真正的artmethod,然后查找到正确的入口。

这个布局我们在看替换stub的整体流程的时候就发现了,替换stub就是沿着遍历class-遍历method的方式来完成的执行入口重定向。

在只有一个入口可以插入的情况下,我们很容易想到做一个wrapper,在wrapper中调用art_method同时完成跟踪:

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

图示中的stack frame 1 2 3就是对应了我们栈上的栈帧,可以看到如果要使用wrapper方式,会在caller和真正的执行函数之间引入一个新的wrapper栈帧,我们结合下面一个点,就会发现问题。

2.walkstack

在anr,抛出异常的时候,都会对java调用栈进行遍历,此种遍历的逻辑主要在walkstack中完成的,这个如果加入了wrapper,会导致穿透的情况变得复杂如下图:

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

这种栈结构要兼容起来就非常的痛苦,在已有的JNI-解释,JNI-quick,quik-quik,quik-解释之上每种都要考虑栈内有wrapper的场景。

总结

通过上述的虚拟机的特征有如下两个问题:

1.art_method的入口只有一个挂载点,JIT和AOT处理后的java函数调用方式也并不能提供exit事件的记录时机。

2.最好不要导致stack结构发生变化,否则在进行栈遍历的时候会带来非常大的兼容负担。

1和2看似是矛盾的,因为常规的手段,只有一个函数入口的话,需要使用wrapper,但是如果使用wrapper函数,栈结构就会发生改变。这个矛盾android使用了一个非常巧妙的方法解决,我们下文就对stub的解决方法做个详细的介绍。

三、stub技术原理探究

因为jit和odex执行的对象实际上都是汇编,我们在汇编中调用一个函数,实际上只能insert一个entrypoint,那出栈如何实现呢?

此处其实就是使用了arm64的calling conversion偷鸡,我们先看下替换的函数art_quick_instrumentation_entry,这个函数是纯汇编写的,我们看下汇编的核心处理:

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

汇编中使用bl指令调用了artInstrumentationMethodEntryFromCode(BL指令在函数结束后,ret会回到此处,而BR则是直接基于当前的contexts做跳转,ret后就回到caller了),在artInstrumentationMethodEntryFromCode中主要做了三个事情

1.抓取并且查询到了真实java函数的入口地址

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

2.记录enter事件

3.记录返回地址的PC(LR寄存器)

artInstrumentationMethodEntryFromCode通过x0把真正java方法的入口返回,然后art_quick_instrumentation_entry做了如下两个事情:

1.把x30设置为art_quick_instrumentation_exit的入口地址(adr x30, 0x21a6a0)

2.通过BR跳转到获取的java方法入口(br x16)

这样,在真正的被调函数完成之后ret,就会定向到exit的汇编上下文中:

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

在exit函数里面

1.记录了出栈事件

2.还原了caller PC

通过改写栈上位置(str x0, [sp, #504]),然后restore的时候(ldp x29, x30, [sp, #496]),就自然读到目标lr了,同时这样不会有寄存器污染的问题

还原lr之后,直接使用br指令跳转到caller原始的位置。

以上就是android利用arm callingconversion实现的exit植入。

总结

如下图所示,android通过篡改调用前的lr,结合BL和BR指令的不同ret方式,完成了单入口,在破坏栈结构的情况下,记录了enter和exit事件对。

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

四、安卓最新的演进

1.演进概述

因为复杂度和对jit的冲突,导致了不太好

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

1.关闭了对odex的支持

2.在jit code生成的时候,如果开启了tracing,会生成出带有enter和exit的code,直接在code gen层面支持。

3.对于stub方式,不做全量的替换,使能trace的时候整个系统回退到解释执行,然后清理jit cache,新的jit函数会直接生成带有enter和exit的code

2.谷歌最新变更相关合入:

1.jit code中直接生成enter/exit hook调用

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

2.instrument替换掉trace odex的支持

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

相关引用

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

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

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 谷歌
    +关注

    关注

    27

    文章

    6139

    浏览量

    105059
  • 函数
    +关注

    关注

    3

    文章

    4303

    浏览量

    62411
  • Method
    +关注

    关注

    0

    文章

    9

    浏览量

    7247
  • 虚拟机
    +关注

    关注

    1

    文章

    908

    浏览量

    28064
  • ART
    ART
    +关注

    关注

    0

    文章

    26

    浏览量

    10474

原文标题:ART虚拟机method tracing技术解析

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    什么是虚拟机虚拟机真的那么好用吗?

    在日新月异的科技世界中,虚拟技术如同一座桥梁,连接着现实与数字的鸿沟,为我们打开了全新的计算维度。虚拟机,这一概念,自其诞生以来,就以其独特的魅力和强大的功能,深深地影响了软件开发、系统测试和云
    的头像 发表于 07-06 08:05 463次阅读
    什么是<b class='flag-5'>虚拟机</b>?<b class='flag-5'>虚拟机</b>真的那么好用吗?

    有关虚拟机虚拟技术的几点诠注

    虚拟机虚拟技术给计算机应用注入了新的研究与开发点,同时也存在诸多不利因素。本文综述了虚拟机虚拟
    发表于 06-22 18:04 36次下载

    虚拟机虚拟技术

    虚拟机虚拟技术给计算机应用注入了新的研究与开发点,同时也存在诸多不利因素。本文综述了虚拟机虚拟
    发表于 09-07 10:15 13次下载

    基于虚拟机技术的DSC仿真系统设计

    提出了基于虚拟机技术的DCS仿真系统的实现方式,描述了虚拟控制器的具体实现方法及虚拟机技术的其他应用。
    发表于 12-03 17:26 26次下载
    基于<b class='flag-5'>虚拟机</b><b class='flag-5'>技术</b>的DSC仿真系统设计

    基于虚拟机技术的DCS仿真系统设计与实现

    提出了基于虚拟机技术的DCS仿真系统的实现方式,描述了虚拟控制器的具体实现方法及虚拟机技术的其他应用。
    发表于 01-16 15:04 2146次阅读
    基于<b class='flag-5'>虚拟机</b><b class='flag-5'>技术</b>的DCS仿真系统设计与实现

    什么是区块链虚拟机和普通虚拟机有啥区别

    区块链技术领域基础设施——虚拟机,是实现智能合约系统最为关键和核心的技术。智能合约不仅是业务逻辑的载体,同时又扎扎实实地落在了技术实现的层面。由此可见,
    发表于 03-04 10:50 4935次阅读

    虚拟机:QEMU虚拟机和主机无线网络通讯设置

    虚拟机:QEMU虚拟机和主机无线网络通讯设置
    的头像 发表于 06-22 10:19 5385次阅读
    <b class='flag-5'>虚拟机</b>:QEMU<b class='flag-5'>虚拟机</b>和主机无线网络通讯设置

    KVM虚拟机管理和基本使用

    KVM — 全称是基于内核的虚拟机(Kernel-based Virtual Machine)是一个开源软件,基于内核的虚拟技术,实际是嵌入系统的一个虚拟化模块,通过优化内核来使用
    的头像 发表于 02-07 09:20 1249次阅读

    虚拟机技术合集1

    恶意代码编写者经常使用反虚拟机技术逃避分析,这种技术可以检测自己是否运行在虚拟机中。如果恶意代码探测到自己在虚拟机中运行,它会执行与其本身行
    的头像 发表于 02-14 13:45 1236次阅读

    虚拟机技术合集2

    恶意代码编写者经常使用反虚拟机技术逃避分析,这种技术可以检测自己是否运行在虚拟机中。如果恶意代码探测到自己在虚拟机中运行,它会执行与其本身行
    的头像 发表于 02-14 13:45 655次阅读
    反<b class='flag-5'>虚拟机</b><b class='flag-5'>技术</b>合集2

    虚拟机技术合集3

    恶意代码编写者经常使用反虚拟机技术逃避分析,这种技术可以检测自己是否运行在虚拟机中。如果恶意代码探测到自己在虚拟机中运行,它会执行与其本身行
    的头像 发表于 02-14 13:45 627次阅读
    反<b class='flag-5'>虚拟机</b><b class='flag-5'>技术</b>合集3

    虚拟机技术合集4

    恶意代码编写者经常使用反虚拟机技术逃避分析,这种技术可以检测自己是否运行在虚拟机中。如果恶意代码探测到自己在虚拟机中运行,它会执行与其本身行
    的头像 发表于 02-14 13:46 1009次阅读
    反<b class='flag-5'>虚拟机</b><b class='flag-5'>技术</b>合集4

    Docker与虚拟机的区别

    Docker和虚拟机是两种不同的虚拟技术,它们在实现方式、资源消耗、运行性能等方面存在许多差异。本文将会详细介绍它们的区别。 一、实现方式 1.1 虚拟机
    的头像 发表于 11-23 09:37 9511次阅读

    怎么安装linux虚拟机

    在计算机领域,虚拟机是一种软件程序,它允许在主操作系统上运行多个虚拟操作系统。Linux虚拟机在开发、测试和学习等环境中得到广泛应用。本文将详细介绍如何安装Linux虚拟机,并提供一个
    的头像 发表于 11-23 10:50 1056次阅读

    虚拟机ubuntu怎么联网

    虚拟机ubuntu怎么联网  虚拟机(Virtual Machine)是运行在物理(Host Machine)上的虚拟操作系统环境。在虚拟机
    的头像 发表于 12-27 16:51 940次阅读