• debug:am trace-ipc源码分析


    debug:am trace-ipc源码分析

    一、使用

    官网介绍

    链接:Slow rendering

    如果您有 binder 事务,则可以使用以下 adb 命令捕获其调用堆栈:

    $ adb shell am trace-ipc start
        … use the app - scroll/animate ...
        $ adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc-trace.txt
        $ adb pull /data/local/tmp/ipc-trace.txt
        
    

    命令提示

    :/ # am
    Activity manager (activity) commands:
      trace-ipc [start|stop] [--dump-file <FILE>]
          Trace IPC transactions.
          start: start tracing IPC transactions.
          stop: stop tracing IPC transactions and dump the results to file.
          --dump-file <FILE>: Specify the file the trace should be dumped to.
    

    小结

    1、抓所有进程的java层面的binder调用栈,只抓发起端的线程栈,也就是堆栈的最上一句总是

    Trace: java.lang.Throwable
    android.os.BinderProxy.transact(BinderProxy.java:509)
    

    2、抓的是一段时间start<-->stop,没有类似logcat的缓存

    3、主要用于在java层面上

    • 确认谁(进程)调用了某个binder服务
    • 确认操作过程中发生了哪些ipc动作

    需要注意的是,此处局限于java层,局限于ipc。

    • 如果你的目的是确认最全面(进程内+进程外)的代码流程,则还需要cpu-profiler工具来抓进程内的流程。或者是traceviewSimpleperf

    • 如果你想确认最全面的ipc流程,目前native的ipc记录工具还有待发掘。

    下面分析源码实现。

    二、源码分析

    代码基于android11。am命令的实现见debug:am、cmd命令

    书接上文,

    ActivityManagerShellCommand#onCommand

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand

     176     @Override
     177     public int onCommand(String cmd) {      
     183             switch (cmd) {
     184                 case "start":
     185                 case "start-activity":
     186                     return runStartActivity(pw);
    ......
     203                 case "trace-ipc":
     204                     return runTraceIpc(pw);
    ......
    

    走到204行

    ActivityManagerShellCommand#runTraceIpc

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand

     754     int runTraceIpc(PrintWriter pw) throws RemoteException { 
     755         String op = getNextArgRequired();
     756         if (op.equals("start")) {
     757             return runTraceIpcStart(pw);
     758         } else if (op.equals("stop")) {
     759             return runTraceIpcStop(pw);
     760         } else {
     761             getErrPrintWriter().println("Error: unknown trace ipc command '" + op + "'");
    

    757行start,759行stop。先看start

    am trace-ipc start

    ActivityManagerShellCommand#runTraceIpcStart

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand

     168     ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) {
     169         mInterface = service;     
    ----------------------------------------------------------------------------------
     766     int runTraceIpcStart(PrintWriter pw) throws RemoteException {                      
     767         pw.println("Starting IPC tracing.");
     768         pw.flush();
     769         mInterface.startBinderTracking();
     770         return 0;
     771     }
    

    769行,mInterface是ams,见构造函数169行。继续跟

    ActivityManagerService.java#startBinderTracking

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    18843     public boolean startBinderTracking() throws RemoteException {
    18844         synchronized (this) {
    18845             mBinderTransactionTrackingEnabled = true;
    18846             // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
    18847             // permission (same as profileControl).
    18848             if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
    18849                     != PackageManager.PERMISSION_GRANTED) {
    18850                 throw new SecurityException("Requires permission "
    18851                         + android.Manifest.permission.SET_ACTIVITY_WATCHER);
    18852             }
    18853 
    18854             for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
    18855                 ProcessRecord process = mProcessList.mLruProcesses.get(i);
    18856                 if (!processSanityChecksLocked(process)) {
    18857                     continue;
    18858                 }
    18859                 try {
    18860                     process.thread.startBinderTracking();
    18861                 } catch (RemoteException e) {
    

    18848-18852鉴权。

    18854行for循环,将系统的java进程都开启binderTracking,重点就是18860行。

    下面进入java进程的ActivityThread中跟踪

    ActivityThread.java$ApplicationThread#startBinderTracking

    frameworks/base/core/java/android/app/ActivityThread.java

     947     private class ApplicationThread extends IApplicationThread.Stub {
    1688         @Override
    1689         public void startBinderTracking() {                                             
    1690             sendMessage(H.START_BINDER_TRACKING, null);
    1691         }     
    --------------------------------------------------------------------------
    1803     class H extends Handler {
    1907         public void handleMessage(Message msg) {
    2042                 case START_BINDER_TRACKING:                                             
    2043                     handleStartBinderTracking();
    2044                     break;
    --------------------------------------------------------------------------
    3842     private void handleStartBinderTracking() {
    3843         Binder.enableTracing();                                                         
    3844     }
    

    Binder.java#enableTracing

    frameworks/base/core/java/android/os/Binder.java

     138     /**
     139      * Enable Binder IPC tracing.
     140      *
     141      * @hide
     142      */
     143     public static void enableTracing() {
     144         sTracingEnabled = true;                                                         
     145     }
    -------------------------------------------------------------------
     161     public static boolean isTracingEnabled() {
     162         return sTracingEnabled;
     163     }
    

    144行,将标志位置true。作用是在每次java世界发起binder调用的时候,来通过这个参数判断是否要抓trace。

    BinderProxy.java#transact

    frameworks/base/core/java/android/os/BinderProxy.java

    495     public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    517         final boolean tracingEnabled = Binder.isTracingEnabled();
    518         if (tracingEnabled) {
    519             final Throwable tr = new Throwable();
    520             Binder.getTransactionTracker().addTrace(tr);
    521             StackTraceElement stackTraceElement = tr.getStackTrace()[1];
    522             Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
    523                     stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
    524         }
    526         // Make sure the listener won't change while processing a transaction.
    527         final Binder.ProxyTransactListener transactListener = sTransactListener;
    530         if (transactListener != null) {
    531             final int origWorkSourceUid = Binder.getCallingWorkSourceUid();
    532             session = transactListener.onTransactStarted(this, code, flags);
    540         }
    549         try {
    550             return transactNative(code, data, reply, flags);
    551         } finally {
    554             if (transactListener != null) {
    555                 transactListener.onTransactEnded(session);
    556             }
    558             if (tracingEnabled) {
    559                 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
    

    519、520行抓调用栈放到TransactionTracker里存放

    522和559是抓systrace,抓出来类似长这样,有详细的类名方法名:

    本图来自Android Tec And Perf群,本文首偷,再偷必究

    另外需要关注的是transactListener,除了命令行这种看发生了啥ipc,用这个Listener可以在代码里监听本进程的bidner调用,做些监控工作。

    TransactionTracker.java#addTrace

    frameworks/base/core/java/android/os/TransactionTracker.java

     33 public class TransactionTracker {
     34     private Map<String, Long> mTraces;
     46     public void addTrace(Throwable tr) {
     47         String trace = Log.getStackTraceString(tr);
     48         synchronized (this) {
     49             if (mTraces.containsKey(trace)) {
     50                 mTraces.put(trace, mTraces.get(trace) + 1);
     51             } else {
     52                 mTraces.put(trace, Long.valueOf(1));
    

    Binder.java#execTransactInternal

    frameworks/base/core/java/android/os/Binder.java

    1129     private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,int callingUid) {
    1141         // Log any exceptions as warnings, don't silently suppress them.
    1142         // If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
    1143         final boolean tracingEnabled = Binder.isTracingEnabled();
    1144         try {
    1145             if (tracingEnabled) {
    1146                 final String transactionName = getTransactionName(code);
    1147                 Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
    1148                         + (transactionName != null ? transactionName : code));
    1149             }
    1181         } finally {
    1182             if (tracingEnabled) {
    1183                 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
    1184             }
    

    1147、1183行。在每次java世界的binder调用时,服务端也会开启systrace。

    至于如何得知每次binder调用都走到BinderProxy.java#transact、Binder.java#execTransactInternal,

    可以看下android10Binder(五)java世界的binder:AndroidFramework

    下面看am trace-ipc stop

    am trace-ipc stop

    ActivityManagerShellCommand.java#runTraceIpcStop

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java

     754     int runTraceIpc(PrintWriter pw) throws RemoteException {
     755         String op = getNextArgRequired();
     756         if (op.equals("start")) {
     757             return runTraceIpcStart(pw);
     758         } else if (op.equals("stop")) {
     759             return runTraceIpcStop(pw);         
     -------------------------------------------------------------------
     773     int runTraceIpcStop(PrintWriter pw) throws RemoteException {
     774         final PrintWriter err = getErrPrintWriter();
     775         String opt;
     776         String filename = null;
     777         while ((opt=getNextOption()) != null) {
     778             if (opt.equals("--dump-file")) {
     779                 filename = getNextArgRequired();
     780             }
     790         File file = new File(filename);
     791         file.delete();
     792         ParcelFileDescriptor fd = openFileForSystem(filename, "w");
     797         if (!mInterface.stopBinderTrackingAndDump(fd)) {
    

    779-792行拿到fd。一般我们传的文件路径是/data/local/tmp/xxx.txt

    797行,再次进入ams

    ActivityManagerService.java#stopBinderTrackingAndDump

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    18869     public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException {
    18870         try {
    18871             synchronized (this) {
    18872                 mBinderTransactionTrackingEnabled = false;
    18875                 if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
    18876                         != PackageManager.PERMISSION_GRANTED) {
    18877                     throw new SecurityException("Requires permission "
    18878                             + android.Manifest.permission.SET_ACTIVITY_WATCHER);
    18879                 }
    18885                 PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));                               
    18886                 pw.println("Binder transaction traces for all processes.
    ");
    18887                 for (ProcessRecord process : mProcessList.mLruProcesses) {
    18892                     pw.println("Traces for process: " + process.processName);
    18893                     pw.flush();   
    18894                     try {
    18895                         TransferPipe tp = new TransferPipe();
    18896                         try {
    18897                             process.thread.stopBinderTrackingAndDump(tp.getWriteFd());
    18898                             tp.go(fd.getFileDescriptor());
    18899                         } finally {
    18900                             tp.kill();
    

    18875行,鉴权。

    18885行,输出流用于写文件。

    18887行for循环,18897、18898遍历所有java进程将trace写文件。

    ActivityThread.java$ApplicationThread#stopBinderTrackingAndDump

    frameworks/base/core/java/android/app/ActivityThread.java

     947     private class ApplicationThread extends IApplicationThread.Stub {
    1693         @Override
    1694         public void stopBinderTrackingAndDump(ParcelFileDescriptor pfd) {
    1695             try {
    1696                 sendMessage(H.STOP_BINDER_TRACKING_AND_DUMP, pfd.dup());               
    1698             } finally {
    1699                 IoUtils.closeQuietly(pfd);
    --------------------------------------------------------------------------
    1803     class H extends Handler {
    1907         public void handleMessage(Message msg) {
    2045                 case STOP_BINDER_TRACKING_AND_DUMP:                                     
    2046                     handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
    2047                     break;
    --------------------------------------------------------------------------     
    3846     private void handleStopBinderTrackingAndDump(ParcelFileDescriptor fd) {             
    3847         try {
    3848             Binder.disableTracing();
    3849             Binder.getTransactionTracker().writeTracesToFile(fd);
    3850         } finally {
    3851             IoUtils.closeQuietly(fd);
    3852             Binder.getTransactionTracker().clearTraces();
    

    TransactionTracker.java#writeTracesToFile

    frameworks/base/core/java/android/os/TransactionTracker.java

     57     public void writeTracesToFile(ParcelFileDescriptor fd) {
     62         PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
     63         synchronized (this) {
     64             for (String trace : mTraces.keySet()) {
     65                 pw.println("Count: " + mTraces.get(trace));
     66                 pw.println("Trace: " + trace);
     67                 pw.println();
     68             }
     69         }
     70         pw.flush();
     71     }
    

    最终就是每个进程将自己抓的trace写入文件。

    三、总结

    am trace-ipc start命令做了什么呢,打开每个进程的binder trace记录开关tracingEnabled。当本进程发起ipc时

    • 获取调用栈保存起来TransactionTracker#mTraces。

    • 打开systrace开关,此时抓systrace、perfetto会记录slice

    am trace-ipc stop --dumpfile /data/local/tmp/xxx.txt命令将上述每个进程保存的调用栈写入到文件

    还有个Binder.ProxyTransactListener值得关注

    作者:秋城 | 博客:https://www.cnblogs.com/houser0323 | 转载请注明作者出处
  • 相关阅读:
    加速 gradle 打包速度 !!
    坑爹的希腊字母 ο
    09 你这导师太假了,不如换个学术大佬 !
    导师不敢和你说的水论文隐藏技巧,只教你水论文
    12 水论文如何吹一个好故事
    07 导师不敢和你说的水论文隐藏技巧,毕业论文,小论文和综述的区别,三者怎么进行换汤不换药转换
    04 导师不敢和你说的水论文隐藏技巧,论文模型需不需要继承
    从Python到水一篇AI论文(核心 or Sci三区+)(目录)
    05 导师不敢和你说的水论文隐藏技巧,没有继承且无法复现论文模型怎么办?如何做一个好的学术裁缝
    06 导师不敢和你说的水论文隐藏技巧,顶刊、顶会、水刊的论文读哪个,如何做一个称职的学术裁缝.md
  • 原文地址:https://www.cnblogs.com/houser0323/p/15069884.html
Copyright © 2020-2023  润新知