0% found this document useful (0 votes)
17 views22 pages

Oc Method Trace

123

Uploaded by

sonnefishy
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views22 pages

Oc Method Trace

123

Uploaded by

sonnefishy
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 22

/*

* OCMethodTrace.m
* OCMethodTrace
*
* https://github.com/omxcodec/OCMethodTrace.git
*
* Copyright (C) 2018 Michael Chen <[email protected]>
*
* 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]];
if ([originClass isSubclassOfClass:NSBundle.class] &&
[NSStringFromSelector(originSelector) isEqualToString:@"dealloc"]) {
disableTrace = YES;
}
int deep = [self atomicAddDeep];

if (!disableTrace) {
if ([originClass isSubclassOfClass:NSBundle.class] &&
[NSStringFromSelector(originSelector) isEqualToString:@"dealloc"]) {
return;
}
OMTBlock *block = [self blockWithTarget:invocation.target];
OMTTracePosition postion = [self
tracePosition:NSStringFromSelector(originSelector)];
NSDate *start;

__weak id target = invocation.target;


// 1 原方法调用前回调
if (postion & OMTTracePositionBefore) {
[block runBefore: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];
if (target) {
[block runAfter: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

You might also like