Trace工具主要是为了快速的分析java层方法耗时,在收集收集数据时,我们只收集了自己关注的数据,没有其他数据的干扰,可以很快的找到方法索引,分析方法耗时。
搜索方式:类名|方法名,或者方法名
方法1:com.autonavi.bundle.vui.impl.VUIOuterServiceImpl|loadVCS
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672649087761-34bedfde-0d93-49b6-b0cf-fa3d64c58454.png#clientId=u40b5ce88-9bec-4&from=paste&height=616&id=ANt1J&originHeight=1108&originWidth=2204&originalType=binary&ratio=1&rotation=0&showTitle=false&size=108942&status=done&style=none&taskId=ubaaa979c-3522-484d-8551-d96c958fe38&title=&width=1224.4444768811459)
方法2:com.autonavi.bundle.vui.impl.VUIOuterServiceImpl|preLoad
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672649117947-9d45a43b-5a21-4c33-bcc1-ef66b3cc9b86.png#clientId=u40b5ce88-9bec-4&from=paste&height=791&id=Ya3R8&originHeight=1424&originWidth=2434&originalType=binary&ratio=1&rotation=0&showTitle=false&size=176000&status=done&style=none&taskId=u7faf8873-e23b-4969-b231-1405057238c&title=&width=1352.2222580438788)
1 使用
1.1修改
android/amap_bundle_portal/build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| buildscript { repositories { maven { url 'https://maven.amap.com/nexus/content/repositories/libs/' } } dependencies { classpath 'com.amap.trace:trace-build:1.1.4' } }
apply plugin: 'com.amap.trace'
trace { packages = ['com.autonavi.vcs','com.autonavi.bundle.vui'] isUI = true }
|
1.2编译
如果编译有问题,删除如下目录
1
| android/amap_bundle_portal/build/intermediates/transforms/AmapTraceTransform
|
1.3 运行app
方法栈为超过500次调用,输出一次方法栈记录
输出目录为:/sdcard/Android/data/com.autonavi.minimap/files/trace/trace-1672751118743
1
| adb pull /sdcard/Android/data/com.autonavi.minimap/files/trace/
|
1.4 Perfetto查看
https://ui.perfetto.dev/
将导出的文件拖入perfetto网页
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672664851349-42dc2cb5-ada0-4a98-901a-292308505a0e.png#clientId=u40b5ce88-9bec-4&from=paste&height=593&id=u3a0ecd29&originHeight=1068&originWidth=3566&originalType=binary&ratio=1&rotation=0&showTitle=false&size=172078&status=done&style=none&taskId=u5bb19a64-c068-48fd-8a49-98d95026552&title=&width=1981.1111635926343)
输入关注的方法:例如我想看VUICenter中的onPageLifeCreate方法
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672664892982-aa0c008a-f2bc-4c07-8fb6-dc42e1c5f846.png#clientId=u40b5ce88-9bec-4&from=paste&height=356&id=u537dd814&originHeight=640&originWidth=1816&originalType=binary&ratio=1&rotation=0&showTitle=false&size=159989&status=done&style=none&taskId=u3b104cfc-9a1a-47d2-ba67-8e8286f769a&title=&width=1008.888915615318)
搜索:onPageLifeCreate 或者VUICenter|onPageLifeCreate
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672665194257-a40555e8-74f7-4a8e-b70d-7028674aa2e6.png#clientId=u40b5ce88-9bec-4&from=paste&height=631&id=u701dd012&originHeight=1136&originWidth=1992&originalType=binary&ratio=1&rotation=0&showTitle=false&size=123206&status=done&style=none&taskId=uf5554db7-5963-49da-81d0-6b22cb06b57&title=&width=1106.6666959833224)
使用快捷键 w放大,s缩小,a左移,d右移
2 原理
我们这里使用了atrace的日志输出格式,我们给所有的关注的方法进行插桩,在方法开始和结束的位置进行打点,然后将打的点进行输出,输出格式是atrace的日志格式。
atrace的日志格式,我么可以使用systrace和perfetto进行解析。
2.1 atrace日志格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| capturing trace... done TRACE: # tracer: nop # # entries-in-buffer/entries-written: 251577/251577 #P:8 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION # | | | | |||| | | <...>-14676 (14676) [004] .... 362012.007768: tracing_mark_write: B|14676|MainActivity:onCreate <...>-14676 (14676) [004] .... 362011.991722: tracing_mark_write: E|14676
|
格式如下:
1
| <线程名>-<线程id> (进程id) [cpu] .... <us时间戳>: tracing_mark_write: <B|E>|<进程id>|<tag>|<args>
|
那么我们只需要在方法开始和结束的地方进行插桩,将上方的信息写入到trace日志中,即可统计到方法耗时。
我们导出的日志如下
1 2 3 4 5 6 7 8 9
| TRACE: # tracer: nop #
main-30573 (30573) [001] ... 513231.528305: tracing_mark_write: B|30573|com.autonavi.bundle.vui.impl.VUIOuterServiceImpl|loadVCS main-30573 (30573) [001] ... 513231.583242: tracing_mark_write: E|30573|com.autonavi.bundle.vui.impl.VUIOuterServiceImpl|loadVCS TaskScheduler#3-30674 (30573) [001] ... 513231.729805: tracing_mark_write: B|30573|com.autonavi.bundle.vui.impl.VUIOuterServiceImpl|preLoad TaskScheduler#3-30674 (30573) [001] ... 513231.732829: tracing_mark_write: E|30573|com.autonavi.bundle.vui.impl.VUIOuterServiceImpl|preLoad
|
2.2 插桩
这里插桩使用了tranform + asm;
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672665751075-7db5ef1b-cb67-4189-a687-28e48d71792d.png#clientId=u40b5ce88-9bec-4&from=paste&height=628&id=ufe64cc84&originHeight=1130&originWidth=1168&originalType=binary&ratio=1&rotation=0&showTitle=false&size=252032&status=done&style=none&taskId=ue8ebed68-2a33-4c2b-9b08-8d0288a3324&title=&width=648.8889060785746)
AmapTrace 主要是将信息写入文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| public class AmapTrace {
static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static List<String> traces = new ArrayList<>();
public static int pid = 0; public static boolean isFirst = true;
public static final String FILE_NAME = "trace-" + System.currentTimeMillis(); public static String FILE_PATH;
public static final Map<Long, Integer> threadMap = new HashMap<>();
public static String getFilePath() { return FILE_PATH + File.separator + "files" + File.separator + "trace" + File.separator + FILE_NAME; }
public static void startTrace(String methodId) { if (pid == 0) { pid = android.os.Process.myPid(); }
final long time = SystemClock.elapsedRealtimeNanos();
Integer tid = getTid();
String pkg = Thread.currentThread().getName();
executorService.execute(new Runnable() { @Override public void run() { realStartTrace(methodId, tid, pkg, time); } }); }
private static int getTid() { Integer aLong = threadMap.get(Thread.currentThread().getId()); if (aLong == null) { aLong = Process.myTid(); threadMap.put(Thread.currentThread().getId(), aLong); } return aLong; }
public static void endTrace(String methodId) { if (pid == 0) { pid = android.os.Process.myPid(); }
final long time = SystemClock.elapsedRealtimeNanos();
int tid = getTid();
String pkg = Thread.currentThread().getName();
executorService.execute(new Runnable() { @Override public void run() { realEndTrace(methodId, tid, pkg, time); } });
}
private static void realStartTrace(String methodId, int tid, String pkg, long time) { String us = String.valueOf(time / 1000); us = us.substring(0, 6) + "." + us.substring(6); traces.add(String.format("%s-%d (%d) [001] ... %s: tracing_mark_write: B|%d|%s", pkg, tid, pid, us, pid, methodId)); }
private static void realEndTrace(String methodId, int tid, String pkg, long time) {
String us = String.valueOf(time / 1000); us = us.substring(0, 6) + "." + us.substring(6); traces.add(String.format("%s-%d (%d) [001] ... %s: tracing_mark_write: E|%d|%s", pkg, tid, pid, us, pid, methodId)); if (traces.size() > 1000) { export(); } }
public static void export() { } }
|
文件中FILE_PATH 没有进行赋值,是在编译阶段进行的字节码修改
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672665961165-290ea6ef-c227-447a-8806-d3089bae5cd2.png#clientId=u40b5ce88-9bec-4&from=paste&height=326&id=u2ae9303f&originHeight=586&originWidth=2198&originalType=binary&ratio=1&rotation=0&showTitle=false&size=127786&status=done&style=none&taskId=ud3690bb7-9845-4da7-a47c-43d0e1fe0e3&title=&width=1221.1111434595093)
3 问题
如果一个包中同时存在大小写class,解压时被覆盖的问题,如下,r.class 的内容会被R.class的内容覆盖。
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672666698786-d5b82e9e-555a-454f-a3d6-02e4ae52d286.png#clientId=u40b5ce88-9bec-4&from=paste&height=61&id=u946755c8&originHeight=110&originWidth=806&originalType=binary&ratio=1&rotation=0&showTitle=false&size=18352&status=done&style=none&taskId=u3915fc97-e37f-4289-966a-73ab139b44f&title=&width=447.77778963983826)
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1672666721865-85e6a2d2-227f-4b22-8416-af91cec5312c.png#clientId=u40b5ce88-9bec-4&from=paste&height=82&id=u8492c405&originHeight=148&originWidth=882&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24150&status=done&style=none&taskId=u4ec4111e-b28c-4911-b719-9e8f36c3b8c&title=&width=490.0000129805674)
这个问题暂时还没有解决,目前处理是当遇到这个jar包时,直接过滤了。
4 未来
未来计划使用Trace.beginSection和Trace.endSection,主要需要解决2个问题。
- hook FD : /sys/kernel/debug/tracing/trace_marker
- atrace_enabled_tags 的值返回1
hook 使用kwai::linker