DKWechatHelper/dkhelper/dkhelperDylib/Trace/OCMethodTrace.m
DKJone b2ddc1ad24 init project
添加初始项目
2019-01-23 11:38:18 +08:00

1132 lines
43 KiB
Objective-C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* OCMethodTrace.m
* OCMethodTrace
*
* https://github.com/omxcodec/OCMethodTrace.git
*
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "OCMethodTrace.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <execinfo.h>
#import <dlfcn.h>
#import <pthread.h>
#import "OCSelectorTrampolines.h"
#define OMT_LOG(_level_, _fmt_, ...) do { \
if ((_level_) <= [OCMethodTrace sharedInstance].logLevel) { \
if ([OCMethodTrace sharedInstance].delegate && [[OCMethodTrace sharedInstance].delegate respondsToSelector:@selector(log:format:)]) { \
[[OCMethodTrace sharedInstance].delegate log:OMTLogLevelDebug format:(_fmt_), ## __VA_ARGS__]; \
} else { \
NSLog((_fmt_), ## __VA_ARGS__); \
} \
} \
} while(0)
#define OMT_LOGE(_fmt_, ...) OMT_LOG(OMTLogLevelError, (_fmt_), ## __VA_ARGS__)
#define OMT_LOGD(_fmt_, ...) OMT_LOG(OMTLogLevelDebug, (_fmt_), ## __VA_ARGS__)
static NSString *const OMTMessageTempPrefix = @"__OMTMessageTemp_";
static NSString *const OMTMessageFinalPrefix = @"__OMTMessageFinal_";
static NSString *const OMTBlockRunBeforeSelector = @"runBefore:";
static NSString *const OMTBlockRunAfterSelector = @"runAfter:";
static NSString *const OMTDescriptionWithTargetSelector = @"descriptionWithTarget:";
// trace位置
typedef NS_ENUM(NSUInteger, OMTTracePosition) {
OMTTracePositionBefore = 1 << 0, // before
OMTTracePositionAfter = 1 << 1, // after
};
// 错误码,对内使用,便于调试
typedef NS_ENUM(NSUInteger, OMTErrorCode) {
OMTErrorNoError, // NERR 没有错误
OMTErrorSelectorPassThrough, // SPTH 透传不处理
OMTErrorSelectorAlreadyHooked, // SAHK 已经hook过
OMTErrorSelectorInUserBlacklist, // SIUB 在用户的黑名单里
OMTErrorClassInSelfBlacklist, // CISB 在内部的类黑名单里
OMTErrorSelectorInSelfBlacklist, // SISB 在内部的sel黑名单里
OMTErrorSelectorUnsuppotedMethodSignature, // SUMS 无法支持的方法签名(导致crash的异常签名)
OMTErrorSelectorUnsuppotedEncodeType, // SUEY 无法支持的编码类型(局部无法识别的签名)
OMTErrorDoesNotRespondToMethod, // NMTD 无此method
OMTErrorDoesNotRespondToSelector, // NSEL 无此sel
OMTErrorDoesNotRespondToIMP, // NIMP 无此IMP
OMTErrorSwizzleMethodFailed, // SMDF 替换方法失败
};
#define LOG_LEVEL_4_ERROR_CODE(_errorCode_) (((_errorCode_) >= OMTErrorSelectorUnsuppotedMethodSignature) ? OMTLogLevelError : OMTLogLevelDebug)
////////////////////////////////////////////////////////////////////////////////
#pragma mark - C Helper Define
// 替换实例方法
static void swizzle_instance_method(Class cls, SEL originSel, SEL newSel);
// 替换类方法
static void swizzle_class_method(Class cls, SEL originSel, SEL newSel);
// 是否支持某类型限定符
static BOOL omt_isTypeQualifier(const char argumentType);
// 是否是struct类型
static BOOL omt_isStructType(const char *argumentType);
// 获取struct类型名
static NSString *omt_structName(const char *argumentType);
// 是否是union类型
static BOOL omt_isUnionType(const char *argumentType);
// 获取union类型名
static NSString *omt_unionName(const char *argumentType);
static BOOL isCGRect (const char *type);
static BOOL isCGPoint (const char *type);
static BOOL isCGSize (const char *type);
static BOOL isCGVector (const char *type);
static BOOL isUIOffset (const char *type);
static BOOL isUIEdgeInsets (const char *type);
static BOOL isCGAffineTransform(const char *type);
////////////////////////////////////////////////////////////////////////////////
#pragma mark - Class Define
@interface OMTBlock : NSObject
@property (nonatomic, strong) NSString *className;
@property (nonatomic, copy) OMTConditionBlock condition;
@property (nonatomic, copy) OMTBeforeBlock before;
@property (nonatomic, copy) OMTAfterBlock after;
- (BOOL)runCondition:(SEL)sel;
- (void)runBefore:(id)target class:(Class)cls sel:(SEL)sel args:(NSArray *)args deep:(int)deep;
- (void)runAfter:(id)target class:(Class)cls sel:(SEL)sel ret:(id)ret deep:(int)deep interval:(NSTimeInterval)interval;
@end
@interface OMTMessageStub : NSObject
@property (nonatomic, unsafe_unretained) id target;
@property (nonatomic, assign) SEL selector;
- (instancetype)initWithTarget:(id)target selector:(SEL)aSelector;
@end
@interface NSObject (OCMethodTrace)
// 类转发方法
+ (id)omt_forwardingTargetForSelector:(SEL)aSelector;
// 实例转发方法
- (id)omt_forwardingTargetForSelector:(SEL)aSelector;
@end
@interface NSInvocation (OCMethodTrace)
// 获取原始的类名
- (Class)omt_originClass;
// 获取原始的方法名
- (SEL)omt_originSelector;
// 获取方法返回值
- (id)omt_getReturnValue;
// 获取方法参数
- (NSArray *)omt_getArguments;
@end
@interface OCMethodTrace() {
pthread_mutex_t _blockMutex;
pthread_mutex_t _deepMutex;
}
@property (nonatomic, strong) NSArray *defaultClassBlackList;
@property (nonatomic, strong) NSArray *defaultMethodBlackList;
@property (nonatomic, strong) NSDictionary *supportedTypeDict;
@property (nonatomic, strong) NSDictionary *tracePositionDict;
@property (nonatomic, strong) NSMutableDictionary *blockCache;
@property (nonatomic, assign) int deep;
// 初始化内部默认黑名单
- (void)initDefaultClassBlackList;
- (void)initDefaultMethodBlackList;
// 初始化所支持类型编码的字典
- (void)initSupportedTypeDict;
// 初始化trace位置字典
- (void)initTracePositionDict;
// 根据错误码返回错误描述
+ (NSString *)errorString:(OMTErrorCode)errorCode;
// 判断函数数组里任意函数是否存在递归循环调用
+ (BOOL)detectInfiniteLoopAtSelectorArray:(NSArray *)selectorArray;
// 获取target的description
- (NSString *)descriptionWithTarget:(id)target class:(Class)cls selector:(SEL)sel targetPosition:(OMTTargetPosition)targetPosition;
// 判断类是否在内部默认黑名单中
- (BOOL)isClassInBlackList:(NSString *)className;
// 判断方法是否在内部默认黑名单中
- (BOOL)isSelectorInBlackList:(NSString *)methodName;
// 判断类型编码是否可以处理
- (BOOL)isSupportedType:(NSString *)typeEncode;
// 获取方法名对应的trace位置
- (OMTTracePosition)tracePosition:(NSString *)methodName;
// block相关
- (void)setBlock:(OMTBlock *)block forKey:(NSString *)key;
- (OMTBlock *)blockforKey:(NSString *)key;
- (OMTBlock *)blockWithTarget:(id)target;
// 原子操作deep++,返回原值
- (int)atomicAddDeep;
// 原子操作deep--
- (void)atomicIncDeep;
// 根据条件跟踪目标类的方法
- (void)traceMethodWithClass:(Class)cls condition:(OMTConditionBlock)condition;
// 判断方法是否支持跟踪
- (OMTErrorCode)isTraceSupportedWithClass:(Class)cls method:(Method)method;
// 替换方法
- (OMTErrorCode)swizzleMethodWithClass:(Class)cls selector:(SEL)selector;
// 转发实现
- (void)omt_forwardInvocation:(NSInvocation *)invocation;
@end
////////////////////////////////////////////////////////////////////////////////
#pragma mark - C Helper
static void swizzle_instance_method(Class cls, SEL originSel, SEL newSel)
{
Method originMethod = class_getInstanceMethod(cls, originSel);
Method newMethod = class_getInstanceMethod(cls, newSel);
if (class_addMethod(cls, originSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
class_replaceMethod(cls, newSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
} else {
method_exchangeImplementations(originMethod, newMethod);
}
}
static void swizzle_class_method(Class cls, SEL originSel, SEL newSel)
{
Class metaClass = objc_getMetaClass([NSStringFromClass(cls) UTF8String]);
Method originMethod = class_getInstanceMethod(metaClass, originSel);
Method newMethod = class_getInstanceMethod(metaClass, newSel);
if (class_addMethod([metaClass class], originSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
class_replaceMethod([metaClass class], newSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
} else {
method_exchangeImplementations(originMethod, newMethod);
}
}
static BOOL omt_isTypeQualifier(const char argumentType)
{
// Table 6-2 Objective-C method encodings
static const char supportedTypeQualifierList[] = { 'r', 'n', 'N', 'o', 'O', 'R', 'V' };
static const size_t kNumSupportedTypeQualifier = sizeof(supportedTypeQualifierList) / sizeof(supportedTypeQualifierList[0]);
size_t i;
for (i = 0; i < kNumSupportedTypeQualifier; i++) {
if (argumentType == supportedTypeQualifierList[i]) {
return YES;
}
}
return NO;
}
static BOOL omt_isStructType(const char *argumentType)
{
NSString *typeString = [NSString stringWithUTF8String:argumentType];
return ([typeString hasPrefix:@"{"] && [typeString hasSuffix:@"}"]);
}
static NSString *omt_structName(const char *argumentType)
{
NSString *typeString = [NSString stringWithUTF8String:argumentType];
NSUInteger start = [typeString rangeOfString:@"{"].location;
NSUInteger end = [typeString rangeOfString:@"="].location;
if (end > start) {
return [typeString substringWithRange:NSMakeRange(start + 1, end - start - 1)];
} else {
return nil;
}
}
static BOOL omt_isUnionType(const char *argumentType)
{
NSString *typeString = [NSString stringWithUTF8String:argumentType];
return ([typeString hasPrefix:@"("] && [typeString hasSuffix:@")"]);
}
static NSString *omt_unionName(const char *argumentType)
{
NSString *typeString = [NSString stringWithUTF8String:argumentType];
NSUInteger start = [typeString rangeOfString:@"("].location;
NSUInteger end = [typeString rangeOfString:@"="].location;
if (end > start) {
return [typeString substringWithRange:NSMakeRange(start + 1, end - start - 1)];
} else {
return nil;
}
}
static BOOL isCGRect (const char *type) {return [omt_structName(type) isEqualToString:@"CGRect"];}
static BOOL isCGPoint (const char *type) {return [omt_structName(type) isEqualToString:@"CGPoint"];}
static BOOL isCGSize (const char *type) {return [omt_structName(type) isEqualToString:@"CGSize"];}
static BOOL isCGVector (const char *type) {return [omt_structName(type) isEqualToString:@"CGVector"];}
static BOOL isUIOffset (const char *type) {return [omt_structName(type) isEqualToString:@"UIOffset"];}
static BOOL isUIEdgeInsets (const char *type) {return [omt_structName(type) isEqualToString:@"UIEdgeInsets"];}
static BOOL isCGAffineTransform(const char *type) {return [omt_structName(type) isEqualToString:@"CGAffineTransform"];}
////////////////////////////////////////////////////////////////////////////////
#pragma mark - OCMethodTrace
@implementation OCMethodTrace
////////////////////////////////////////////////////////////////////////////////
#pragma mark - Public OCMethodTrace API
+ (OCMethodTrace *)sharedInstance
{
static OCMethodTrace *instance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
instance = [[OCMethodTrace alloc] init];
});
return instance;
}
- (void)traceMethodWithClass:(Class)cls
condition:(OMTConditionBlock)condition
before:(OMTBeforeBlock)before
after:(OMTAfterBlock)after
{
#ifndef DEBUG
return;
#endif
// 黑名单的类不跟踪
if ([self isClassInBlackList:NSStringFromClass(cls)]) {
OMT_LOG(OMTLogLevelDebug, @"[%@] trace class: %@",
[self.class errorString:OMTErrorClassInSelfBlacklist],
NSStringFromClass(cls));
return;
}
// 处理一个类被添加到blockCache多次的情况
if (cls && ![self blockforKey:NSStringFromClass(cls)]) {
OMTBlock *block = [[OMTBlock alloc] init];
block.className = NSStringFromClass(cls);
block.condition = condition;
block.before = before;
block.after = after;
[self setBlock:block forKey:block.className];
}
// 处理实例方法
[self traceMethodWithClass:cls condition:condition];
// 处理类方法
Class metaCls = object_getClass(cls);
if (class_isMetaClass(metaCls)) {
[self traceMethodWithClass:metaCls condition:condition];
}
}
////////////////////////////////////////////////////////////////////////////////
#pragma mark - Private OCMethodTrace API
- (instancetype)init
{
self = [super init];
if (self) {
// 参考ANYMethodLog的处理
[self initDefaultClassBlackList];
[self initDefaultMethodBlackList];
[self initSupportedTypeDict];
[self initTracePositionDict];
self.disableTrace = NO;
self.logLevel = OMTLogLevelDebug;
self.blockCache = [NSMutableDictionary dictionary];
self.deep = 0;
pthread_mutex_init(&_blockMutex, NULL);
pthread_mutex_init(&_deepMutex, NULL);
}
return self;
}
- (void)dealloc
{
pthread_mutex_destroy(&_blockMutex);
pthread_mutex_destroy(&_deepMutex);
}
- (void)initDefaultClassBlackList
{
self.defaultClassBlackList = @[@"OCMethodTrace",
@"OMTBlock",
@"OMTMessageStub",
@"NSMethodSignature",
@"NSInvocation",
];
}
- (void)initDefaultMethodBlackList
{
self.defaultMethodBlackList = @[@".cxx_destruct",
@"_isDeallocating",
@"release",
@"autorelease",
@"recycle",
@"retain",
@"Retain",
@"_tryRetain",
@"copy",
@"copyWithZone:",
@"mutableCopyWithZone:",
@"nsis_descriptionOfVariable:",
@"respondsToSelector:",
@"class",
@"methodSignatureForSelector:",
@"allowsWeakReference",
@"retainWeakReference",
@"forwardInvocation:",
@"description",
@"sharedInstance",
@"SharedInstance",
@"getInstance",
@"GetInstance",
];
}
- (void)initSupportedTypeDict
{
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1
// Table 6-1 Objective-C type encodings
self.supportedTypeDict = @{[NSString stringWithUTF8String:@encode(char)] : @"(char)",
[NSString stringWithUTF8String:@encode(int)] : @"(int)",
[NSString stringWithUTF8String:@encode(short)] : @"(short)",
[NSString stringWithUTF8String:@encode(long)] : @"(long)",
[NSString stringWithUTF8String:@encode(long long)] : @"(long long)",
[NSString stringWithUTF8String:@encode(unsigned char)] : @"(unsigned char))",
[NSString stringWithUTF8String:@encode(unsigned int)] : @"(unsigned int)",
[NSString stringWithUTF8String:@encode(unsigned short)] : @"(unsigned short)",
[NSString stringWithUTF8String:@encode(unsigned long)] : @"(unsigned long)",
[NSString stringWithUTF8String:@encode(unsigned long long)] : @"(unsigned long long)",
[NSString stringWithUTF8String:@encode(float)] : @"(float)",
[NSString stringWithUTF8String:@encode(double)] : @"(double)",
[NSString stringWithUTF8String:@encode(BOOL)] : @"(BOOL)",
[NSString stringWithUTF8String:@encode(void)] : @"(void)",
[NSString stringWithUTF8String:@encode(char *)] : @"(char *)",
[NSString stringWithUTF8String:@encode(id)] : @"(id)",
[NSString stringWithUTF8String:@encode(Class)] : @"(Class)",
[NSString stringWithUTF8String:@encode(SEL)] : @"(SEL)",
[NSString stringWithUTF8String:@encode(CGRect)] : @"(CGRect)",
[NSString stringWithUTF8String:@encode(CGPoint)] : @"(CGPoint)",
[NSString stringWithUTF8String:@encode(CGSize)] : @"(CGSize)",
[NSString stringWithUTF8String:@encode(CGVector)] : @"(CGVector)",
[NSString stringWithUTF8String:@encode(CGAffineTransform)] : @"(CGAffineTransform)",
[NSString stringWithUTF8String:@encode(UIOffset)] : @"(UIOffset)",
[NSString stringWithUTF8String:@encode(UIEdgeInsets)] : @"(UIEdgeInsets)",
@"@?" : @"(block)", // block类型
}; // 添加更多类型
}
- (void)initTracePositionDict
{
// @interface NSObject
self.tracePositionDict = @{@"initialize" : @(OMTTracePositionBefore | OMTTracePositionAfter),
@"init" : @(OMTTracePositionBefore | OMTTracePositionAfter),
@"new" : @(OMTTracePositionBefore | OMTTracePositionAfter),
@"allocWithZone:" : @(OMTTracePositionBefore | OMTTracePositionAfter),
@"alloc" : @(OMTTracePositionBefore | OMTTracePositionAfter),
@"dealloc" : @(OMTTracePositionBefore),
@"finalize" : @(OMTTracePositionBefore),
}; // 添加更多类型
}
+ (NSString *)errorString:(OMTErrorCode)errorCode
{
struct CodeToString {
OMTErrorCode errorCode;
const char *errorString;
};
static const struct CodeToString kCodeToString[] = {
{ OMTErrorNoError, "NERR" },
{ OMTErrorSelectorPassThrough, "SPTH" },
{ OMTErrorSelectorAlreadyHooked, "SAHK" },
{ OMTErrorSelectorInUserBlacklist, "SIUB" },
{ OMTErrorClassInSelfBlacklist, "CISB" },
{ OMTErrorSelectorInSelfBlacklist, "SISB" },
{ OMTErrorSelectorUnsuppotedMethodSignature,"SUMS" },
{ OMTErrorSelectorUnsuppotedEncodeType, "SUEY" },
{ OMTErrorDoesNotRespondToMethod, "NMTD" },
{ OMTErrorDoesNotRespondToSelector, "NSEL" },
{ OMTErrorDoesNotRespondToIMP, "NIMP" },
{ OMTErrorSwizzleMethodFailed, "SMDF" },
};
static const size_t kNumCodeToString = sizeof(kCodeToString) / sizeof(kCodeToString[0]);
size_t i;
for (i = 0; i < kNumCodeToString; i++) {
if (errorCode == kCodeToString[i].errorCode) {
break;
}
}
NSAssert(i != kNumCodeToString, @"Unknown errorCode???");
return [NSString stringWithUTF8String:kCodeToString[i].errorString];
}
+ (BOOL)detectInfiniteLoopAtSelectorArray:(NSArray *)selectorArray
{
void *callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
BOOL exists = NO;
// 跳过自身方法所以从1开始
for (int i = 1; i < frames && strs && !exists; i++) {
NSString *frame = [NSString stringWithUTF8String:strs[i]];
// OMT_LOGD(@"frame[%d]: %@", i, frame);
for (NSString *selector in selectorArray) {
if ([frame containsString:selector]) {
exists = YES;
break;
}
}
}
if (strs) {
free(strs);
}
return exists;
}
- (NSString *)descriptionWithTarget:(id)target class:(Class)cls selector:(SEL)sel targetPosition:(OMTTargetPosition)targetPosition
{
if (nil != self.delegate && [self.delegate respondsToSelector:@selector(descriptionWithTarget:class:selector:targetPosition:)]) {
return [self.delegate descriptionWithTarget:target class:cls selector:sel targetPosition:targetPosition];
} else {
return [target description];
}
}
- (BOOL)isClassInBlackList:(NSString *)className
{
return [self.defaultClassBlackList containsObject:className];
}
- (BOOL)isSelectorInBlackList:(NSString *)methodName
{
return [self.defaultMethodBlackList containsObject:methodName];
}
- (BOOL)isSupportedType:(NSString *)typeEncode
{
return [self.supportedTypeDict.allKeys containsObject:typeEncode];
}
- (OMTTracePosition)tracePosition:(NSString *)methodName
{
OMTTracePosition defaultPostion = OMTTracePositionBefore | OMTTracePositionAfter;
return [self.tracePositionDict.allKeys containsObject:methodName] ? [self.tracePositionDict[methodName] intValue]: defaultPostion;
}
- (void)setBlock:(OMTBlock *)block forKey:(NSString *)key
{
pthread_mutex_lock(&_blockMutex);
[self.blockCache setObject:block forKey:key];
pthread_mutex_unlock(&_blockMutex);
}
- (OMTBlock *)blockforKey:(NSString *)key
{
pthread_mutex_lock(&_blockMutex);
OMTBlock *block = [self.blockCache objectForKey:key];
pthread_mutex_unlock(&_blockMutex);
return block;
}
- (OMTBlock *)blockWithTarget:(id)target
{
pthread_mutex_lock(&_blockMutex);
Class cls = [target class];
OMTBlock *block = [self.blockCache objectForKey:NSStringFromClass(cls)];
while (nil == block) {
cls = [cls superclass];
if (nil == cls) {
break;
}
block = [self.blockCache objectForKey:NSStringFromClass(cls)];
}
pthread_mutex_unlock(&_blockMutex);
return block;
}
- (int)atomicAddDeep
{
pthread_mutex_lock(&_deepMutex);
int deep = _deep++;
pthread_mutex_unlock(&_deepMutex);
return deep;
}
- (void)atomicIncDeep
{
pthread_mutex_lock(&_deepMutex);
_deep--;
pthread_mutex_unlock(&_deepMutex);
}
- (void)traceMethodWithClass:(Class)cls condition:(OMTConditionBlock)condition
{
unsigned int outCount;
Method *methods = class_copyMethodList(cls, &outCount);
for (unsigned int i = 0; i < outCount; i++) {
Method method = *(methods + i);
SEL selector = method_getName(method);
OMTErrorCode errorCode = [self isTraceSupportedWithClass:cls method:method];
if (errorCode == OMTErrorNoError && condition) {
// 用户只有两种选择:跟踪,不跟踪
if (!condition(selector)) {
errorCode = OMTErrorSelectorInUserBlacklist;
}
}
if (errorCode == OMTErrorNoError) {
errorCode = [self swizzleMethodWithClass:cls selector:selector];
}
OMT_LOG(LOG_LEVEL_4_ERROR_CODE(errorCode), @"[%@] trace class: %@ method: %@ types: %s",
[self.class errorString:errorCode],
NSStringFromClass(cls),
NSStringFromSelector(selector),
method_getDescription(method)->types);
}
free(methods);
}
- (OMTErrorCode)isTraceSupportedWithClass:(Class)cls method:(Method)method
{
char returnTypeCString[1024];
memset(returnTypeCString, 0, sizeof(returnTypeCString));
method_getReturnType(method, returnTypeCString, sizeof(returnTypeCString));
const char *returnType = returnTypeCString;
NSString *selectorName = NSStringFromSelector(method_getName(method));
if ([selectorName hasPrefix:@"omt_"]) {
// 1 内部方法不处理
return OMTErrorSelectorPassThrough;
} else if ([selectorName hasPrefix:OMTMessageFinalPrefix]) {
// 2 处理过的方法不处理
return OMTErrorSelectorAlreadyHooked;
}
// 3 内部黑名单中的方法不处理
if ([self isSelectorInBlackList:selectorName]) {
return OMTErrorSelectorInSelfBlacklist;
}
// 4 签名异常的方法不处理。模拟器下有一些奇葩的方法(系统方法居多)签名异常,会导致[NSMethodSignature signatureWithObjCTypes:] crash所以需捕捉异常
BOOL hasSignatureException = NO;
@try {
__unused NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(method)];
} @catch (__unused NSException *e) {
hasSignatureException = YES;
}
if (hasSignatureException) {
return OMTErrorSelectorUnsuppotedMethodSignature;
}
// 5 处理返回值类型
// 跳过类型限定符
if (omt_isTypeQualifier(returnType[0])) {
returnType++;
}
// 支持指针类型
if (returnType[0] != _C_PTR) {
// struct和union有可能无法解析但是也通过解释不出的用NSValue输出便于打印函数调用链
if (!(omt_isStructType(returnType) || omt_isUnionType(returnType))) {
if (![self isSupportedType:[NSString stringWithUTF8String:returnType]]) {
return OMTErrorSelectorUnsuppotedEncodeType;
}
}
}
// 6 处理参数类型。需注意发送消息时第0个参数是self, 第1个参数是sel第2个参数才是真正意义上的第一个方法参数所以从2开始算
for (int k = 2 ; k < method_getNumberOfArguments(method); k++) {
char argumentTypeCString[1024];
memset(argumentTypeCString, 0, sizeof(argumentTypeCString));
method_getArgumentType(method, k, argumentTypeCString, sizeof(argumentTypeCString));
const char *argumentType = argumentTypeCString;
// 跳过类型限定符
if (omt_isTypeQualifier(argumentType[0])) {
argumentType++;
}
// 支持指针类型
if (argumentType[0] == _C_PTR) {
continue;
}
// struct和union有可能无法解析但是也通过解释不出的用NSValue输出便于打印函数调用链
if (!(omt_isStructType(argumentType) || omt_isUnionType(argumentType))) {
if (![self isSupportedType:[NSString stringWithUTF8String:argumentType]]) {
return OMTErrorSelectorUnsuppotedEncodeType;
}
}
}
return OMTErrorNoError;
}
- (OMTErrorCode)swizzleMethodWithClass:(Class)cls selector:(SEL)selector
{
// 1 检查该selector是否已经hook过
SEL newSelector = NSSelectorFromString([NSString stringWithFormat:@"%@%@->%@",
OMTMessageFinalPrefix,
NSStringFromClass(cls),
NSStringFromSelector(selector)]);
if (class_respondsToSelector(cls, newSelector)) {
return OMTErrorSelectorAlreadyHooked;
}
// 2 原方法相关校验
Method originMethod = class_getInstanceMethod(cls, selector);
if (!originMethod) {
return OMTErrorDoesNotRespondToMethod;
}
IMP originIMP = method_getImplementation(originMethod);
if (!originIMP) {
return OMTErrorDoesNotRespondToIMP;
}
const char *originTypes = method_getTypeEncoding(originMethod);
// 3 转发旧方法跳转到转发IMP
// 使用比较另类的"类与方法间隔符",如"->"。如果使用"_",有可能会导致冲突,如系统内部类"__CFNotification"就会异常
SEL forwardingSEL = NSSelectorFromString([NSString stringWithFormat:@"%@%@->%@",
OMTMessageTempPrefix,
NSStringFromClass(cls),
NSStringFromSelector(selector)]);
IMP forwardingIMP = imp_implementationWithSelector(forwardingSEL, originTypes);
NSAssert(originIMP != forwardingIMP, @"originIMP != forwardingIMP");
method_setImplementation(originMethod, forwardingIMP);
// 4 还原新SEL跳转回旧IMP
if (!class_addMethod(cls, newSelector, originIMP, originTypes)) {
return OMTErrorSwizzleMethodFailed;
}
return OMTErrorNoError;
}
- (void)omt_forwardInvocation:(NSInvocation *)invocation
{
if (self.disableTrace) {
[invocation invoke];
return;
}
Class originClass = [invocation omt_originClass];
SEL originSelector = [invocation omt_originSelector];
// XXX 通过堆栈搜索递归调用会有挺大的性能损耗。但确实不知道怎么处理比较好了解的麻烦知会email一下我
// 其实主要是因为description调用当遇到description则用加递归锁直接跳过runBefore和runAfer但是如果description内部有条件等待可能会死锁.
BOOL disableTrace = [[self class] detectInfiniteLoopAtSelectorArray:@[OMTBlockRunBeforeSelector,
OMTBlockRunAfterSelector,
OMTDescriptionWithTargetSelector]];
int deep = [self atomicAddDeep];
if (!disableTrace) {
OMTBlock *block = [self blockWithTarget:invocation.target];
OMTTracePosition postion = [self tracePosition:NSStringFromSelector(originSelector)];
NSDate *start;
// 1 原方法调用前回调
if (postion & OMTTracePositionBefore) {
[block runBefore:invocation.target class:originClass sel:originSelector args:[invocation omt_getArguments] deep:deep];
}
if (postion & OMTTracePositionAfter) {
start = [NSDate date];
}
// 2 调用原方法
[invocation invoke];
// 3 原方法调用后回调
if (postion & OMTTracePositionAfter) {
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:start];
[block runAfter:invocation.target class:originClass sel:originSelector ret:[invocation omt_getReturnValue] deep:deep interval:interval];
}
} else {
// 直接调用原方法
[invocation invoke];
}
[self atomicIncDeep];
}
@end
////////////////////////////////////////////////////////////////////////////////
#pragma mark - OMTBlock
@implementation OMTBlock
- (BOOL)runCondition:(SEL)sel
{
if (self.condition) {
return self.condition(sel);
} else {
return YES;
}
}
- (void)runBefore:(id)target class:(Class)cls sel:(SEL)sel args:(NSArray *)args deep:(int)deep
{
if (self.before) {
self.before(target, cls, sel, args, deep);
}
}
- (void)runAfter:(id)target class:(Class)cls sel:(SEL)sel ret:(id)ret deep:(int)deep interval:(NSTimeInterval)interval
{
if (self.after) {
self.after(target, cls, sel, ret, deep, interval);
}
}
@end
////////////////////////////////////////////////////////////////////////////////
#pragma mark - OMTMessageStub
@implementation OMTMessageStub
- (instancetype)initWithTarget:(id)target selector:(SEL)aSelector
{
self = [super init];
if (self) {
self.target = target;
NSString *finalSelectorName = [NSStringFromSelector(aSelector) stringByReplacingOccurrencesOfString:OMTMessageTempPrefix withString:OMTMessageFinalPrefix];
self.selector = NSSelectorFromString(finalSelectorName);
}
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
Method method = class_getInstanceMethod(object_getClass(self.target), self.selector);
if (NULL == method) {
OMT_LOGE(@"No Method, target: %@ selector: %@", self.target, NSStringFromSelector(self.selector));
assert(NULL != method);
}
const char *types = method_getTypeEncoding(method);
if (NULL == types) {
OMT_LOGE(@"No Types, target: %@ selector: %@", self.target, NSStringFromSelector(self.selector));
assert(NULL != types);
}
return [NSMethodSignature signatureWithObjCTypes:types];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
anInvocation.target = self.target;
anInvocation.selector = self.selector;
// 勿删,调试观察类名和方法名
__unused const char *className = [NSStringFromClass([self.target class]) UTF8String];
__unused const char *selectorName = [NSStringFromSelector(self.selector) UTF8String];
[[OCMethodTrace sharedInstance] omt_forwardInvocation:anInvocation];
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p target: %p selector: %@>",
NSStringFromClass([self class]), self, self.target, NSStringFromSelector(self.selector)];
}
@end
////////////////////////////////////////////////////////////////////////////////
#pragma mark - NSObject (OCMethodTrace)
@implementation NSObject (OCMethodTrace)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
swizzle_class_method([self class], @selector(forwardingTargetForSelector:), @selector(omt_forwardingTargetForSelector:));
swizzle_instance_method([self class], @selector(forwardingTargetForSelector:), @selector(omt_forwardingTargetForSelector:));
});
}
+ (id)omt_forwardingTargetForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) hasPrefix:OMTMessageTempPrefix] && ![self isKindOfClass:[OMTMessageStub class]]) {
return [[OMTMessageStub alloc] initWithTarget:self selector:aSelector];
}
return [self omt_forwardingTargetForSelector:aSelector];
}
- (id)omt_forwardingTargetForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) hasPrefix:OMTMessageTempPrefix] && ![self isKindOfClass:[OMTMessageStub class]]) {
return [[OMTMessageStub alloc] initWithTarget:self selector:aSelector];
}
return [self omt_forwardingTargetForSelector:aSelector];
}
@end
////////////////////////////////////////////////////////////////////////////////
#pragma mark - NSInvocation (OCMethodTrace)
@implementation NSInvocation (OCMethodTrace)
- (Class)omt_originClass
{
NSString *finalSelectorName = [NSStringFromSelector(self.selector) stringByReplacingOccurrencesOfString:OMTMessageFinalPrefix withString:@""];
NSUInteger location = [finalSelectorName rangeOfString:@"->"].location;
NSString *originClassName = [finalSelectorName substringWithRange:NSMakeRange(0, location)];
return NSClassFromString(originClassName);
}
- (SEL)omt_originSelector
{
NSString *finalSelectorName = [NSStringFromSelector(self.selector) stringByReplacingOccurrencesOfString:OMTMessageFinalPrefix withString:@""];
NSUInteger location = [finalSelectorName rangeOfString:@"->"].location;
NSString *originSelectorName = [finalSelectorName substringWithRange:NSMakeRange(location + 2, finalSelectorName.length - location - 2)];
return NSSelectorFromString(originSelectorName);
}
- (id)omt_getReturnValue
{
const char *returnType = self.methodSignature.methodReturnType;
id ret = nil;
// 跳过类型限定符
if (omt_isTypeQualifier(returnType[0])) {
returnType++;
}
// 基本类型
#define GET_RETURN_VALUE(_type) \
if (0 == strcmp(returnType, @encode(_type))) { \
_type ret_temp; \
[self getReturnValue:&ret_temp]; \
ret = @(ret_temp); \
}
// 结构体类型
#define GET_STRUCT_RETURN_VALUE(_type) \
if (is##_type(returnType)) { \
_type ret_temp; \
[self getReturnValue:&ret_temp]; \
ret = NSStringFrom##_type(ret_temp); \
}
if (omt_isStructType(returnType)) {
GET_STRUCT_RETURN_VALUE(CGRect)
else GET_STRUCT_RETURN_VALUE(CGPoint)
else GET_STRUCT_RETURN_VALUE(CGSize)
else GET_STRUCT_RETURN_VALUE(CGVector)
else GET_STRUCT_RETURN_VALUE(UIOffset)
else GET_STRUCT_RETURN_VALUE(UIEdgeInsets)
else GET_STRUCT_RETURN_VALUE(CGAffineTransform)
}
else if (omt_isUnionType(returnType)) {
// do nothing
}
else GET_RETURN_VALUE(char)
else GET_RETURN_VALUE(int)
else GET_RETURN_VALUE(short)
else GET_RETURN_VALUE(long)
else GET_RETURN_VALUE(long long)
else GET_RETURN_VALUE(unsigned char)
else GET_RETURN_VALUE(unsigned int)
else GET_RETURN_VALUE(unsigned short)
else GET_RETURN_VALUE(unsigned long)
else GET_RETURN_VALUE(unsigned long long)
else GET_RETURN_VALUE(float)
else GET_RETURN_VALUE(double)
else GET_RETURN_VALUE(BOOL)
else if (strcmp(returnType, @encode(void)) == 0) {
ret = @"void";
}
else if (0 == strcmp(returnType, @encode(char *))) {
char *ret_temp;
[self getReturnValue:&ret_temp];
ret = [NSString stringWithUTF8String:ret_temp ? ret_temp : "NULL"];
}
else if (0 == strcmp(returnType, @encode(id))) {
__unsafe_unretained id ret_temp;
[self getReturnValue:&ret_temp];
ret = [[OCMethodTrace sharedInstance] descriptionWithTarget:ret_temp class:[self omt_originClass] selector:[self omt_originSelector] targetPosition:OMTTargetPositionAfterReturnValue];
}
else if (0 == strcmp(returnType, @encode(Class))) {
Class ret_temp;
[self getReturnValue:&ret_temp];
ret = NSStringFromClass(ret_temp);
}
else if (0 == strcmp(returnType, @encode(SEL))) {
SEL ret_temp;
[self getReturnValue:&ret_temp];
ret = NSStringFromSelector(ret_temp);
}
else if (0 == strcmp(returnType, "@?")) { // block
// 则模仿lldb bt堆栈打印形式直接打印地址
void *ret_temp;
[self getReturnValue:&ret_temp];
ret = [NSString stringWithFormat:@"%p", ret_temp];
}
else if (returnType[0] == _C_PTR) {
void *ret_temp;
[self getReturnValue:&ret_temp];
if (0 == strcmp(returnType, @encode(CFStringRef))) {
ret = [NSString stringWithString:ret_temp ? (__bridge NSString *)ret_temp : @"NULL"]; // 深拷贝
}
if (nil == ret) {
// 模仿lldb bt堆栈打印形式直接打印地址
ret = [NSString stringWithFormat:@"%p", ret_temp];
}
}
if (nil == ret) {
NSUInteger valueSize = 0;
NSGetSizeAndAlignment(returnType, &valueSize, NULL);
unsigned char valueBytes[valueSize];
[self getReturnValue:valueBytes];
ret = [NSValue valueWithBytes:valueBytes objCType:returnType];
}
return ret;
}
- (NSArray *)omt_getArguments
{
NSMethodSignature *methodSignature = [self methodSignature];
NSMutableArray *argList = (methodSignature.numberOfArguments > 2 ? [NSMutableArray array] : nil);
for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) {
const char *argumentType = [methodSignature getArgumentTypeAtIndex:i];
id arg = nil;
// 跳过类型限定符
if (omt_isTypeQualifier(argumentType[0])) {
argumentType++;
}
// 基本类型
#define GET_ARGUMENT(_type) \
if (0 == strcmp(argumentType, @encode(_type))) { \
_type arg_temp; \
[self getArgument:&arg_temp atIndex:i]; \
arg = @(arg_temp); \
}
// 结构体类型
#define GET_STRUCT_ARGUMENT(_type) \
if (is##_type(argumentType)) { \
_type arg_temp; \
[self getArgument:&arg_temp atIndex:i]; \
arg = NSStringFrom##_type(arg_temp); \
}
if (omt_isStructType(argumentType)) {
GET_STRUCT_ARGUMENT(CGRect)
else GET_STRUCT_ARGUMENT(CGPoint)
else GET_STRUCT_ARGUMENT(CGSize)
else GET_STRUCT_ARGUMENT(CGVector)
else GET_STRUCT_ARGUMENT(UIOffset)
else GET_STRUCT_ARGUMENT(UIEdgeInsets)
else GET_STRUCT_ARGUMENT(CGAffineTransform)
}
else if (omt_isUnionType(argumentType)) {
// do nothing
}
else GET_ARGUMENT(char)
else GET_ARGUMENT(int)
else GET_ARGUMENT(short)
else GET_ARGUMENT(long)
else GET_ARGUMENT(long long)
else GET_ARGUMENT(unsigned char)
else GET_ARGUMENT(unsigned int)
else GET_ARGUMENT(unsigned short)
else GET_ARGUMENT(unsigned long)
else GET_ARGUMENT(unsigned long long)
else GET_ARGUMENT(float)
else GET_ARGUMENT(double)
else GET_ARGUMENT(BOOL)
else if (0 == strcmp(argumentType, @encode(char *))) {
char *arg_temp;
[self getArgument:&arg_temp atIndex:i];
arg = [NSString stringWithUTF8String:arg_temp ? arg_temp : "NULL"];
}
else if (0 == strcmp(argumentType, @encode(id))) {
__unsafe_unretained id arg_temp;
[self getArgument:&arg_temp atIndex:i];
arg = [[OCMethodTrace sharedInstance] descriptionWithTarget:arg_temp class:[arg_temp class] selector:[self omt_originSelector] targetPosition:OMTTargetPositionBeforeArgument];
}
else if (0 == strcmp(argumentType, @encode(Class))) {
Class arg_temp;
[self getArgument:&arg_temp atIndex:i];
arg = NSStringFromClass(arg_temp);
}
else if (0 == strcmp(argumentType, @encode(SEL))) {
SEL arg_temp;
[self getArgument:&arg_temp atIndex:i];
arg = NSStringFromSelector(arg_temp);
}
else if (0 == strcmp(argumentType, "@?")) { // block
// 则模仿lldb bt堆栈打印形式直接打印地址
void *arg_temp;
[self getArgument:&arg_temp atIndex:i];
arg = [NSString stringWithFormat:@"%p", arg_temp];
}
else if (argumentType[0] == _C_PTR) {
void *arg_temp;
[self getArgument:&arg_temp atIndex:i];
if (0 == strcmp(argumentType, @encode(CFStringRef))) {
arg = [NSString stringWithString:arg_temp ? (__bridge NSString *)arg_temp : @"NULL"]; // 深拷贝
}
if (nil == arg) {
// 则模仿lldb bt堆栈打印形式直接打印地址
arg = [NSString stringWithFormat:@"%p", arg_temp];
}
}
if (nil == arg) {
NSUInteger valueSize = 0;
NSGetSizeAndAlignment(argumentType, &valueSize, NULL);
unsigned char valueBytes[valueSize];
[self getArgument:valueBytes atIndex:i];
arg = [NSValue valueWithBytes:valueBytes objCType:argumentType];
}
[argList addObject:arg];
}
return argList;
}
@end