Files
DKJone b2ddc1ad24 init project
添加初始项目
2019-01-23 11:38:18 +08:00

1132 lines
43 KiB
Objective-C

/*
* 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