Android如何查找java代码编译路径

  • 1. 源文件代码位置
  • 2. 查找文件编译后存放的地方
  • 3. 找一下这个文件在哪里
  • 4. 看一下实际效果


1. 源文件代码位置

如果有code search,那么直接使用code search,
如果没有可以使用locate "***.java"来查找对应的文件

举一个例子想要调试FinalizerDaemon这个class,先“frameworks/base”搜索一下
在frameworks/base/config/preloaded-classes可以找到类似定义

java.lang.Daemons$FinalizerDaemon

那么我们其实应该去找Daemons.java在哪里
locate “Daemons.java”
可以找到源码位置

libcore/libart/src/main/java/java/lang/Daemons.java

不过直接修改这个文件发现修改不生效,那么很大可能就是没有编译进去,接下去需要看一下这个文件编译在哪里

2. 查找文件编译后存放的地方

1、libcore/libart/src/main/java/java/lang/Daemons.java
逐级往上查找Android.mk、Android.bp

如这里最近的就是libcore\Android.bp

2、在Android.bp的目录libcore, 先搜索一下Daemons.java或者libcore
find -name “*.bp” |xargs grep “Daemons”

./non_openjdk_java_files.bp: “libart/src/main/java/java/lang/Daemons.java”,

libcore$ find -name “*.bp” |xargs grep “libart”

./non_openjdk_java_files.bp: path: “libart/src/main”,

上面2中都是可以将Daemons.java引用进来

3、打开搜索到的文件libcore/non_openjdk_java_files.bp

//文件组
 filegroup {
 //名字
 name: “non_openjdk_javadoc_libart_files”,
 //源文件,如frameworks/base/core/java/Android.bp中的"**/*.java"代表将frameworks/base/core/java后面的所有java文件做为资源,包括Activity.java
 srcs: [
 //…
 “libart/src/main/java/java/lang/Daemons.java”,
 //…
 ],
 //路径,从这里开始引入源码
 path: “libart/src/main”,
 //私有的,这个文件组只有libcore本包可以访问,其它子包Android.bp访问不了
 visibility: ["//visibility:private"],
 }4、non_openjdk_javadoc_libart_files使用的地方是non_openjdk_javadoc_files,还是文件组,不是生成文件的地方
filegroup {
 name: “non_openjdk_javadoc_files”,
 srcs: [
 //…
 “:non_openjdk_javadoc_libart_files”,
 //…
 ],
 visibility: ["//visibility:private"],
 }5、接着继续找non_openjdk_javadoc_files使用的地方,是core_libart_api_files/non_openjdk_java_files文件组
 libcore$ find -name “*.bp” |xargs grep “non_openjdk_javadoc_files”
 ./JavaLibrary.bp: “:non_openjdk_javadoc_files”,
 ./non_openjdk_java_files.bp: name: “non_openjdk_javadoc_files”,
 ./non_openjdk_java_files.bp: “:non_openjdk_javadoc_files”,//JavaLibrary.bp
 filegroup {
 name: “core_libart_api_files”,
 srcs: [
 “:non_openjdk_javadoc_files”,
 ],
 }//non_openjdk_java_files.bp
 filegroup {
 name: “non_openjdk_java_files”,
 visibility: [
 “//frameworks/base”,
 ],
 srcs: [
 //…
 “:non_openjdk_javadoc_files”,
 ],
 }

6、core_libart_api_files看起来都是用在接口的地方,没有包含实际运行的逻辑

//被java_sdk_library引用,一般是用于接口
 java_sdk_library {
 name: “art.module.public.api”,
 //…
 srcs: [
 //…
 “:core_libart_api_files”,
 //…
 ],
 //…
 }//引用在droidstubs,用于生成接口
 droidstubs {
 name: “java-current-stubs-source”,
 //…
 srcs: [
 //…
 “:core_libart_api_files”,
 //…
 ],
 //…
 }//art_module_api_files是filegroup文件组,还是要继续找
 filegroup {
 name: “art_module_api_files”,
 //…
 srcs: [
 //…
 “:core_libart_api_files”,
 //…
 ],
 //…
 }//art_module_api_files引用在droidstubs,用于生成接口
 droidstubs {
 name: “art-module-public-api-stubs-nullability-validation”,
 srcs: [":art_module_api_files"],
 //…
 }7、non_openjdk_java_files被core_libart_java_files引用,也是文件组
 filegroup {
 name: “core_libart_java_files”,
 //可以被子包应用,如果libcore子目录有Android.bp子包,也是可以用这个filegroup的
 visibility: [
 “//libcore:subpackages”,
 ],
 srcs: [
 “:non_openjdk_java_files”,
 ],
 }

8、core_libart_java_files被core-libart引用,这个是一个java_library(java的库,如.jar)是可以生成文件的看起来像是了

java_library {
 name: “core-libart”,
 visibility: [
 “//art/build/apex”,
 “//art/build/sdk”,
 “//external/wycheproof”,
 “//libcore/benchmarks”,
 “//packages/modules/ArtPrebuilt”,
 ],
 //…
 srcs: [":core_libart_java_files"],
 //…
 }

3. 找一下这个文件在哪里

1、可以搜索生成文件
$ locate “core-libart.jar”
ArtPrebuilt/com.google.android.art.deapexer/android_common/deapexer/javalib/core-libart.jar
dex_artjars_input/core-libart.jar

到这里可以看到这个是在art

那么大概原因应该已经知道了,应该是mainline导致了代码没有生效

手机apex里面也可以找到这个jar包
apex # find . -name “core-libart
./com.android.art/javalib/core-libart.jar
./com.android.art@310924000/javalib/core-libart.jar

2、接着去源码art目录看一下是如何使用这个jar的
//art/build/apex/Android.bp

//放在libcore_java_libs列表里面
// Core Java libraries.
libcore_java_libs = [
“core-oj”,
“core-libart”,
“okhttp”,
“bouncycastle”,
“apache-xml”,
]

//host主机端,也不是终端里面,不是这个
art_apex_test_host {
name: “com.android.art.host”,
//…
java_libs: libcore_java_libs,
//…
]

//这个是defaults,还有别的地方引用
apex_defaults {
name: “com.android.art-device-defaults”,
defaults: [“com.android.art-defaults”],
//…
java_libs: libcore_java_libs,
//…
]

//这个就是给mainline了的
art_apex {
name: “com.android.art”,
defaults: [“com.android.art-device-defaults”],
certificate: “:com.android.art.certificate”,
}

3、那么去掉mainline重新编译应该是生效的

diff --git a/build/mainline_modules_s.mk b/build/mainline_modules_s.mk
--- a/build/mainline_modules_s.mk
+++ b/build/mainline_modules_s.mk

+# delete by yunhen
 # Art
-MAINLINE_COMPRESS_APEX_ART ?= true
-ifeq ($(MAINLINE_COMPRESS_APEX_ART),true)
-PRODUCT_PACKAGES += \
-    com.google.android.art_compressed
-else
-PRODUCT_PACKAGES += \
-    com.google.android.art
-endif
+#MAINLINE_COMPRESS_APEX_ART ?= true
+#ifeq ($(MAINLINE_COMPRESS_APEX_ART),true)
+#PRODUCT_PACKAGES += \
+#    com.google.android.art_compressed
+#else
+#PRODUCT_PACKAGES += \
+#    com.google.android.art
+#endif

4. 看一下实际效果

  1. 加入日志,如在FinalizerDaemon的doFinalize加入一个日志"FinalizerDaemon doFinalize yunhen"
    //libcore/libart/src/main/java/java/lang/Daemons.java
    private static class FinalizerDaemon extends Daemon {
    //…
    private void doFinalize(FinalizerReference<?> reference) {
    System.logE(“FinalizerDaemon doFinalize yunhen”);
    //…
    }
    }
  2. 找到手机的文件
    /apex # find . -name “core-libart*”
    ./com.android.art/javalib/core-libart.jar
    ./com.android.art@319999900/javalib/core-libart.jar
  3. pull出来adb pull /apex/com.android.art/javalib/core-libart.jar core-libart_new.jar
  4. 转换成smali文件
    java -jar apktool_2.3.0.jar d core-libart_new.jar

5.搜索FinalizerDaemon找到Daemons$FinalizerDaemon.smali
$ find . -name “FinalizerDaemon
./smali/java/lang/Daemons$FinalizerDaemon.smali

6.打开Daemons$FinalizerDaemon.smali,果然发现doFinalize有我们加入的代码

.method private doFinalize(Ljava/lang/ref/FinalizerReference;)V
    .locals 4
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(",
            "Ljava/lang/ref/FinalizerReference<",
            "*>;)V"
        }
    .end annotation

    .line 294
    .local p1, "reference":Ljava/lang/ref/FinalizerReference;, "Ljava/lang/ref/FinalizerReference<*>;"
    const-string v0, "FinalizerDaemon doFinalize yunhen"          //常量v0是"FinalizerDaemon doFinalize yunhen" 

    invoke-static {v0}, Ljava/lang/System;->logE(Ljava/lang/String;)V //调用logE打印日志

    .line 295
    invoke-static {p1}, Ljava/lang/ref/FinalizerReference;->remove(Ljava/lang/ref/FinalizerReference;)V

7、实际手机也会输出
$ adb logcat | grep “FinalizerDaemon doFinalize yunhen”
03-03 15:29:52.648 9462 9478 E System : FinalizerDaemon doFinalize yunhen
03-03 15:30:05.826 2832 2850 E System : FinalizerDaemon doFinalize yunhen
03-03 15:30:05.826 2832 2850 E System : FinalizerDaemon doFinalize yunhen

ps:
System.logE 日志打印需要runtime的一些环境,故先不要在FinalizerDaemon的clinit中打印(clinit是类初始化的时候调用,Android的preloaded-classes就是调用clinit,将部分class提前载入zygote),这个函数用在FinalizerDaemon有点特殊,别的如framework的java日志打印调用Log.w或者Slog.w都是没有问题的

dex2oatd F 03-03 11:48:21  4711  4711 unstarted_runtime.cc:2130] Calling native method java.lang.String dalvik.system.VMRuntime.bootClassPath() in an unstarted non-transactional runtime
//...
  at dalvik.system.VMRuntime.bootClassPath(Native method)
  at java.lang.System.initUnchangeableSystemProperties(System.java:971)
  at java.lang.System.<clinit>(System.java:1690)
  at java.lang.System.logE(System.java:1727)
  at java.lang.Daemons$FinalizerDaemon.<init>(Daemons.java:246)
  at java.lang.Daemons$FinalizerDaemon.<clinit>(Daemons.java:237)
  at java.lang.Daemons$FinalizerDaemon.access$200(Daemons.java:235)
  at java.lang.Daemons.<clinit>(Daemons.java:56)

//如在构造函数FinalizerDaemon中打印日志,本来构造函数不在clinit中,但是静态变量INSTANCE在clinit中,故还是会报错

private static class FinalizerDaemon extends Daemon {
        @UnsupportedAppUsage
        private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
        //...
        FinalizerDaemon() {
            super("FinalizerDaemon");
            //System.logE("FinalizerDaemon yunhen"); //build error
        }