如何用AndroidStudio动态调试ART虚拟机?

1.前言

之前有debug过jvm, 开发anroid 临时起意想能不能动态debug art虚拟机,代码是看不下去的,还是跑起来直观。其实调试其他native lib 的原理是一样的。网上也看到一些,受启发比较大的是这位大佬17年写的https://zhuanlan.zhihu.com/p/24867284, 在此先感谢一下他,自己也算是做一个较的,记录一下自己踩过的坑。以下全部基于Android 9 。

2. 编译Android 9 系统

这个有门槛,毕竟AOSP 超大。有个在64核服务器当然好,大概半个小时,全目录60GB左右,但是自己笔记本还是吃不消的,怎么拉下自己编译,还请google。如果只是想要原生这里有https://ci.android.com/,AOSP google编译的,可以在里面直接下载AOSP编译文件,也比较大,且需要联网。如果你还有个pixel, 还可以用来刷机。

  • 自己有条件,可以编译
  • 从AOSP下载
  • 用别人给的,比如我android_framework_native_debug
  • 不管怎么,当然要有目标库的源码最好,(哦,你不要的的话,😂 可以直接指令级debug,其实可以不用这么麻烦了,可以直接return )

3.准备一台手机

  • 可以刷机,那就刷自己编译的系统
  • 不能刷机 ,但可以root,看你是需要调试那个库,如果是其它库,你可能需要自己把编译好的so库push到手机
  • 不能root,同用的基础库才可以,比如本次我尝试的art库,libart.so 在2台手机 root和非root,刷机和不刷机的情况下都是可以的。想想厂商还是很少改动art的

4. 新建一个AS工程

我们需要

  • 编译出来好的 目标so文件

  • AOSP 工程里面源码

  • 编译出来的symbol 目录,里面对应的就是相应库的 debug标记文件,调试中需要用到。找到对应的libart.so。对比大小你就会发现,一个是7mb,一个是150mb。拷贝出来,可以放到工程目录下


    当然也可以用你现在的工程,只要是方便debug,查看源码,进行native debug。可以先导入 art 源码到工程里面,可以参考android_framework_native_debug样式。主要是通过AS里面的debug attach 到选择的进程,就可以进行相应的进程debug了。

    5.将目标库PUSH到手机

    当然也可以不用,比如我在另外一台其它品牌不能root的手机上测试,art库可以直接使用。如果你其它库,建议可以尝试。

6.Attach到目标进程Debug

本[android_framework_native_debug](https://github.com/kaiattrib/android_framework_native_debug)工程先写了一个native code ,方便打上断掉,寻找入库,你也可以不用,直接pause也是可以的。<br />选择带native,可以是dual,等待lldb连接成功。<br /> ![](https://cdn.nlark.com/yuque/0/2020/png/501385/1589559645183-3d9e2d39-a854-4685-9621-b12d9ac860d2.png#align=left&display=inline&height=643&margin=%5Bobject%20Object%5D&name=Xnip2020-05-16_00-20-07.png&originHeight=1134&originWidth=772&size=112011&status=done&style=none&width=438)
  • 等待lldb连接成功,点击触发

  • 但是现在看到,栈还没有行号信息,我们自己写的native代码,才有


  • 1
    2
    3
    4
    5
    6
    ::Java_com_test_CustomizeThread_start(JNIEnv , jobject) native-lib.cpp:38 //这里有行号,
    art_quick_generic_jni_trampoline 0x0000006f4bb9b9e4 //这里只有地址
    art_quick_invoke_stub 0x0000006f4bb9298c
    art::ArtMethod::Invoke(art::Thread, unsigned int, unsigned int, art::JValue, char const) 0x0000006f4b70c6cc
    art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread, art::ArtMethod, art::ShadowFrame, unsigned short, art::JValue) 0x0000006f4b8bcab8
    bool art::interpreter::DoCall<false, false>(art::ArtMethod, art::Thread, art::ShadowFrame&,
  • 我们继续进入,force step into

    1
    2
    3
    4
    5
    6
    art::(anonymous namespace)::CheckJNI::FindClass(_JNIEnv*, char const*) 0x0000006f4b71e728
    _JNIEnv::FindClass(char const*) jni.h:504
    ::Java_com_test_CustomizeThread_start(JNIEnv *, jobject) native-lib.cpp:38
    art_quick_generic_jni_trampoline 0x0000006f4bb9b9e4
    art_quick_invoke_stub 0x0000006f4bb9298c
    ---
  • 可以看到,又没有源码信息了,接下来我们手动加入符号信息库

  • 通过 d -p 反编译,可以知道我们当前pc所在的库和函数

    1
    2
    3
    4
    5
    6
    (lldb) d -p
    libart.so`art::(anonymous namespace)::CheckJNI::FindClass:
    -> 0x6f4b71e728 <+4>: stp x28, x27, [sp, #0x70]
    0x6f4b71e72c <+8>: stp x26, x25, [sp, #0x80]
    0x6f4b71e730 <+12>: stp x24, x23, [sp, #0x90]
    0x6f4b71e734 <+16>: stp x22, x21, [sp, #0xa0]
  • 手动加入符号信息库, 有下面信息,说明ok

  • 报错就是目标so不符合

    1
    2
    (lldb) add-dsym /Users/kenchan/GitHub/android_framework_native_debug/symbol/libart.so
    symbol file '/Users/kenchan/GitHub/android_framework_native_debug/symbol/libart.so' has been added to '/Users/kenchan/.lldb/module_cache/remote-android/.cache/88B6A7B4-8B23-9260-2988-33B24671013C/libart.so'
  • 通过souce info 查看一下 对应的源码信息

    1
    2
    3
    (lldb) source info
    Lines found in module `libart.so
    [0x0000006f4b71e714-0x0000006f4b71e750): art/runtime/scoped_thread_state_change-inl.h:38:5
  • 可以看到目录信息,这里linux下是绝对路径。所以我们要做一下映射

    1
    (lldb) settings set target.source-map art/ /Users/kenchan/GitHub/android_framework_native_debug/art
  • 你再点击一下 setp,就会发现调用栈里面有 源码文件信息了

  • 我们点击某一个调用栈,就是发现 跳转了源码文件了

  • 现在的调用栈是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<<<std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*) 0x0000006f4b71e750
    art::ScopedThreadStateChange::ScopedThreadStateChange(art::Thread*, art::ThreadState) scoped_thread_state_change-inl.h:38
    art::ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(_JNIEnv*) scoped_thread_state_change-inl.h:108
    art::ScopedObjectAccess::ScopedObjectAccess(_JNIEnv*) scoped_thread_state_change-inl.h:119
    art::(anonymous namespace)::CheckJNI::DefineClass(_JNIEnv*, char const*, _jobject*, signed char const*, int) check_jni.cc:1824
    _JNIEnv::FindClass(char const*) jni.h:504
    ::Java_com_test_CustomizeThread_start(JNIEnv *, jobject) native-lib.cpp:38
    art_quick_alloc_array_resolved32_tlab 0x0000006f4bb9b9e4
    art_quick_throw_string_bounds 0x0000006f4bb9298c


接下来就是正常debug了

7. 你可以用本android_framework_native_debug工程做什么?

  • 如果你的手机也是 anroid 9.0
  • 如果你也是想debug art
  • 你可以直接下载运行本工程,按步骤即可,不需要root,不需要push
  • 我编译的是arm平台的,所以在模拟器里面不行

    参考

    https://zhuanlan.zhihu.com/p/24867284