写了个 Android 性能检测的库,还有人看性能相关的么?( 三 )


最终终端 log 打印效果如下:
com.xander.performace.demo W/demo_Issue: =================================================type: THREADmsg: THREAD POOL CREATEcreate time: 2021-01-13 11:23:47create trace:com.xander.performance.StackTraceUtils.list(StackTraceUtils.java:39)com.xander.performance.ThreadTool$ThreadPoolExecutorConstructorHook.afterHookedMethod(ThreadTool.java:158)de.robv.android.xposed.DexposedBridge.handleHookedArtMethod(DexposedBridge.java:265)me.weishu.epic.art.entry.Entry64.onHookObject(Entry64.java:64)me.weishu.epic.art.entry.Entry64.referenceBridge(Entry64.java:239)java.util.concurrent.Executors.newSingleThreadExecutor(Executors.java:179)com.xander.performance.demo.MainActivity.testThreadPool(MainActivity.kt:38)java.lang.reflect.Method.invoke(Method.java:-2)androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:397)android.view.View.performClick(View.java:7496)android.view.View.performClickInternal(View.java:7473)android.view.View.access$3600(View.java:831)android.view.View$PerformClick.run(View.java:28641)android.os.Handler.handleCallback(Handler.java:938)android.os.Handler.dispatchMessage(Handler.java:99)android.os.Looper.loop(Looper.java:236)android.app.ActivityThread.main(ActivityThread.java:7876)java.lang.reflect.Method.invoke(Method.java:-2)com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

  • IPC(进程间通讯)监控的原理
进程间通讯的具体原理,也就是 Binder 机制,这里不做详细的说明,也不是这个框架库的原理 。
检测进程间通讯的方法和前面检测线程的方法类似,就是找到所有的进程间通讯的方法的共同点,然后 对共同点做一些修改或者说切片,让应用在进行进程间通讯的时候,打印一下调用栈,然后继续做原来 的事情 。就达到了 IPC 监控的目的 。
那如何找到共同点,或者说切片,就是本节的重点 。
进程间通讯离不开 Binder ,需要从 Binder 入手 。
写一个 AIDL demo 后来发现,自动生成的代码里面,接口 A 继承自 IInterface 接口,然后接口里面有个 内部抽象类 Stub 类,继承自 Binder ,同时实现了接口 A。这个 Stub 类里面还有一个内部类 Proxy , 实现了接口 A ,并持有一个 IBinder 实例 。
我们在使用 AIDL 的时候,会用到 Stub 类的 asInterFace 的方法,这个方法会新建一个 Proxy 实例, 并给这个 Proxy 实例传入 IBinder , 或者如果传入的 IBinder 实例如果是接口 A 的话,就强制转化为接口 A 实例 。一般而言,这个 IBinder 实例是 ServiceConnection 的回调方法里面的实例,是 BinderProxy 的实例 。所以 Stub 类的 asInterFace 一般会创建一个 Proxy 实例,查看这个 Proxy 接口的实现方法, 发现最终都会调用 BinderProxy 的 transact 方法,所以 BinderProxy 的 transact 方法是一个很好的切入点 。
本来我也是计划通过 hook 住 BinderProxy 类的 transact 方法来做 IPC 的检测的 。但是 epic 库在 hook 含有 Parcel 类型参数的方法的时候,不稳定,会有异常 。由于暂时还没能力解决这个异常,只能重新找切入点 。最后发现 AIDL demo 生成的代码里面,除了调用了 调用 BinderProxy 的 transact 方法外, 还调用了 Parcel 的 readException 方法,于是决定 hook 这个方法来切入 IPC 调用流程, 从而达到 IPC 监控的目的 。
最终终端 log 打印效果如下:
com.xander.performace.demo W/demo_Issue: =================================================type: IPCmsg: IPCcreate time: 2021-01-13 11:25:04trace:com.xander.performance.StackTraceUtils.list(StackTraceUtils.java:39)com.xander.performance.IPCTool$ParcelReadExceptionHook.beforeHookedMethod(IPCTool.java:96)de.robv.android.xposed.DexposedBridge.handleHookedArtMethod(DexposedBridge.java:229)me.weishu.epic.art.entry.Entry64.onHookVoid(Entry64.java:68)me.weishu.epic.art.entry.Entry64.referenceBridge(Entry64.java:220)me.weishu.epic.art.entry.Entry64.voidBridge(Entry64.java:82)android.app.IActivityManager$Stub$Proxy.getRunningAppProcesses(IActivityManager.java:7285)android.app.ActivityManager.getRunningAppProcesses(ActivityManager.java:3684)com.xander.performance.demo.MainActivity.testIPC(MainActivity.kt:55)java.lang.reflect.Method.invoke(Method.java:-2)androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:397)android.view.View.performClick(View.java:7496)android.view.View.performClickInternal(View.java:7473)android.view.View.access$3600(View.java:831)android.view.View$PerformClick.run(View.java:28641)android.os.Handler.handleCallback(Handler.java:938)android.os.Handler.dispatchMessage(Handler.java:99)android.os.Looper.loop(Looper.java:236)android.app.ActivityThread.main(ActivityThread.java:7876)java.lang.reflect.Method.invoke(Method.java:-2)com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)


推荐阅读