OCMethodTrace - Trace Any Objective-C Method Calls
===========================
跟踪打印Objective-C任何实例(类)方法

## 功能：
1. 支持任意OC实例(类)方法的跟踪打印
2. 支持多架构(arm32，arm64，i386，x86_64)
3. 支持跟踪父类和子类相同的方法
4. 支持各种灵活配置，包括方法黑白名单等

## 配置说明：
### 全局配置：
#### LogLevel：Trace日志级别，对应MDTraceLogLevel，必须定义，默认值：0
    0：MDTraceLogLeveError，错误级别
    1：MDTraceLogLeveDebug，调试级别

#### LogWhen：Trace日志输出时机，对应MDTraceLogWhen，必须定义，默认值：0
    0：MDTraceLogWhenStartup ，启动即输出日志
    1：MDTraceLogWhenVolume ，根据音量键控制输出日志(增加音量:输出日志;降低音量:关闭日志;默认时关闭日志)
    2：MDTraceLogWhenRegexString ，输出日志包含匹配字符串才输出日志
    
    LogRegexString：日志正则匹配字符串，仅当logWhen=MDTraceLogWhenRegexString有效。日志包含指定正则字符串才输出日志

#### TraceFlag：Trace控制位(尽量在该处扩展)，对应MDTraceFlag，全局必须定义，默认值：0x02，指定类可选定义，默认值：0x00
    0x01：MDTraceFlagDoesNotUseDescription，跳过调用对象description方法，避免不正规的description实现导致递归
    0x02：MDTraceFlagDumpClassListInfo，打印类列表信息，便于调试
    0x04：MDTraceFlagDumpClassMethod，打印某个类的方法(不包括父类方法)，便于调试
    0x08：MDTraceFlagDumpSuperClassMethod，打印某个类的父类方法(包括递归父类的方法)，便于调试

#### TraceObject：Trace对象，对应TraceObject，必须定义，默认值：1
    0：MDTraceObjectNone，屏蔽trace所有类
    1：MDTraceObjectCoreClass，trace引擎指定类的方法(仅测试验证使用)，仅需要考虑CORE_CLASS_LIST
    2：MDTraceObjectUserClass，trace用户指定类的方法，需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集"
    3：MDTraceObjectRegexClass，trace用户指定类 + 正则匹配类的方法，需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集" + "匹配ClassRegexString的CLASS_LIST和CORE_CLASS_LIST交集"
    
    MDTraceObjectUserClass和MDTraceObjectRegexClass的区别：
    * MDTraceObjectUserClass：在考虑USER_CLASS_LIST指定的类的时候，如果USER_CLASS_LIST和CORE_CLASS_LIST存在部分交集，交集对应的类需要合并，合并算法详见：+[MDTraceClassInfo mergeInfoWithCoreInfo:userInfo:userInfo:]
    * MDTraceObjectRegexClass：在考虑MDTraceObjectUserClass的基础上，再考虑ClassRegexString正则匹配的类。

    处理TraceObject的详细算法见：-[ MDMethodTrace parseClassListInfo]，算法内部注意优先级顺序：core > user > regex
    
### 指定类配置：
#### USER_CLASS_LIST：用户指定类列表，必须定义
    TraceMode： Trace模式，对应MDTraceMode，可选定义，默认值：1
        0：MDTraceModeOff，屏蔽trace方法
        1：MDTraceModeAll，trace所有方法
        2：MDTraceModeIncludeWhiteList，trace包含"白名单方法列表"的方法
        3：MDTraceModeExcludeBlackList，trace排除"黑名单方法列表"的方法
    TraceFlag：定义同全局配置，但是粒度更小，属于某个类范围内，可选定义，默认值：0x00
    MethodWhiteList：白名单方法列表，可选定义，但MDTraceModeIncludeWhiteList时必须定义
    MethodBlackList：黑名单方法列表，可选定义，但MDTraceModeExcludeBlackList时必须定义

#### CORE_CLASS_LIST：引擎指定类列表，必须定义
    TraceMode：定义同USER_CLASS_LIST的TraceMode，但是仅支持MDTraceModeOff和MDTraceModeExcludeBlackList
    TraceFlag：定义同全局配置，但是粒度更小，属于某个类范围内，可选定义，默认值：0x00
    MethodBlackList：黑名单方法列表，可选定义，但MDTraceModeExcludeBlackList时必须定义

#### ClassRegexString：类正则匹配字符串，仅TraceObject=MDTraceObjectRegexClass有效
    每个匹配的类使用默认类配置(可以理解成一个默认的User类)。使用场景：在不确定trace哪些类的情况下，批量trace一些有规律命名方式的类，匹配字符串如：“Notification|^CM|(F|f)igCaptureSource”


#### USER_CLASS_LIST和CORE_CLASS_LIST的关系：
    * CORE_CLASS_LIST：存在的意义在于，OCMethodTrace框架(引擎)内部也需要调用一些OC类方法，为了保证整个框架可以跑起来，避免trace到框架内部使用到的类导致的打印递归循环，所以，需排除这些指定类的全部方法或者部分方法。
    * USER_CLASS_LIST：用户自己指定的类，可以任意发挥。但是如果CORE_CLASS_LIST有相同的类，会优先CORE_CLASS_LIST中的配置。合并算法详见"+[MDTraceClassInfo mergeInfoWithCoreInfo:userInfo:userInfo:]"

## 技术要点：
### 支持跟踪父类和子类相同的方法  
    SatanWoo提出了一个方法(见感谢2)，使用“runtime的消息转发机制+跳板原理(桥)”实现，流程：
    每个方法置换到不同的IMP桥上 -> 从桥上反推出当前的调用关系（class和selector）-> 构造一个中间态新名字 -> forwardingTargetForSelector(self, 中间态新名字) 。
    但是SatanWoo的demo(见感谢3)有两个比较大的缺陷：
    * 仅支持arm64平台，不支持多架构；
    * 仅支持实例方法，不支持类方法。
    为此我做了一些改进，如下：
    * 支持多架构：参考objc4(见感谢4)的objc-block-trampolines.m，编写了一个通用的跳板实现，其中通过汇编支持多架构。
    * 支持类方法：NSObject (OCMethodTrace)需hook类方法"+[NSObject forwardingTargetForSelector:]"
### Trace输出无限循环递归的特殊处理
    递归调用都发生在"-[OCMethodTrace omt_forwardInvocation:]"函数内部，有两个地方：
    * 对象调用自身的description导致的无限递归：为了打印对象的描述，需要调用对象的description方法，而该方法内部很可能会调用对象的其它内部方法，继而递归又调用"-[OCMethodTrace omt_forwardInvocation:]"，于是无限循环。当前解决的方法是：在"-[OCMethodTrace omt_forwardInvocation:]"开始处通过遍历函数堆栈链查找"OMTBlockRunBeforeSelector"，"OMTBlockRunAfterSelector"和"OMTDescriptionWithTargetSelector"这几个方法，可以知道是否存在递归，发现递归就不要再调用runBefore和runAfter回调即可。缺点：遍历函数堆栈链的方法实在不优雅，而且性能也受影响，需要优化！
    * OCMethodTrace框架使用到的基础类导致的无限递归：比如"-[OCMethodTrace omt_forwardInvocation:]"函数范围内用到一些类：如NSDate，NSString或者runBefore或runAfter回调用到的方法等，一旦他们也被trace，也会导致循环递归。解决的思路是：把这些类放在黑名单里，不再trace。当然还有降低循环递归的方式是，内部实现尽量使用C或者C++实现。

## TODO：
1. 通过遍历堆栈链，判断是否存在循环递归调用的算法性能太差，需优化。
2. 开启全局dump类方法(MDTraceFlagDumpClassMethod)容易导致异常，不建议开启。

## 感谢：
1. https://github.com/qhd/ANYMethodLog
2. https://satanwoo.github.io/2017/09/24/mainthreadchecker1
3. https://github.com/SatanWoo/MainThreadChecker.git
4. https://opensource.apple.com/tarballs/objc4/objc4-723.tar.gz

欢迎提Issues和Pull requests，如有问题，可以联系 Michael Chen (omxcodec@gmail.com)
