debug:am trace-ipc源码分析
debug:am trace-ipc源码分析
一、使用
官网介绍
如果您有 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工具来抓进程内的流程。或者是traceview与Simpleperf。
如果你想确认最全面的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值得关注
debug:am trace-ipc源码分析的更多相关文章
- PHP扩展编写、PHP扩展调试、VLD源码分析、基于嵌入式Embed SAPI实现opcode查看
catalogue . 编译PHP源码 . 扩展结构.优缺点 . 使用PHP原生扩展框架wizard ext_skel编写扩展 . 编译安装VLD . Debug调试VLD . VLD源码分析 . 嵌 ...
- zuul源码分析-探究原生zuul的工作原理
前提 最近在项目中使用了SpringCloud,基于zuul搭建了一个提供加解密.鉴权等功能的网关服务.鉴于之前没怎么使用过Zuul,于是顺便仔细阅读了它的源码.实际上,zuul原来提供的功能是很单一 ...
- debug:am、cmd命令源码分析
debug:am.cmd命令源码分析 目录 debug:am.cmd命令源码分析 am命令的实现 手机里的am am.jar cmd命令的实现 手机里的cmd cmd activity cmd.cpp ...
- Android源码分析(八)-----系统启动流程&IPC简述
一 :系统启动流程图 从下往上依次启动linux kernel -->zygote-->SystemServer-->NativeService-->AndroidServic ...
- [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程
[源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 目录 [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 0x00 摘要 0x01 前文回顾 1.1 基类作用 1. ...
- 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 百篇博客分析OpenHarmony源码 | v28.03
百篇博客系列篇.本篇为: v28.xx 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当 ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- Struts2 源码分析——DefaultActionInvocation类的执行action
本章简言 上一章讲到关于拦截器的机制的知识点,让我们对拦截器有了一定的认识.我们也清楚的知道在执行用户action类实例之前,struts2会先去执行当前action类对应的拦截器.而关于在哪里执行a ...
- Struts2 源码分析——配置管理之PackageProvider接口
本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...
随机推荐
- httprunner的简介、httprunner做接口测试入门知识,使用httprunner模拟get请求及post请求
一.httprunner的简介 HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试.性能测试.线上监控.持续集成等多种 ...
- 04:Django生命周期流程图
- 日志挖掘针对DML语句
作用: 针对用户的误操作,比如更改数据错误,误删除表等,可以用日志挖掘的方式,跟踪哪个用户什么时候做的操作,并进行数据还原. 一.前期准备: 1.添加最小补充日志,能够记录到更详细的信息,为日志挖掘分 ...
- dev下拉框选择不同值显示不同控件
单列的ASPxFormLayout直接前台控制就可以了,多列的前台控制后会出现空白 <dx:LayoutItem Caption="内容类型" Height="40 ...
- 《手把手教你》系列基础篇之(四)-java+ selenium自动化测试- 启动三大浏览器(下)基于Maven(详细教程)
1.简介 上一篇文章,宏哥已经在搭建的java项目环境中实践了,今天就在基于maven项目的环境中给小伙伴们 或者童鞋们演示一下. 2.eclipse中新建maven项目 1.依次点击eclipse的 ...
- gRPC(2):四种基本通信模式
在 gRPC(1):入门及简单使用(go) 中,我们实现了一个简单的 gRPC 应用程序,其中双方通信是简单的请求-响应模式,没发出一个请求都会得到一个响应,然而,借助 gRPC 可以实现不同的通信模 ...
- 如何优雅地实现浏览器兼容与CSS规则回退
读完了<Visual Studio Code权威指南>,前端方面书籍不能停,于是捡起「CSS一姐」 Lea Verou 的<CSS魔法>. 我们没法控制用户使用新版本还是老版本 ...
- Linux从头学02:x86中内存【段寻址】方式的来龙去脉
作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...
- 字典翻译@Dict
1.编写翻译字典@Dict /** * 数据字典翻译注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) publ ...
- 协程与Swoole的原理,相关应用以及适用场景等
什么是协程 协程(Coroutine)也叫用户态线程,其通过协作而不是抢占来进行切换.相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低.协程是进程的补充,或者是互补关系. 要 ...