debug:am trace-ipc原始碼分析

秋城發表於2021-07-28

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

另外需要關注的是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.\n");
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。

am trace-ipc stop --dumpfile /data/local/tmp/xxx.txt命令將上述每個程式儲存的呼叫棧寫入到檔案

還有個Binder.ProxyTransactListener值得關注

相關文章