链接: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层面上
需要注意的是,此处局限于java层,局限于ipc。
如果你的目的是确认最全面(进程内+进程外)的代码流程,则还需要cpu-profiler工具来抓进程内的流程。或者是traceview与Simpleperf。
如果你想确认最全面的ipc流程,目前native的ipc记录工具还有待发掘。
下面分析源码实现。
代码基于android11。am命令的实现见debug:am、cmd命令
书接上文,
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行
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
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行。继续跟
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中跟踪
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 }
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。
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调用,做些监控工作。
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));
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
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
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写文件。
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();
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值得关注