1 Instrument 10 架构
主要分为两个部分 Standard UI (界面) 和 Analysis Core (分析核心)

1.1
其中 Standard UI 也是分为两部分 : table (蓝色部分)和 graph (红色部分).

其中
table 又大致分为5种格式1 List (列表)
2 Aggregation (聚合显示)
3 Call Tree (就是timeprofile 中的堆栈)
4 Narrative (叙事)
5 Time Slice (时间片)
graph的话基本分为4种显示
1 Plot

2 Automatic Treatment
3 Plot Template

4 Histogram (矩形图)
2 graph和table的数据来源.
这就到了介绍 Analysis core的时候了。首先就是收集,存储[不需要我们实现], 分析(合并), 可视化。
一个数据的展示流程
Tracing -> Schemas/Modeling -> Visualization
2.1 数据源
数据源 通过 定义的os_singPost 或者 molder规则产生数据, 我们利用收集到的数据 去定义 Schema ,可以说 Instrument 的所有可视化都是依赖于Schema ,因为定义的 modeler 也是要输出到 Schema中
Schema 分为
<1 <os-signpost-interval-schema> 对应我们自定义的 os_singPost 或者modeler
<2 <point-schema> 点类型 schema

2.2 如何在schema使用数据源
<1 os_singPost的数据源 需要在 schame中 <start-pattern> <end-pattern>中的定义 和 代码中定义的 参数名 一致。
代码中的定义
case .downloading:
os_signpost(.begin, log: SignPostLog.imageLoadLog, name: SignPostLog.imgDownload, signpostID:OSSignpostID(log: SignPostLog.imageLoadLog, object: self),"Image name:%@", name)
case .success:
os_signpost(.end, log: SignPostLog.imageLoadLog, name: SignPostLog.imgDownload, signpostID:OSSignpostID(log: SignPostLog.imageLoadLog, object: self),"download result:%@, time:%d", "success", time)
case .cancel:
os_signpost(.end, log: SignPostLog.imageLoadLog, name: SignPostLog.imgDownload, signpostID:OSSignpostID(log: SignPostLog.imageLoadLog, object: self),"download result:%@, time:%d", "cancel", time)
Schema中的定义
<os-signpost-interval-schema>
<!-- 数据捕捉 -->
<start-pattern>
<message>"Image name:"?image-name</message>
</start-pattern>
<end-pattern>
<message>"download result:" ?result ", time:" ?time</message>
</end-pattern>
</os-signpost-interval-schema>
<2 关于使用 modeler 进行数据源采集
需要到 CLIPS 语法定义 modeler 规则 进行数据收集, 然后在自定义的 Instrument 配置文件中增加 <modeler> 模块
先看一个示例
<!-- 建模器 -->
<modeler>
<id>com.coderhg.dt.download.Anitipatterns</id>
<title>Anti-patterns modeler</title>
<purpose>Generates warnings into detected</purpose>
<production-system>
<rule-path>duplicate-call-detection.clp</rule-path>
</production-system>
<output>
<schema-ref>downloader-narrative</schema-ref>
<required-input>
<schema-ref>os-signpost</schema-ref>
</required-input>
</output>
</modeler>
此modeler 是数据规则 在duplicate-call-detection.clp 文件中定义, 输出的schema 是 downloader-narrative ,还有一个必须要强制导入的 os-signpost(系统的 Schema) 的依赖。
然后 在 downloader-narrative 的 schema 中将 modeler采集到的数据 再定义成需要使用的 column 供给 instrument 使用。
Sorry:本文暂不涉及 modeler文件的编写。
2.3 然后将数据定义成 column 提供给 instrument 中定义的 table 和 graph
如下就是将image-name数据 转换成 column, 还可以自己定义如何展示column的规则
<os-signpost-interval-schema>
<!-- 定义数据结构 -->
<column>
<mnemonic>image-name</mnemonic>
<title>image-name-title</title>
<type>string</type>
<expression>?image-name</expression>
</column>
<column>
<mnemonic>impact</mnemonic>
<title>Impact</title>
<type>event-concept</type>
<!-- 如果size?大于2 就是 高 否则 是 低 -->
<expression>(if (> ?size 2) then "High" else "Low")</expression>
</column>
</os-signpost-interval-schema>
3 Instrument
3.1 Instrument的结构
Instrument 的格式采用 xml 。
主要分为三部分
1 基本参数定义 Instrument 的 一些基本参数(名称、描述、id、icon) ,更多的还可以从外部引入其他 parameter 参数
<instrument>
<id>com.mm.uitest.DownloaderInstrument.downloader</id>
<title>Instruement-title</title>
<category>Behavior</category>
<purpose>Download-purpose</purpose>
<icon>Generic</icon>
<import-parameter>
<from-scope>trace</from-scope>
<name>?target-pid</name>
</import-parameter>
</instrument>
2 创建 table,table 一定要引入 schema
<instrument>
<create-table>
<id>image-download-table</id>
<schema-ref>image-download-schema</schema-ref>
</create-table>
</instrument>
3 创建可视化(graph or table )就是第一部分说的 标准UI 部分, 数据依赖于 第二部定义的 table, 所有数据源都必须是 table 中依赖的 schema 中定义的 column, 具体展示格式可以根据需要进行调整。
以list 为例
<instrument>
<list>
<title>image-download-list</title>
<table-ref>image-download-table</table-ref>
<column>image-name</column>
<column>image-result</column>
<column>image-time-mnemonic</column>
</list>
</instrument>
苹果详细的 Instrument 配置 格式文档
一下是一个完整的Instrument 结构
<package>
<!-- package定义 -->
<!-- 定义或者 引入系统的schema -->
<import-schema>os-signpost</import-schema>
<os-signpost-interval-schema>
......
<column></column>
</os-signpost-interval-schema>
<!-- 定义modeler -->
<modeler>
......
</modeler>
<!-- 定义Instrument -->
<instrument>
<!-- 基本参数定义 -->
<id>com.mm.DownloaderInstrument.downloader</id>
<title>Image Download</title>
<category>Behavior</category>
<purpose>ShowImageDownPurpose</purpose>
<icon>Generic</icon>
<!-- 创建table 必须要依赖某个 schema -->
<create-table>
<id>image-download-table</id>
<schema-ref>image-download-schema</schema-ref>
</create-table>
<!-- 可视化 -->
<graph>
<title>Background Images</title>
<lane>
</lane>
</graph>
<list>
<title>image-download-list</title>
<table-ref>image-download-table</table-ref>
<column>image-name</column>
</list>
</instrument>
总结: 所有的可视化数据 都是来自于table 而table 又依赖于 自定义或者 苹果已经实现的schema 。所以所有的数据都需要换成 schema中的 column
4 自定义展示一个 time-profile
此处我因为没有读懂 time-profile 中modeler源码,所以数据源无法自定义,只能获取 time-profile的数据并自定义的展示出来。
time-profile 的 modeler的源码推测是在 此路径中
Xcode.app ▸ Contents ▸ Applications ▸ Instruments.app ▸ Contents ▸ Packages ▸ Sampling.instrdst ▸ Contents ▸ Extensions ▸ com.apple.dt.instruments.time-profiler.dtac
1、首先在自定义的 Instrument 中 引入 time-profile 所在的 package (sampling), SystemTrace是 另一个package.

系统的
schema和 package可以通过 Instrument的 preferences 查看 我们自定义的 package 也会在这里展示出来
2、通过查看time-profile 的配置xml 文件 ,可知time-profile中所有暴露出的所有 column ,所有的column都可以为我们所用
xml 文件所在路径 Xcode.app ▸ Contents ▸ Applications ▸ Instruments.app ▸ Contents ▸ Packages ▸ Sampling.instrdst ▸ Contents ▸ Extensions ▸ com.apple.dt.instruments.sampling.dtac
3、如此自定义展示需要的column
3.1 package中引入 time-profile
3.2 在Instrument 中 创建表格
3.3 可视化 time-profile 中的 column
备注: 在苹果的文档中透漏了一部分 time-profile 的 xml部分源码,但是time-profile 的具体 modeler的源码 可能是在 Sampling.instrdst 的 Extension 中.
3.4 代码。
<package>
<id>com.mm.uitest.DownloaderInstrument</id>
<title>DownloaderInstrument</title>
<owner>
<name>mumu</name>
</owner>
<import-schema>time-profile</import-schema>
<instrument>
<id>com.mm.uitest.DownloaderInstrument.downloader</id>
<title>Instruement-title</title>
<category>Behavior</category>
<purpose>Download-purpose</purpose>
<icon>Generic</icon>
<import-parameter>
<from-scope>trace</from-scope>
<name>?target-pid</name>
</import-parameter>
<create-parameter>
<name>?recordWaitingThreads</name>
<boolean-value>
<true-choice>Record Waiting Threads</true-choice>
</boolean-value>
</create-parameter>
<create-parameter>
<name>?highFreqSampling</name>
<boolean-value>
<true-choice>High Frequency</true-choice>
</boolean-value>
</create-parameter>
<create-parameter>
<name>?recordKernelStacks</name>
<boolean-value>
<true-choice>Record Kernel Call Stacks</true-choice>
</boolean-value>
</create-parameter>
<create-table>
<id>image-stack-table</id>
<schema-ref>time-profile</schema-ref>
<attribute>
<name>needs-kernel-callstack</name>
<parameter-ref>?recordKernelStacks</parameter-ref>
</attribute>
<attribute>
<name>high-frequency-sampling</name>
<parameter-ref>?highFreqSampling</parameter-ref>
</attribute>
<attribute>
<name>record-waiting-threads</name>
<parameter-ref>?recordWaitingThreads</parameter-ref>
</attribute>
<attribute>
<name>target-pid</name>
<parameter-ref>?target-pid</parameter-ref>
</attribute>
</create-table>
<list>
<title>image-stack-list</title>
<table-ref>image-stack-table</table-ref>
<column>time</column>
<column>stack</column>
<column>thread</column>
<column>process</column>
<column>weight</column>
<column>thread-state</column>
</list>
<calltree>
<title>DemoCallTree</title>
<table-ref>image-stack-table</table-ref>
<backtrace-column>stack</backtrace-column>
<thread-column>thread</thread-column>
<category-column>thread-state</category-column>
<weight-column>weight</weight-column>
</calltree>
</instrument>
可视化效果


后续深入:
1 time-profile如何获取数据源,modeler 绑定方法 ,定义规则
2 (time-profile是从kernel 获取堆栈信息) 比timer-profile更灵活的使用 kernel 中的 堆栈.
3 更细致的理解 package 的参数使用
难点: CLIPS 语法理解
和实际在 modeler 中的使用
参考
WWDC 2018:创建自定义的 Instrument
Xcode 中自定义 Instruments
WWDC 405_measuring_performance_using_logging
WWDC 410_creating_custom_instruments
WWDC 414_developing_a_great_profiling_experience
WWDC 418_using_time_profiler_in_instruments
WWDC 421_modeling_in_custom_instruments