移动性能测试 iOS instruments trace 文件解析方案

匿名 · 2016年03月28日 · 最后由 李鹏 回复于 2019年10月23日 · 3121 次阅读
本帖已被设为精华帖!

原文在这:http://www.cnblogs.com/hyddd/p/5329608.html

前言

已很少写文章,不过这次感觉有必要写一下。因为:

  1. 这个方案通过 debug 逆向得来,很有参考意义。
  2. iOS 这方面资料非常少,做这块时,无论国内外,翻遍了 google,baidu 都没太多合适的资料。 故此,我觉得把整个流程记录下来,你可以认为,这是一次 iOS 的 debug 之旅。

问题起因

最近做 iOS 性能测试,要监控一段时间内 App 的 CPU 占用和网络流量。遗憾的是,iOS instruments 提供的 Activity Monitor 和 Network 模板并不满足我的需求。在 UI 工具中,Activity Monitor 只提供了 CPU 瞬时值,Network 也只提供了总流量,它们均不提供采集样本值。

由于 iOS 闭源,放出的资料又少,故解决此问题的方案屈指可数:

(1). 设备越狱,通过后台守护进程采集数据。
缺陷:新设备无法越狱。

(2). 在产品中嵌入性能数据采集模块。
缺陷:我不倾向在产品中嵌入测试模块;另外,该方案对第三方产品也失效。

(3). 从 instruments 结果文件(.trace)中尝试获取样本值。
缺陷:能不能做都不知道…

因方案 1,2 有硬伤,故选方案 3 尝试。

其实,我开始时也并不确定 trace 文件包含样本数据,但 instruments 可通过 trace 文件恢复监控过程的柱状图,它给了我继续这个方向的信心。

而从 trace 文件获取样本值,有 2 个方向:

(1)分析 trace 结构,获取明文数据
trace 其实是文件夹,但里面文件内容均为二进制,遂放弃......

(2)通过 Undocumented API 解析 trace 文件
由于 instruments 可以解析 trace 文件,那么(2)方法是一定可行的,问题是怎么找到相关的 Undocumented API。

Undocumented API 可行性确认

Lucky,我同事发现了这玩意:TraceUtility,它一个是获取 Time Profiler 样本值的工具(那作者也和我一样遇到这种蛋疼问题......),并且 Readme 中找到重要线索:

(1)所需的 Undocumented API 在/Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/Frameworks:

  • DVTInstrumentsFoundation.framework
  • InstrumentsPlugIn.framework

(2)Instruments 模板以 PlugIns 形式存在于
/Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns

TraceUtility 源码分析

项目就 2 个文件:

InstrumentsPrivateHeader.h: Undocumented API 和 Undocumented 数据结构声明。

main.m:样本数据提取主流程。

深入 Debug 分析

然而 TraceUtility 仅用于获取 Time Profiler 样本数据,并不能满足我获取 CPU 和网络流量样本的要求。

我需要解决的问题是:

(1)确定 instruments 处理类。
(2)从处理类中读出样本数据。

1. 确定 instruments 处理类

方式很简单,在 loadDocument 后,debug 变量 trace._baseInstruments


对比图 1、2

  • XRSamplerInstrument=>XRSamplerRun
  • XRActivityInstrument=>XRActivityRun?
  • XRNetworkingInstrument=>XRNetworkingRun?

在另一个 github 项目class-dump-o-tron, 搜到了相关信息:XRSamplerRun.h,然而在 ActivityPlugin.xrplugin 下却没找到 XRActivityRun.h,而是 XRActivityInstrumentRun.h(大 Apple 的命名能统一点嘛……);在 XRNetworkingPlugIn.xrplugin 下,找到了 XRNetworkingRun.h;

  • XRSamplerInstrument=>XRSamplerRun
  • XRActivityInstrument=>XRActivityInstrumentRun
  • XRNetworkingInstrument=>XRNetworkingRun

2. 读取 trace 样本数据

(1)CPU 样本结果

debug 变量 run._data,发现了样本数据

TraceUtility 使用了 Undocumented API 获取数据,而我在 XRActivityInstrumentRun.h 没找到相关 API,直接通过反射获取吧。


至此,CPU 样本数据获取完成。

(2)网络流量样本结果

XRNetworkRun 和 XRActivityInstrumentRun 对象属性不一样,没有_data,但_saveActivityQueries 中有段 sql,初步预估这玩意用了 localdb,但 db 类型未明。另外,估计_saveInstrumentUUID 应该 db 文件。

然后 cat 2A183EAD-5B9C-45DD-B2BA-D63DCD1165D4 看下,因为文件可能会在头部加注类型信息,cat 结果如下:

捕获 SQLite 文件一个……接下来的事情就是分析表结构了,没什么难度,不作详述了。至此,网络流量样本数据获取完成。

共收到 26 条回复 时间 点赞
扫地僧 回复

trace 文件没有符号化,那怎么自动解决啊。手动可以载入。
但是在做的是自动化的性能数据解析平台
感谢!

李鹏 回复

麻烦问一下,又找到自动化解析 trace 文件的方法么?

rhyme 回复

就是在研究这个 TraceUtility 啊。目前没看到别的。
这个研究起来很需要 OC 语言背景啊,有点吃力,。。。。

匿名 #7 · 2016年03月28日

收到打赏信息,感谢各位打赏:)

匿名 #1 · 2016年03月28日

格式转换得不好,凑合着看吧

#1 楼 @hyddd 好久没见到这个 id 了...

匿名 #3 · 2016年03月28日

@seveniruby 是啊,比较忙,很久没上来混了,先来发靠谱的:)

是真的好久不见了

#3 楼 @hyddd 😄 instruments 是个好东西, mac 有个 dtrace 工具很强大. 不知道 iOS 里面能不能搞.

匿名 #6 · 2016年03月28日

@seveniruby dtrace 没了解,有空看下,哈哈

赞,ios 这块空白区终于填上了

思路值得借鉴,顶个。

https://github.com/powerli/InstrumentsParser 这也有一个开源的, 可以试试看

赞~ 学习了~

#7 楼 @hyddd 分享到 Testerhome 公众号了,顺便更新了一下格式,你看下现在格式有没有问题?

我的 instruments 下面没有这个框架,DVTInstrumentsFoundation.framework

匿名 #14 · 2016年03月29日

@noshuai 👌
@chenhengjie123 格式没问题;有朋友早上告诉 testhome 公众号有转发了
@wangcityboy xcode 目录下搜下吧,我的在.//Contents/SharedFrameworks/DVTInstrumentsFoundation.framework

调用 PFTLoadPlugins();时总是 crash 掉

在 PFTLoadPlugins();这一句中 crash 了,下面是回溯,请问这是什么原因呢?我的是 Xcode7.3,是不是 Xcode6 之前才可以?
2016-04-08 15:11:36.017 TraceUtility[79428:2047286] [MT] DVTAssertions: ASSERTION FAILURE in /Library/Caches/com.apple.xbs/Sources/DVTFrameworks/DVTFrameworks-10174/DVTFoundation/DeveloperStructure/DVTDeveloperPaths.m:58
Details: +[DVTDeveloperPaths applicationDirectoryName] was called, but +initializeApplicationDirectoryName: has not been called yet. Your process must initialize the application directory name if you use this class.
Object:
Method: +applicationDirectoryName
Thread: {number = 1, name = main}
Hints: None
Backtrace:
0 -DVTAssertionHandler handleFailureInMethod:object:fileName:lineNumber:assertionSignature:messageFormat:arguments:
1 _DVTAssertionHandler (in DVTFoundation)
2 _DVTAssertionFailureHandler (in DVTFoundation)
3 +DVTDeveloperPaths applicationDirectoryName
4 -DVTDeveloperPaths sourceSpecificationSearchPathForPlatform:
5 +DVTSourceSpecification searchForAndRegisterAllAvailableSpecifications
6 __PFTLoadPlugins_block_invoke (in InstrumentsPlugIn)
7 _dispatch_client_callout (in libdispatch.dylib)
8 dispatch_once_f (in libdispatch.dylib)
9 PFTLoadPlugins (in InstrumentsPlugIn)
10 main at /Users/yemingyu/Desktop/InstrumentParse/TraceUtility-master/TraceUtility/main.m:13 (in TraceUtility)
11 start (in libdyld.dylib)

匿名 #17 · 2016年04月24日

#16 楼 @yemingyu 解决方案看原文补充部分,不转帖这里了

#17 楼 @hyddd 你本人试过能行吗?

匿名 #19 · 2016年04月26日

#18 楼 @quqing 不行我写出来找喷么.....

#19 楼 @hyddd 文中提到 “(3). 从 instruments 结果文件(.trace)中尝试获取样本值”,为什么我导其他的可以,这两个却不行(求助贴:https://testerhome.com/topics/4747):
instruments -w #udid# -v -t "Activity Monitor" -D Activity_Monitor.trace xxx.ipa
instruments -w #udid# -v -t "Network" -D Network.trace xxx.ipa
你是如何获取这两个 trace 的,盼望回复!

7.3 的.trace 文件貌似解析不出来啊,大神@hyddd

#10 楼 @noshuai 这个开源的你用过吗?xcode7.3 怎么解析失败呢?

#20 楼 @quqing 你好 请问一下 你用脚本怎么取 instruments 关于 cpu 等这些数据的?😄

24楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册