├── README.md ├── cpapm.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── yangyouyong.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── yangyouyong.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── WellTangAPM.xcscheme │ ├── cpapm.xcscheme │ └── xcschememanagement.plist └── cpapm ├── APM ├── ANT │ ├── ANT.h │ ├── ANT.m │ ├── ANTPingThread.h │ └── ANTPingThread.m ├── CrashANT │ ├── CrashCatcher.h │ ├── CrashCatcher.m │ ├── NSObject+MethodSwizzleList.h │ ├── NSObject+MethodSwizzleList.m │ ├── OriginalObjectMethod+Swizzle.h │ ├── OriginalObjectMethod+Swizzle.m │ ├── OriginalObjectMethod.h │ ├── OriginalObjectMethod.m │ ├── fishhook.c │ ├── fishhook.h │ ├── method_check.c │ └── method_check.h ├── Dependency │ └── Backtrace │ │ ├── BSBacktraceLogger.h │ │ ├── BSBacktraceLogger.m │ │ ├── WTBacktrace.h │ │ └── WTBacktrace.m ├── Leaks │ ├── DetectorProtocol.h │ ├── NSObject+Detector.h │ ├── NSObject+Detector.m │ ├── UINavigationController+Detector.h │ ├── UINavigationController+Detector.m │ ├── UIView+Detector.h │ ├── UIView+Detector.m │ ├── UIViewController+Detector.h │ ├── UIViewController+Detector.m │ ├── WTLeaksDetector.h │ ├── WTLeaksDetector.m │ ├── WTObjectProxy.h │ └── WTObjectProxy.m └── Net │ ├── CFNet.h │ ├── CFNet.m │ ├── CFNetProxy.h │ ├── CFNetProxy.m │ ├── CFNetProxyModel.h │ ├── CFNetProxyModel.m │ ├── Hook_CFNetwork.h │ ├── Hook_CFNetwork.m │ ├── NetAnt.h │ ├── NetAnt.m │ ├── NetAntURLSessionConfiguration.h │ ├── NetAntURLSessionConfiguration.m │ ├── NetProtocol.h │ ├── NetProtocol.m │ └── SocketBasedRequest │ ├── CHttpRequest.c │ ├── CHttpRequest.h │ ├── HTTPRequest.hpp │ ├── XXNetwork.h │ └── XXNetwork.mm └── APP ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Crash ├── CrashViewController.h ├── CrashViewController.m ├── EVGView.h └── EVGView.m ├── Info.plist ├── Leaks ├── LeaksViewController.h └── LeaksViewController.m ├── Net ├── NetViewController.h └── NetViewController.m ├── Trace ├── TraceViewController.h └── TraceViewController.m ├── ViewController.h ├── ViewController.m └── main.mm /README.md: -------------------------------------------------------------------------------- 1 | ## iOS监控 2 | 3 | 1. [卡顿监控](#Blocked) 4 | 1. [内存泄露监控](#Leaks) 5 | 1. [网络监控](#Network) 6 | * [基于CFNetwork的监控](#fishhook) 7 | 1. [crash监控](#Crash) 8 | 1. [方法调换监控](#HookCheck) 9 | 10 | ### 卡顿监控 11 | 12 | > 通过分析栈调用情况自顶向下查找卡顿的方法调用 13 | 14 | ``` 15 | 2018-01-11 14:29:12.493901+0800 cpapm[16525:25693273] ----mainTrace---- 16 | 2018-01-11 14:29:12.494088+0800 cpapm[16525:25693273] Backtrace of Thread 771: 17 | libsystem_platform.dylib 0x114e71f49 _platform_memmove$VARIANT$Haswell + 41 18 | CoreFoundation 0x110f2b3d0 _CFStringCheckAndGetCharacters + 240 19 | CoreFoundation 0x110f2b282 -[__NSCFString getCharacters:range:] + 34 20 | Foundation 0x11007fd38 _NSNewStringByAppendingStrings + 1071 21 | Foundation 0x1100a2b2f -[NSString stringByAppendingString:] + 185 22 | cpapm 0x10fd734b0 -[TraceViewController blockFoo] + 128 23 | cpapm 0x10fd733f1 -[TraceViewController ANTTask:] + 257 24 | UIKit 0x111488972 -[UIApplication sendAction:to:from:forEvent:] + 83 25 | UIKit 0x111607c3c -[UIControl sendAction:to:forEvent:] + 67 26 | UIKit 0x111607f59 -[UIControl _sendActionsForEvents:withEvent:] + 450 27 | UIKit 0x111606e86 -[UIControl touchesEnded:withEvent:] + 618 28 | UIKit 0x1114fe807 -[UIWindow _sendTouchesForEvent:] + 2807 29 | 30 | ``` 31 | 32 | ### 内存泄露监控 33 | 34 | > 查看内存泄露的日志, 定位到具体的类. 可加入FBRetainCycle 找到具体引用关系 35 | 36 | ``` 37 | 2018-01-11 14:32:33.153020+0800 cpapm[16525:25692073] *** is still alive *** 38 | 2018-01-11 14:32:33.153255+0800 cpapm[16525:25692073] 39 | 40 | Detect Possible Controller Leak: LeaksViewController 41 | 42 | ``` 43 | 44 | ### 网络监控 45 | 46 | > 通过设置代理 拿到每次网络请求的入参及返回值 做一些事情(如网络本地缓存, 网络调试等, 网络性能监控等) 47 | 48 | ``` 49 | // catch any network based on NSURLProtocol 50 | -(void)netAntDidCatchRequest:(NSURLRequest *)request response:(NSURLResponse *)response data:(NSData *)data { 51 | id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; 52 | if (obj == nil) { 53 | obj = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 54 | } 55 | NSLog(@"catch_request:%@\nresponse:%@,\ndata:%@",request.URL.absoluteString, response.URL.absoluteString, obj); 56 | } 57 | 58 | ``` 59 | 60 | #### 基于CFNetwork的监控 61 | 62 | > 拦截CFNetwork 下的网络请求. 校验DNS拦截, 请求信息, 网络性能, 响应时间等信息. 63 | > 64 | > 暂时未做响应body的拦截 65 | 66 | ``` 67 | void hooked_proxyCallBack(CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo){ 68 | 69 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPProxy)); 70 | NSString *requestId = proxyInfo[CFRequest_KEY]; 71 | NSMutableDictionary *proxyCallBackMap = [CFNetProxy sharedInstance].requestMap; 72 | 73 | CFReadStreamClientCallBack original_callback = NULL; 74 | if (clientCallBackInfo != NULL) { 75 | CFNetProxyModel *proxy = proxyCallBackMap[requestId]; 76 | if (proxy) { 77 | original_callback = [proxy.callbackPointer pointerValue]; 78 | }else{ 79 | NSLog(@"missed_data"); 80 | } 81 | } 82 | 83 | CFReadStreamRef readStream = stream; 84 | 85 | if(type == kCFStreamEventHasBytesAvailable){ 86 | 87 | CFNetProxyModel *proxy = proxyCallBackMap[requestId]; 88 | if (proxy) { 89 | // proxy receive data 90 | /** 91 | UInt8 buff[255]; 92 | long length = CFReadStreamRead(stream, buff, 255); 93 | NSMutableData *mutiData = proxy.bufferData; 94 | if(!mutiData){ 95 | mutiData = [[NSMutableData alloc] init]; 96 | } 97 | [mutiData appendBytes:buff length:length]; 98 | 99 | proxy.bufferData = mutiData; 100 | */ 101 | } 102 | 103 | }else if(type == kCFStreamEventEndEncountered){ 104 | 105 | // end data 106 | CFNetProxyModel *proxy = proxyCallBackMap[requestId]; 107 | if (proxy) { 108 | proxy.responseData = proxy.bufferData; 109 | } 110 | NSLog(@"proxy complete"); 111 | } 112 | 113 | if (original_callback != NULL) { 114 | original_callback(readStream, type, clientCallBackInfo); 115 | } 116 | } 117 | 118 | ``` 119 | 120 | 121 | ### crash监控 122 | 123 | > 通过拦截NSExecption对象 和拦截single 对象捕获crash 做crash 上报等事情. 124 | 125 | ``` 126 | / you can report logs here or store them 127 | -(void)logLocalException:(NSException *)exception { 128 | 129 | NSArray *stackArray = [CrashCatcher backtrace]; 130 | NSString *reason = [exception reason]; 131 | NSString *name = [exception name]; 132 | NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@\nUserInfo:%@\n",name, reason, stackArray,exception.userInfo]; 133 | 134 | NSString *logFilePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject stringByAppendingPathComponent:@"debug_exception_log.txt"]; 135 | NSFileManager *manager = [NSFileManager defaultManager]; 136 | if(![manager fileExistsAtPath:logFilePath]){ 137 | [manager createFileAtPath:logFilePath contents:[exceptionInfo dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; 138 | }else{ 139 | NSData *data = [NSData dataWithContentsOfFile:logFilePath]; 140 | NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 141 | contents = [contents stringByAppendingString:exceptionInfo]; 142 | [contents writeToFile:logFilePath atomically:true encoding:NSUTF8StringEncoding error:nil]; 143 | } 144 | } 145 | 146 | ``` 147 | 148 | ### 方法调换监控 149 | 150 | > 通过C方法查找到当前类哪些方法被hook掉或者被category重写了. 用于hook系统方法前的检测 151 | 152 | ``` 153 | 154 | 2018-01-11 14:40:35.325799+0800 cpapm[16525:25692073] swizzledInfo:( 155 | { 156 | originalName = Small; 157 | swizzledMethodName = "-[OriginalObjectMethod(Swizzle) swizzle_Small]"; 158 | }, 159 | { 160 | originalName = Foo; 161 | swizzledMethodName = "-[OriginalObjectMethod(Swizzle) swizzle_Foo]"; 162 | }, 163 | { 164 | originalName = write; 165 | swizzledMethodName = "-[OriginalObjectMethod(Swizzle) write]"; 166 | }, 167 | { 168 | originalName = "swizzle_Small"; 169 | swizzledMethodName = "-[OriginalObjectMethod Small]"; 170 | }, 171 | { 172 | originalName = "swizzle_Foo"; 173 | swizzledMethodName = "-[OriginalObjectMethod Foo]"; 174 | } 175 | ) 176 | 177 | ``` 178 | 179 | 180 | 181 | TODO: 182 | > fishhook 监控基于CFNetwork的网络请求 183 | 184 | > CPU爆表 & 内存爆表监控 185 | 186 | > PLeakSniffer 再造 187 | 188 | > FileManager 189 | 190 | > ... -------------------------------------------------------------------------------- /cpapm.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /cpapm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /cpapm.xcodeproj/project.xcworkspace/xcuserdata/yangyouyong.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YangYouYong/cpapm/16001e684aa386ec77c36038acbcffb832fba18d/cpapm.xcodeproj/project.xcworkspace/xcuserdata/yangyouyong.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /cpapm.xcodeproj/xcuserdata/yangyouyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 22 | 23 | 24 | 26 | 39 | 40 | 41 | 43 | 56 | 57 | 58 | 60 | 73 | 74 | 75 | 77 | 90 | 91 | 92 | 94 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /cpapm.xcodeproj/xcuserdata/yangyouyong.xcuserdatad/xcschemes/WellTangAPM.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 76 | 78 | 84 | 85 | 86 | 87 | 88 | 89 | 95 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /cpapm.xcodeproj/xcuserdata/yangyouyong.xcuserdatad/xcschemes/cpapm.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 76 | 78 | 84 | 85 | 86 | 87 | 88 | 89 | 95 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /cpapm.xcodeproj/xcuserdata/yangyouyong.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WellTangAPM.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | cpapm.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | C06A5DA01FF4881E0013FB75 21 | 22 | primary 23 | 24 | 25 | C06A5DB81FF4881F0013FB75 26 | 27 | primary 28 | 29 | 30 | C06A5DC31FF4881F0013FB75 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /cpapm/APM/ANT/ANT.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANT.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ANT; 12 | @protocol ANTDelegate 13 | 14 | -(void)ANT:(ANT *)ant 15 | catchWithThreadHold:(double)threadHold 16 | mainThreadBacktrace:(NSString *)mainBacktrace 17 | allThreadBacktrace:(NSString *)allBacktrace; 18 | 19 | @end 20 | 21 | @interface ANT : NSObject 22 | 23 | @property (nonatomic, assign) id delegate; 24 | @property (nonatomic, assign, readonly) BOOL isOpen; 25 | 26 | -(void)openWithThreadhold:(double)threadHold; 27 | -(void)close; 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /cpapm/APM/ANT/ANT.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANT.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "ANT.h" 10 | #import "WTBacktrace.h" 11 | #import "ANTPingThread.h" 12 | 13 | @interface ANT() 14 | 15 | @property (nonatomic, strong) ANTPingThread *pingThread; 16 | 17 | @end 18 | 19 | @implementation ANT 20 | 21 | -(BOOL)isOpen { 22 | return !self.pingThread.isCancelled; 23 | } 24 | 25 | -(void)openWithThreadhold:(double)threadHold { 26 | 27 | self.pingThread = [ANTPingThread new]; 28 | __weak typeof(self) weakSelf = self; 29 | [self.pingThread startWithThreadHold:threadHold completion:^{ 30 | NSString *main = [WTBacktrace mainThread]; 31 | NSString *all = [WTBacktrace allThread]; 32 | if (!weakSelf) { 33 | return ; 34 | } 35 | [weakSelf.delegate ANT:weakSelf 36 | catchWithThreadHold:threadHold 37 | mainThreadBacktrace:main 38 | allThreadBacktrace:all]; 39 | }]; 40 | } 41 | 42 | -(void)close { 43 | [self.pingThread cancel]; 44 | } 45 | 46 | -(void)dealloc { 47 | [self.pingThread cancel]; 48 | } 49 | 50 | @end 51 | 52 | -------------------------------------------------------------------------------- /cpapm/APM/ANT/ANTPingThread.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANTPingThread.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^ANTPingThreadCallBack)(void); 12 | 13 | @interface ANTPingThread : NSThread 14 | 15 | -(void)startWithThreadHold:(double)threadHold 16 | completion:(ANTPingThreadCallBack)completionBlock; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /cpapm/APM/ANT/ANTPingThread.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANTPingThread.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "ANTPingThread.h" 10 | 11 | @interface ANTPingThread() 12 | 13 | @property (nonatomic, copy) ANTPingThreadCallBack completion; 14 | @property (nonatomic, assign) double threadHold; 15 | @property (nonatomic, strong) dispatch_semaphore_t semaphore; 16 | @property (nonatomic, assign) BOOL isMainThreadBlock; 17 | 18 | @end 19 | 20 | @implementation ANTPingThread 21 | 22 | -(instancetype)init { 23 | if (self = [super init]) { 24 | self.semaphore = dispatch_semaphore_create(0); 25 | self.threadHold = 0.4; 26 | } 27 | return self; 28 | } 29 | 30 | -(void)startWithThreadHold:(double)threadHold completion:(ANTPingThreadCallBack)completionBlock { 31 | self.threadHold = threadHold; 32 | self.completion = completionBlock; 33 | [self start]; 34 | } 35 | 36 | -(void)main { 37 | 38 | while (self.isCancelled == false) { 39 | self.isMainThreadBlock = true; 40 | dispatch_async(dispatch_get_main_queue(), ^{ 41 | self.isMainThreadBlock = false; 42 | dispatch_semaphore_signal(self.semaphore); 43 | }); 44 | 45 | [NSThread sleepForTimeInterval:self.threadHold]; 46 | if (self.isMainThreadBlock) { 47 | if (self.completion) { 48 | self.completion(); 49 | } 50 | } 51 | 52 | dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); 53 | } 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/CrashCatcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // CrashCatcher.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/3. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CrashCatcher : NSObject 12 | 13 | +(instancetype)sharedInstance; 14 | 15 | +(NSArray *)backtrace; // return call stacks 16 | 17 | -(void)setOpen:(BOOL)open; 18 | 19 | -(NSString *)nameOfSignal:(int)signalType; 20 | 21 | -(void)killApp; 22 | 23 | void catchCrash(NSException *exception); 24 | 25 | /////////////////// signal type and funcs ////////////////// 26 | 27 | void receiveSignal(int signalType); 28 | 29 | //////////////////////////////////////////////////////////// 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/CrashCatcher.m: -------------------------------------------------------------------------------- 1 | // 2 | // CrashCatcher.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/3. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #include 10 | #import "CrashCatcher.h" 11 | #include 12 | #include 13 | 14 | //signal信号名 15 | NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName"; 16 | NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey"; 17 | NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey"; 18 | 19 | volatile int32_t UncaughtExceptionCount = 0; 20 | const int32_t UncaughtExceptionMaximum = 10; //表示最多只截获10次异常,如果超过十次则不截获弹出alter了直接崩溃 21 | 22 | const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4; 23 | const NSInteger UncaughtExceptionHandlerReportAddressCount = 13; 24 | 25 | @interface CrashCatcher() 26 | 27 | @property (nonatomic, assign) NSTimeInterval execptionTime; 28 | 29 | @end 30 | 31 | @implementation CrashCatcher 32 | 33 | +(instancetype)sharedInstance { 34 | static CrashCatcher *sharedInstance = nil; 35 | static dispatch_once_t onceToken; 36 | dispatch_once(&onceToken, ^{ 37 | sharedInstance = [[CrashCatcher alloc] init]; 38 | sharedInstance.execptionTime = 30; 39 | }); 40 | 41 | return sharedInstance; 42 | } 43 | 44 | + (NSArray *)backtrace 45 | { 46 | void* callstack[128]; 47 | int frames = backtrace(callstack, 128); 48 | char **strs = backtrace_symbols(callstack, frames); 49 | 50 | int i; 51 | NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames]; 52 | for ( 53 | i = UncaughtExceptionHandlerSkipAddressCount; 54 | i < UncaughtExceptionHandlerSkipAddressCount + 55 | UncaughtExceptionHandlerReportAddressCount; 56 | i++) 57 | { 58 | [backtrace addObject:[NSString stringWithUTF8String:strs[i]]]; 59 | } 60 | free(strs); 61 | 62 | return backtrace; 63 | } 64 | 65 | -(void)setOpen:(BOOL)open { 66 | if (open) { 67 | NSSetUncaughtExceptionHandler(&catchCrash); 68 | setCrashSignalHandler(); 69 | } 70 | } 71 | 72 | - (void)handleException:(NSException *)exception{ 73 | 74 | CFRunLoopRef runLoop = CFRunLoopGetCurrent(); 75 | CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); 76 | 77 | 78 | while (self.execptionTime > 0) 79 | { 80 | for (NSString *mode in (__bridge NSArray *)allModes) 81 | { 82 | //为阻止线程退出,使用 CFRunLoopRunInMode(model, 0.001, false)等待系统消息,false表示RunLoop没有超时时间 83 | CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); 84 | self.execptionTime -= 0.001; 85 | } 86 | } 87 | 88 | CFRelease(allModes); 89 | 90 | NSSetUncaughtExceptionHandler(NULL); 91 | signal(SIGABRT, SIG_DFL); 92 | signal(SIGILL, SIG_DFL); 93 | signal(SIGSEGV, SIG_DFL); 94 | signal(SIGFPE, SIG_DFL); 95 | signal(SIGBUS, SIG_DFL); 96 | signal(SIGPIPE, SIG_DFL); 97 | 98 | NSLog(@"%@",[exception name]); 99 | [self logLocalException:exception]; 100 | 101 | if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName]) 102 | { 103 | kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]); 104 | } 105 | else 106 | { 107 | [exception raise]; 108 | } 109 | } 110 | 111 | // you can report logs here or store them 112 | -(void)logLocalException:(NSException *)exception { 113 | 114 | NSArray *stackArray = [CrashCatcher backtrace]; 115 | NSString *reason = [exception reason]; 116 | NSString *name = [exception name]; 117 | NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@\nUserInfo:%@\n",name, reason, stackArray,exception.userInfo]; 118 | 119 | NSString *logFilePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject stringByAppendingPathComponent:@"debug_exception_log.txt"]; 120 | NSFileManager *manager = [NSFileManager defaultManager]; 121 | if(![manager fileExistsAtPath:logFilePath]){ 122 | [manager createFileAtPath:logFilePath contents:[exceptionInfo dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; 123 | }else{ 124 | NSData *data = [NSData dataWithContentsOfFile:logFilePath]; 125 | NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 126 | contents = [contents stringByAppendingString:exceptionInfo]; 127 | [contents writeToFile:logFilePath atomically:true encoding:NSUTF8StringEncoding error:nil]; 128 | } 129 | } 130 | 131 | // stack crash 132 | void catchCrash(NSException *exception) { 133 | 134 | //递增一个全局计数器 135 | int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); 136 | if (exceptionCount > UncaughtExceptionMaximum) 137 | { 138 | return; 139 | } 140 | 141 | //渠道回溯的堆栈 142 | NSArray *callStack = [CrashCatcher backtrace]; 143 | NSMutableDictionary *userInfo = 144 | [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]]; 145 | 146 | //把堆栈信息存入字典中 147 | [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey]; 148 | 149 | [[CrashCatcher sharedInstance] 150 | performSelectorOnMainThread:@selector(handleException:) 151 | withObject: 152 | [NSException 153 | exceptionWithName:[exception name] 154 | reason:[exception reason] 155 | userInfo:userInfo] 156 | waitUntilDone:YES]; 157 | } 158 | 159 | // C or C++ API crash signal 160 | void setCrashSignalHandler() { 161 | signal(SIGABRT, receiveSignal); 162 | signal(SIGILL, receiveSignal); 163 | signal(SIGSEGV, receiveSignal); 164 | signal(SIGFPE, receiveSignal); 165 | signal(SIGBUS, receiveSignal); 166 | signal(SIGPIPE, receiveSignal); 167 | } 168 | 169 | void receiveSignal(int signalType) { 170 | 171 | NSString *reason = [NSString stringWithFormat:@"Signal name:%@ \n was raised",[[CrashCatcher sharedInstance] nameOfSignal: signalType]]; 172 | 173 | int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); 174 | if (exceptionCount > UncaughtExceptionMaximum) 175 | { 176 | return; 177 | } 178 | 179 | NSMutableDictionary *userInfo = @{UncaughtExceptionHandlerSignalKey: @(signalType)}.mutableCopy; 180 | 181 | [userInfo 182 | setObject:[CrashCatcher backtrace] 183 | forKey:UncaughtExceptionHandlerAddressesKey]; 184 | 185 | [[CrashCatcher sharedInstance] 186 | performSelectorOnMainThread:@selector(handleException:) 187 | withObject: 188 | [NSException 189 | exceptionWithName:UncaughtExceptionHandlerSignalExceptionName 190 | reason:reason 191 | userInfo:@{UncaughtExceptionHandlerSignalKey: @(signalType)}] 192 | waitUntilDone:YES]; 193 | 194 | [[CrashCatcher sharedInstance] killApp]; 195 | } 196 | 197 | -(NSString *)nameOfSignal:(int)signalType { 198 | switch (signalType) { 199 | case SIGABRT: 200 | return @"SIGABRT"; 201 | break; 202 | case SIGILL: 203 | return @"SIGILL"; 204 | break; 205 | case SIGSEGV: 206 | return @"SIGSEGV"; 207 | break; 208 | case SIGFPE: 209 | return @"SIGFPE"; 210 | break; 211 | case SIGBUS: 212 | return @"SIGBUS"; 213 | break; 214 | case SIGPIPE: 215 | return @"SIGPIPE"; 216 | break; 217 | default: 218 | return @"OTHER"; 219 | break; 220 | } 221 | } 222 | 223 | -(void)killApp { 224 | 225 | NSSetUncaughtExceptionHandler(nil); 226 | 227 | signal(SIGABRT, SIG_DFL); 228 | signal(SIGILL, SIG_DFL); 229 | signal(SIGSEGV, SIG_DFL); 230 | signal(SIGFPE, SIG_DFL); 231 | signal(SIGBUS, SIG_DFL); 232 | signal(SIGPIPE, SIG_DFL); 233 | 234 | kill(getpid(), SIGKILL); 235 | } 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/NSObject+MethodSwizzleList.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MethodSwizzleList.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/29. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (MethodSwizzleList) 12 | 13 | @property (nonatomic, strong) NSArray *swizzleMethodList; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/NSObject+MethodSwizzleList.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MethodSwizzleList.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/29. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MethodSwizzleList.h" 10 | #import 11 | 12 | static const void *methodListKey = &methodListKey; 13 | 14 | @implementation NSObject (MethodSwizzleList) 15 | 16 | -(NSArray *)swizzleMethodList { 17 | return objc_getAssociatedObject(self, methodListKey); 18 | } 19 | 20 | -(void)setSwizzleMethodList:(NSArray *)swizzleMethodList { 21 | objc_setAssociatedObject(self, methodListKey, swizzleMethodList, OBJC_ASSOCIATION_RETAIN); 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/OriginalObjectMethod+Swizzle.h: -------------------------------------------------------------------------------- 1 | // 2 | // OriginalObjectMethod+Swizzle.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "OriginalObjectMethod.h" 10 | 11 | @interface OriginalObjectMethod (Swizzle) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/OriginalObjectMethod+Swizzle.m: -------------------------------------------------------------------------------- 1 | // 2 | // OriginalObjectMethod+Swizzle.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "OriginalObjectMethod+Swizzle.h" 10 | #import 11 | 12 | @implementation OriginalObjectMethod (Swizzle) 13 | 14 | +(void)load { 15 | Method originalMethod = class_getInstanceMethod(self, @selector(Foo)); 16 | Method swizzledMethod = class_getInstanceMethod(self, @selector(swizzle_Foo)); 17 | method_exchangeImplementations(originalMethod, swizzledMethod); 18 | 19 | originalMethod = class_getInstanceMethod(self, @selector(Small)); 20 | swizzledMethod = class_getInstanceMethod(self, @selector(swizzle_Small)); 21 | method_exchangeImplementations(originalMethod, swizzledMethod); 22 | } 23 | 24 | -(void)swizzle_Foo { 25 | 26 | NSLog(@"swizzle func swizzle_Foo"); 27 | [self swizzle_Foo]; 28 | } 29 | 30 | -(void)swizzle_Small { 31 | NSLog(@"swizzle func swizzle_Small"); 32 | [self swizzle_Small]; 33 | } 34 | 35 | -(void)write { 36 | NSLog(@"method override in category"); 37 | } 38 | 39 | +(void)install { 40 | NSLog(@"override in category"); 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/OriginalObjectMethod.h: -------------------------------------------------------------------------------- 1 | // 2 | // OriginalObjectMethod.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OriginalObjectMethod : NSObject 12 | 13 | -(void)Foo; 14 | 15 | -(void)Small; 16 | 17 | -(void)checkSwizzledMethod; 18 | 19 | +(void)install; 20 | 21 | -(void)write; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/OriginalObjectMethod.m: -------------------------------------------------------------------------------- 1 | // 2 | // OriginalObjectMethod.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "OriginalObjectMethod.h" 10 | #import 11 | #import "method_check.h" 12 | #import "NSObject+MethodSwizzleList.h" 13 | 14 | @implementation OriginalObjectMethod 15 | 16 | +(void)install { 17 | NSLog(@"class func :install"); 18 | } 19 | 20 | -(void)Foo { 21 | NSLog(@"original func Foo:"); 22 | } 23 | 24 | -(void)Small { 25 | NSLog(@"this is small func"); 26 | } 27 | 28 | -(void)write { 29 | NSLog(@"original method write"); 30 | } 31 | 32 | -(void)checkSwizzledMethod { 33 | // check method list find out has hooked method 34 | 35 | NSMutableArray *swizzleMethodListArray = @[].mutableCopy; 36 | unsigned int methodCount = 0; 37 | Method *methodList = class_copyMethodList([self class], &methodCount); 38 | 39 | if (methodList) { 40 | 41 | NSLog(@"methodCount:%d",methodCount); 42 | 43 | const char *originalName = NULL; 44 | const char *swizzledMethodList = NULL; 45 | unsigned int swizzledCount = 0; 46 | BOOL validate = validate_methods(NSStringFromClass([self class]).UTF8String, &swizzledMethodList, &originalName, &swizzledCount); 47 | 48 | if (swizzledCount > 0 || !validate) { 49 | if (swizzledMethodList == NULL || originalName == NULL) { 50 | NSLog(@"swizzleNameList:%s,originalNameList:%s",swizzledMethodList, originalName); 51 | return ; 52 | } 53 | NSString *swizzledNameList = [[NSString stringWithUTF8String:swizzledMethodList] stringByReplacingOccurrencesOfString:@"\n\n" withString:@"\n"]; 54 | NSString *originalNameList = [[NSString stringWithUTF8String:originalName] stringByReplacingOccurrencesOfString:@"\n\n" withString:@"\n"]; 55 | NSArray *swizzledList = [swizzledNameList componentsSeparatedByString:@"\n"]; 56 | NSArray *originalList = [originalNameList componentsSeparatedByString:@"\n"]; 57 | 58 | for (int i = 0; i < swizzledCount; i ++) { 59 | NSString *methodOriginalName = originalList[i]; 60 | NSString *swizzledName = swizzledList[i]; 61 | NSMutableDictionary *swizzleInfoDict = @{}.mutableCopy; 62 | swizzleInfoDict[@"originalName"] = methodOriginalName; 63 | swizzleInfoDict[@"swizzledMethodName"] = swizzledName; 64 | [swizzleMethodListArray addObject: swizzleInfoDict]; 65 | } 66 | } 67 | self.swizzleMethodList = swizzleMethodListArray; 68 | free(methodList); 69 | } 70 | NSLog(@"swizzledInfo:%@",self.swizzleMethodList); 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/fishhook.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #import "fishhook.h" 25 | 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | 34 | #ifdef __LP64__ 35 | typedef struct mach_header_64 mach_header_t; 36 | typedef struct segment_command_64 segment_command_t; 37 | typedef struct section_64 section_t; 38 | typedef struct nlist_64 nlist_t; 39 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 40 | #else 41 | typedef struct mach_header mach_header_t; 42 | typedef struct segment_command segment_command_t; 43 | typedef struct section section_t; 44 | typedef struct nlist nlist_t; 45 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT 46 | #endif 47 | 48 | #ifndef SEG_DATA_CONST 49 | #define SEG_DATA_CONST "__DATA_CONST" 50 | #endif 51 | 52 | struct rebindings_entry { 53 | struct rebinding *rebindings; 54 | size_t rebindings_nel; 55 | struct rebindings_entry *next; 56 | }; 57 | 58 | static struct rebindings_entry *_rebindings_head; 59 | 60 | static int prepend_rebindings(struct rebindings_entry **rebindings_head, 61 | struct rebinding rebindings[], 62 | size_t nel) { 63 | struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); 64 | if (!new_entry) { 65 | return -1; 66 | } 67 | new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel); 68 | if (!new_entry->rebindings) { 69 | free(new_entry); 70 | return -1; 71 | } 72 | memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); 73 | new_entry->rebindings_nel = nel; 74 | new_entry->next = *rebindings_head; 75 | *rebindings_head = new_entry; 76 | return 0; 77 | } 78 | 79 | static void perform_rebinding_with_section(struct rebindings_entry *rebindings, 80 | section_t *section, 81 | intptr_t slide, 82 | nlist_t *symtab, 83 | char *strtab, 84 | uint32_t *indirect_symtab) { 85 | uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; 86 | void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); 87 | for (uint i = 0; i < section->size / sizeof(void *); i++) { 88 | uint32_t symtab_index = indirect_symbol_indices[i]; 89 | if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || 90 | symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { 91 | continue; 92 | } 93 | uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 94 | char *symbol_name = strtab + strtab_offset; 95 | if (strnlen(symbol_name, 2) < 2) { 96 | continue; 97 | } 98 | struct rebindings_entry *cur = rebindings; 99 | while (cur) { 100 | for (uint j = 0; j < cur->rebindings_nel; j++) { 101 | if (strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { 102 | if (cur->rebindings[j].replaced != NULL && 103 | indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { 104 | *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; 105 | } 106 | indirect_symbol_bindings[i] = cur->rebindings[j].replacement; 107 | goto symbol_loop; 108 | } 109 | } 110 | cur = cur->next; 111 | } 112 | symbol_loop:; 113 | } 114 | } 115 | 116 | static void rebind_symbols_for_image(struct rebindings_entry *rebindings, 117 | const struct mach_header *header, 118 | intptr_t slide) { 119 | Dl_info info; 120 | if (dladdr(header, &info) == 0) { 121 | return; 122 | } 123 | 124 | segment_command_t *cur_seg_cmd; 125 | segment_command_t *linkedit_segment = NULL; 126 | struct symtab_command* symtab_cmd = NULL; 127 | struct dysymtab_command* dysymtab_cmd = NULL; 128 | 129 | uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); 130 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 131 | cur_seg_cmd = (segment_command_t *)cur; 132 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 133 | if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { 134 | linkedit_segment = cur_seg_cmd; 135 | } 136 | } else if (cur_seg_cmd->cmd == LC_SYMTAB) { 137 | symtab_cmd = (struct symtab_command*)cur_seg_cmd; 138 | } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { 139 | dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; 140 | } 141 | } 142 | 143 | if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || 144 | !dysymtab_cmd->nindirectsyms) { 145 | return; 146 | } 147 | 148 | // Find base symbol/string table addresses 149 | uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; 150 | nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); 151 | char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); 152 | 153 | // Get indirect symbol table (array of uint32_t indices into symbol table) 154 | uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); 155 | 156 | cur = (uintptr_t)header + sizeof(mach_header_t); 157 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 158 | cur_seg_cmd = (segment_command_t *)cur; 159 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 160 | if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && 161 | strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { 162 | continue; 163 | } 164 | for (uint j = 0; j < cur_seg_cmd->nsects; j++) { 165 | section_t *sect = 166 | (section_t *)(cur + sizeof(segment_command_t)) + j; 167 | if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 168 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 169 | } 170 | if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { 171 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | static void _rebind_symbols_for_image(const struct mach_header *header, 179 | intptr_t slide) { 180 | rebind_symbols_for_image(_rebindings_head, header, slide); 181 | } 182 | 183 | int rebind_symbols_image(void *header, 184 | intptr_t slide, 185 | struct rebinding rebindings[], 186 | size_t rebindings_nel) { 187 | struct rebindings_entry *rebindings_head = NULL; 188 | int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); 189 | rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); 190 | free(rebindings_head); 191 | return retval; 192 | } 193 | 194 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { 195 | int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); 196 | if (retval < 0) { 197 | return retval; 198 | } 199 | // If this was the first call, register callback for image additions (which is also invoked for 200 | // existing images, otherwise, just run on existing images 201 | if (!_rebindings_head->next) { 202 | _dyld_register_func_for_add_image(_rebind_symbols_for_image); 203 | } else { 204 | uint32_t c = _dyld_image_count(); 205 | for (uint32_t i = 0; i < c; i++) { 206 | _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); 207 | } 208 | } 209 | return retval; 210 | } 211 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/fishhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #ifndef fishhook_h 25 | #define fishhook_h 26 | 27 | #include 28 | #include 29 | 30 | #if !defined(FISHHOOK_EXPORT) 31 | #define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) 32 | #else 33 | #define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif //__cplusplus 39 | 40 | /* 41 | * A structure representing a particular intended rebinding from a symbol 42 | * name to its replacement 43 | */ 44 | struct rebinding { 45 | const char *name; 46 | void *replacement; 47 | void **replaced; 48 | }; 49 | 50 | /* 51 | * For each rebinding in rebindings, rebinds references to external, indirect 52 | * symbols with the specified name to instead point at replacement for each 53 | * image in the calling process as well as for all future images that are loaded 54 | * by the process. If rebind_functions is called more than once, the symbols to 55 | * rebind are added to the existing list of rebindings, and if a given symbol 56 | * is rebound more than once, the later rebinding will take precedence. 57 | */ 58 | FISHHOOK_VISIBILITY 59 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); 60 | 61 | /* 62 | * Rebinds as above, but only in the specified image. The header should point 63 | * to the mach-o header, the slide should be the slide offset. Others as above. 64 | */ 65 | FISHHOOK_VISIBILITY 66 | int rebind_symbols_image(void *header, 67 | intptr_t slide, 68 | struct rebinding rebindings[], 69 | size_t rebindings_nel); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif //__cplusplus 74 | 75 | #endif //fishhook_h 76 | 77 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/method_check.c: -------------------------------------------------------------------------------- 1 | // 2 | // method_check.c 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #include "method_check.h" 10 | -------------------------------------------------------------------------------- /cpapm/APM/CrashANT/method_check.h: -------------------------------------------------------------------------------- 1 | // 2 | // method_check.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #ifndef method_check_h 10 | #define method_check_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // find out which method was hooked 21 | static inline BOOL validate_methods(const char *cls,const char **swizzleName, const char **originalName, unsigned int *swizzledMethodCount) __attribute__ ((always_inline)); 22 | 23 | static inline const char *join(const char *, const char *) __attribute__ ((always_inline)); 24 | static inline const char *joinWithoutSpace(const char *, const char *) __attribute__ ((always_inline)); 25 | 26 | BOOL validate_methods(const char *cls,const char **swizzleName, const char **originalName, unsigned int *swizzledMethodCount){ 27 | Class aClass = objc_getClass(cls); 28 | Method *methods; 29 | unsigned int nMethods; 30 | Dl_info info; 31 | IMP imp; 32 | char buf[128]; 33 | Method m; 34 | const char *swizzledMethodNameList = NULL; 35 | const char *swizzledOriginalMethodNameList = NULL; 36 | unsigned int swizzledCount = 0; 37 | 38 | if(!aClass) 39 | return NO; 40 | methods = class_copyMethodList(aClass, &nMethods); 41 | while (nMethods--) { 42 | m = methods[nMethods]; 43 | printf("validating [%s %s]\n",(const char *)class_getName(aClass), sel_getName(method_getName(m))); 44 | const char *function_name = sel_getName(method_getName(m)); 45 | imp = method_getImplementation(m); 46 | //imp = class_getMethodImplementation(aClass, sel_registerName("allObjects")); 47 | if(!imp){ 48 | printf("error:method_getImplementation(%s) failed\n",sel_getName(method_getName(m))); 49 | free(methods); 50 | return NO; 51 | } 52 | 53 | if(!dladdr(imp, &info)){ 54 | printf("error:dladdr() failed for %s\n",sel_getName(method_getName(m))); 55 | free(methods); 56 | return NO; 57 | } 58 | 59 | 60 | const char *func_type = "-["; 61 | char type[] = "-"; 62 | if (info.dli_sname) { 63 | strncpy(type, info.dli_sname, 1); 64 | if (strcmp(type, "+") == 0) { 65 | func_type = "+["; 66 | }else if (strcmp(type, "-") == 0){ 67 | func_type = "-["; 68 | }else{ 69 | printf("method:%s not class func also not instance",function_name); 70 | } 71 | } 72 | 73 | /*formate method*/ 74 | const char *dli_function_name = joinWithoutSpace("-[", class_getName(aClass)); 75 | dli_function_name = joinWithoutSpace(dli_function_name, " "); 76 | dli_function_name = joinWithoutSpace(dli_function_name, function_name); 77 | dli_function_name = joinWithoutSpace(dli_function_name, "]"); 78 | 79 | if(strcmp(info.dli_sname, dli_function_name)){ 80 | swizzledCount = swizzledCount + 1; 81 | swizzledOriginalMethodNameList = join(swizzledOriginalMethodNameList, function_name); 82 | swizzledMethodNameList = join(swizzledMethodNameList, info.dli_sname); 83 | continue; 84 | } 85 | if (info.dli_sname != NULL && strcmp(info.dli_sname, "") != 0) { 86 | /*Validate class name in symbol*/ 87 | snprintf(buf, sizeof(buf), "[%s ",(const char *) class_getName(aClass)); 88 | if(strncmp(info.dli_sname + 1, buf, strlen(buf))){ 89 | snprintf(buf, sizeof(buf),"[%s(",(const char *)class_getName(aClass)); 90 | if(strncmp(info.dli_sname + 1, buf, strlen(buf))){ 91 | swizzledCount = swizzledCount + 1; 92 | swizzledOriginalMethodNameList = join(swizzledOriginalMethodNameList, function_name); 93 | swizzledMethodNameList = join(swizzledMethodNameList, info.dli_sname); 94 | continue; 95 | } 96 | } 97 | 98 | /*Validate selector in symbol*/ 99 | snprintf(buf, sizeof(buf), " %s]",(const char*)sel_getName(method_getName(m))); 100 | if(strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)), buf, strlen(buf))){ 101 | swizzledCount = swizzledCount + 1; 102 | swizzledOriginalMethodNameList = join(swizzledOriginalMethodNameList, function_name); 103 | swizzledMethodNameList = join(swizzledMethodNameList, info.dli_sname); 104 | continue; 105 | } 106 | }else{ 107 | printf(" \n"); 108 | } 109 | } 110 | 111 | free(methods); 112 | *swizzledMethodCount = swizzledCount; 113 | *swizzleName = swizzledMethodNameList; 114 | *originalName = swizzledOriginalMethodNameList; 115 | return YES; 116 | } 117 | 118 | const char* join(const char *s1, const char *s2) 119 | { 120 | if (s1 == NULL) { 121 | char *result = malloc(strlen(s2)+1); 122 | strcpy(result, s2); 123 | return result; 124 | } 125 | const char *separator = "\n"; 126 | char *result = malloc(strlen(s1)+ strlen(separator) +strlen(s2)+1); 127 | if (result == NULL) exit (1); 128 | 129 | strcpy(result, s1); 130 | strcat(result, separator); 131 | strcat(result, s2); 132 | 133 | return result; 134 | } 135 | 136 | const char* joinWithoutSpace(const char *s1, const char *s2) 137 | { 138 | if (s1 == NULL) { 139 | char *result = malloc(strlen(s2)+1); 140 | strcpy(result, s2); 141 | return result; 142 | } 143 | char *result = malloc(strlen(s1)+strlen(s2)+1); 144 | if (result == NULL) exit (1); 145 | 146 | strcpy(result, s1); 147 | strcat(result, s2); 148 | 149 | return result; 150 | } 151 | 152 | #endif /* method_check_h */ 153 | -------------------------------------------------------------------------------- /cpapm/APM/Dependency/Backtrace/BSBacktraceLogger.h: -------------------------------------------------------------------------------- 1 | // 2 | // BSBacktraceLogger.h 3 | // BSBacktraceLogger 4 | // 5 | // Created by 张星宇 on 16/8/27. 6 | // Copyright © 2016年 bestswifter. All rights reserved. 7 | // 8 | 9 | #import 10 | #include 11 | #import 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define BSLOG NSLog(@"%@",[BSBacktraceLogger bs_backtraceOfCurrentThread]); 20 | #define BSLOG_MAIN NSLog(@"%@",[BSBacktraceLogger bs_backtraceOfMainThread]); 21 | #define BSLOG_ALL NSLog(@"%@",[BSBacktraceLogger bs_backtraceOfAllThread]); 22 | 23 | 24 | @interface BSBacktraceLogger : NSObject 25 | 26 | + (NSString *)bs_backtraceOfAllThread; 27 | + (NSString *)bs_backtraceOfCurrentThread; 28 | + (NSString *)bs_backtraceOfMainThread; 29 | + (NSString *)bs_backtraceOfNSThread:(NSThread *)thread; 30 | 31 | NSString *_bs_backtraceOfThread(thread_t thread); 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /cpapm/APM/Dependency/Backtrace/BSBacktraceLogger.m: -------------------------------------------------------------------------------- 1 | // 2 | // BSBacktraceLogger.m 3 | // BSBacktraceLogger 4 | // 5 | // Created by 张星宇 on 16/8/27. 6 | // Copyright © 2016年 bestswifter. All rights reserved. 7 | // 8 | 9 | #import "BSBacktraceLogger.h" 10 | 11 | #pragma -mark DEFINE MACRO FOR DIFFERENT CPU ARCHITECTURE 12 | #if defined(__arm64__) 13 | #define DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(3UL)) 14 | #define BS_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT 15 | #define BS_THREAD_STATE ARM_THREAD_STATE64 16 | #define BS_FRAME_POINTER __fp 17 | #define BS_STACK_POINTER __sp 18 | #define BS_INSTRUCTION_ADDRESS __pc 19 | 20 | #elif defined(__arm__) 21 | #define DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(1UL)) 22 | #define BS_THREAD_STATE_COUNT ARM_THREAD_STATE_COUNT 23 | #define BS_THREAD_STATE ARM_THREAD_STATE 24 | #define BS_FRAME_POINTER __r[7] 25 | #define BS_STACK_POINTER __sp 26 | #define BS_INSTRUCTION_ADDRESS __pc 27 | 28 | #elif defined(__x86_64__) 29 | #define DETAG_INSTRUCTION_ADDRESS(A) (A) 30 | #define BS_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT 31 | #define BS_THREAD_STATE x86_THREAD_STATE64 32 | #define BS_FRAME_POINTER __rbp 33 | #define BS_STACK_POINTER __rsp 34 | #define BS_INSTRUCTION_ADDRESS __rip 35 | 36 | #elif defined(__i386__) 37 | #define DETAG_INSTRUCTION_ADDRESS(A) (A) 38 | #define BS_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT 39 | #define BS_THREAD_STATE x86_THREAD_STATE32 40 | #define BS_FRAME_POINTER __ebp 41 | #define BS_STACK_POINTER __esp 42 | #define BS_INSTRUCTION_ADDRESS __eip 43 | 44 | #endif 45 | 46 | #define CALL_INSTRUCTION_FROM_RETURN_ADDRESS(A) (DETAG_INSTRUCTION_ADDRESS((A)) - 1) 47 | 48 | #if defined(__LP64__) 49 | #define TRACE_FMT "%-4d%-31s 0x%016lx %s + %lu" 50 | #define POINTER_FMT "0x%016lx" 51 | #define POINTER_SHORT_FMT "0x%lx" 52 | #define BS_NLIST struct nlist_64 53 | #else 54 | #define TRACE_FMT "%-4d%-31s 0x%08lx %s + %lu" 55 | #define POINTER_FMT "0x%08lx" 56 | #define POINTER_SHORT_FMT "0x%lx" 57 | #define BS_NLIST struct nlist 58 | #endif 59 | 60 | typedef struct BSStackFrameEntry{ 61 | const struct BSStackFrameEntry *const previous; 62 | const uintptr_t return_address; 63 | } BSStackFrameEntry; 64 | 65 | static mach_port_t main_thread_id; 66 | 67 | @implementation BSBacktraceLogger 68 | 69 | + (void)load { 70 | main_thread_id = mach_thread_self(); 71 | } 72 | 73 | #pragma -mark Implementation of interface 74 | + (NSString *)bs_backtraceOfNSThread:(NSThread *)thread { 75 | return _bs_backtraceOfThread(bs_machThreadFromNSThread(thread)); 76 | } 77 | 78 | + (NSString *)bs_backtraceOfCurrentThread { 79 | return [self bs_backtraceOfNSThread:[NSThread currentThread]]; 80 | } 81 | 82 | + (NSString *)bs_backtraceOfMainThread { 83 | return [self bs_backtraceOfNSThread:[NSThread mainThread]]; 84 | } 85 | 86 | + (NSString *)bs_backtraceOfAllThread { 87 | thread_act_array_t threads; 88 | mach_msg_type_number_t thread_count = 0; 89 | const task_t this_task = mach_task_self(); 90 | 91 | kern_return_t kr = task_threads(this_task, &threads, &thread_count); 92 | if(kr != KERN_SUCCESS) { 93 | return @"Fail to get information of all threads"; 94 | } 95 | 96 | NSMutableString *resultString = [NSMutableString stringWithFormat:@"Call Backtrace of %u threads:\n", thread_count]; 97 | for(int i = 0; i < thread_count; i++) { 98 | [resultString appendString:_bs_backtraceOfThread(threads[i])]; 99 | } 100 | return [resultString copy]; 101 | } 102 | 103 | #pragma -mark Get call backtrace of a mach_thread 104 | NSString *_bs_backtraceOfThread(thread_t thread) { 105 | uintptr_t backtraceBuffer[50]; 106 | int i = 0; 107 | NSMutableString *resultString = [[NSMutableString alloc] initWithFormat:@"Backtrace of Thread %u:\n", thread]; 108 | 109 | _STRUCT_MCONTEXT machineContext; 110 | if(!bs_fillThreadStateIntoMachineContext(thread, &machineContext)) { 111 | return [NSString stringWithFormat:@"Fail to get information about thread: %u", thread]; 112 | } 113 | 114 | const uintptr_t instructionAddress = bs_mach_instructionAddress(&machineContext); 115 | backtraceBuffer[i] = instructionAddress; 116 | ++i; 117 | 118 | uintptr_t linkRegister = bs_mach_linkRegister(&machineContext); 119 | if (linkRegister) { 120 | backtraceBuffer[i] = linkRegister; 121 | i++; 122 | } 123 | 124 | if(instructionAddress == 0) { 125 | return @"Fail to get instruction address"; 126 | } 127 | 128 | BSStackFrameEntry frame = {0}; 129 | const uintptr_t framePtr = bs_mach_framePointer(&machineContext); 130 | if(framePtr == 0 || 131 | bs_mach_copyMem((void *)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) { 132 | return @"Fail to get frame pointer"; 133 | } 134 | 135 | for(; i < 50; i++) { 136 | backtraceBuffer[i] = frame.return_address; 137 | if(backtraceBuffer[i] == 0 || 138 | frame.previous == 0 || 139 | bs_mach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { 140 | break; 141 | } 142 | } 143 | 144 | int backtraceLength = i; 145 | Dl_info symbolicated[backtraceLength]; 146 | bs_symbolicate(backtraceBuffer, symbolicated, backtraceLength, 0); 147 | for (int i = 0; i < backtraceLength; ++i) { 148 | [resultString appendFormat:@"%@", bs_logBacktraceEntry(i, backtraceBuffer[i], &symbolicated[i])]; 149 | } 150 | [resultString appendFormat:@"\n"]; 151 | return [resultString copy]; 152 | } 153 | 154 | #pragma -mark Convert NSThread to Mach thread 155 | thread_t bs_machThreadFromNSThread(NSThread *nsthread) { 156 | char name[256]; 157 | mach_msg_type_number_t count; 158 | thread_act_array_t list; 159 | task_threads(mach_task_self(), &list, &count); 160 | 161 | NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970]; 162 | NSString *originName = [nsthread name]; 163 | [nsthread setName:[NSString stringWithFormat:@"%f", currentTimestamp]]; 164 | 165 | if ([nsthread isMainThread]) { 166 | return (thread_t)main_thread_id; 167 | } 168 | 169 | for (int i = 0; i < count; ++i) { 170 | pthread_t pt = pthread_from_mach_thread_np(list[i]); 171 | if ([nsthread isMainThread]) { 172 | if (list[i] == main_thread_id) { 173 | return list[i]; 174 | } 175 | } 176 | if (pt) { 177 | name[0] = '\0'; 178 | pthread_getname_np(pt, name, sizeof name); 179 | if (!strcmp(name, [nsthread name].UTF8String)) { 180 | [nsthread setName:originName]; 181 | return list[i]; 182 | } 183 | } 184 | } 185 | 186 | [nsthread setName:originName]; 187 | return mach_thread_self(); 188 | } 189 | 190 | #pragma -mark GenerateBacbsrackEnrty 191 | NSString* bs_logBacktraceEntry(const int entryNum, 192 | const uintptr_t address, 193 | const Dl_info* const dlInfo) { 194 | char faddrBuff[20]; 195 | char saddrBuff[20]; 196 | 197 | const char* fname = bs_lastPathEntry(dlInfo->dli_fname); 198 | if(fname == NULL) { 199 | sprintf(faddrBuff, POINTER_FMT, (uintptr_t)dlInfo->dli_fbase); 200 | fname = faddrBuff; 201 | } 202 | 203 | uintptr_t offset = address - (uintptr_t)dlInfo->dli_saddr; 204 | const char* sname = dlInfo->dli_sname; 205 | if(sname == NULL) { 206 | sprintf(saddrBuff, POINTER_SHORT_FMT, (uintptr_t)dlInfo->dli_fbase); 207 | sname = saddrBuff; 208 | offset = address - (uintptr_t)dlInfo->dli_fbase; 209 | } 210 | return [NSString stringWithFormat:@"%-30s 0x%08" PRIxPTR " %s + %lu\n" ,fname, (uintptr_t)address, sname, offset]; 211 | } 212 | 213 | const char* bs_lastPathEntry(const char* const path) { 214 | if(path == NULL) { 215 | return NULL; 216 | } 217 | 218 | char* lastFile = strrchr(path, '/'); 219 | return lastFile == NULL ? path : lastFile + 1; 220 | } 221 | 222 | #pragma -mark HandleMachineContext 223 | bool bs_fillThreadStateIntoMachineContext(thread_t thread, _STRUCT_MCONTEXT *machineContext) { 224 | mach_msg_type_number_t state_count = BS_THREAD_STATE_COUNT; 225 | kern_return_t kr = thread_get_state(thread, BS_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count); 226 | return (kr == KERN_SUCCESS); 227 | } 228 | 229 | uintptr_t bs_mach_framePointer(mcontext_t const machineContext){ 230 | return machineContext->__ss.BS_FRAME_POINTER; 231 | } 232 | 233 | uintptr_t bs_mach_stackPointer(mcontext_t const machineContext){ 234 | return machineContext->__ss.BS_STACK_POINTER; 235 | } 236 | 237 | uintptr_t bs_mach_instructionAddress(mcontext_t const machineContext){ 238 | return machineContext->__ss.BS_INSTRUCTION_ADDRESS; 239 | } 240 | 241 | uintptr_t bs_mach_linkRegister(mcontext_t const machineContext){ 242 | #if defined(__i386__) || defined(__x86_64__) 243 | return 0; 244 | #else 245 | return machineContext->__ss.__lr; 246 | #endif 247 | } 248 | 249 | kern_return_t bs_mach_copyMem(const void *const src, void *const dst, const size_t numBytes){ 250 | vm_size_t bytesCopied = 0; 251 | return vm_read_overwrite(mach_task_self(), (vm_address_t)src, (vm_size_t)numBytes, (vm_address_t)dst, &bytesCopied); 252 | } 253 | 254 | #pragma -mark Symbolicate 255 | void bs_symbolicate(const uintptr_t* const backtraceBuffer, 256 | Dl_info* const symbolsBuffer, 257 | const int numEntries, 258 | const int skippedEntries){ 259 | int i = 0; 260 | 261 | if(!skippedEntries && i < numEntries) { 262 | bs_dladdr(backtraceBuffer[i], &symbolsBuffer[i]); 263 | i++; 264 | } 265 | 266 | for(; i < numEntries; i++) { 267 | bs_dladdr(CALL_INSTRUCTION_FROM_RETURN_ADDRESS(backtraceBuffer[i]), &symbolsBuffer[i]); 268 | } 269 | } 270 | 271 | bool bs_dladdr(const uintptr_t address, Dl_info* const info) { 272 | info->dli_fname = NULL; 273 | info->dli_fbase = NULL; 274 | info->dli_sname = NULL; 275 | info->dli_saddr = NULL; 276 | 277 | const uint32_t idx = bs_imageIndexContainingAddress(address); 278 | if(idx == UINT_MAX) { 279 | return false; 280 | } 281 | const struct mach_header* header = _dyld_get_image_header(idx); 282 | const uintptr_t imageVMAddrSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx); 283 | const uintptr_t addressWithSlide = address - imageVMAddrSlide; 284 | const uintptr_t segmentBase = bs_segmentBaseOfImageIndex(idx) + imageVMAddrSlide; 285 | if(segmentBase == 0) { 286 | return false; 287 | } 288 | 289 | info->dli_fname = _dyld_get_image_name(idx); 290 | info->dli_fbase = (void*)header; 291 | 292 | // Find symbol tables and get whichever symbol is closest to the address. 293 | const BS_NLIST* bestMatch = NULL; 294 | uintptr_t bestDistance = ULONG_MAX; 295 | uintptr_t cmdPtr = bs_firstCmdAfterHeader(header); 296 | if(cmdPtr == 0) { 297 | return false; 298 | } 299 | for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { 300 | const struct load_command* loadCmd = (struct load_command*)cmdPtr; 301 | if(loadCmd->cmd == LC_SYMTAB) { 302 | const struct symtab_command* symtabCmd = (struct symtab_command*)cmdPtr; 303 | const BS_NLIST* symbolTable = (BS_NLIST*)(segmentBase + symtabCmd->symoff); 304 | const uintptr_t stringTable = segmentBase + symtabCmd->stroff; 305 | 306 | for(uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++) { 307 | // If n_value is 0, the symbol refers to an external object. 308 | if(symbolTable[iSym].n_value != 0) { 309 | uintptr_t symbolBase = symbolTable[iSym].n_value; 310 | uintptr_t currentDistance = addressWithSlide - symbolBase; 311 | if((addressWithSlide >= symbolBase) && 312 | (currentDistance <= bestDistance)) { 313 | bestMatch = symbolTable + iSym; 314 | bestDistance = currentDistance; 315 | } 316 | } 317 | } 318 | if(bestMatch != NULL) { 319 | info->dli_saddr = (void*)(bestMatch->n_value + imageVMAddrSlide); 320 | info->dli_sname = (char*)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx); 321 | if(*info->dli_sname == '_') { 322 | info->dli_sname++; 323 | } 324 | // This happens if all symbols have been stripped. 325 | if(info->dli_saddr == info->dli_fbase && bestMatch->n_type == 3) { 326 | info->dli_sname = NULL; 327 | } 328 | break; 329 | } 330 | } 331 | cmdPtr += loadCmd->cmdsize; 332 | } 333 | return true; 334 | } 335 | 336 | uintptr_t bs_firstCmdAfterHeader(const struct mach_header* const header) { 337 | switch(header->magic) { 338 | case MH_MAGIC: 339 | case MH_CIGAM: 340 | return (uintptr_t)(header + 1); 341 | case MH_MAGIC_64: 342 | case MH_CIGAM_64: 343 | return (uintptr_t)(((struct mach_header_64*)header) + 1); 344 | default: 345 | return 0; // Header is corrupt 346 | } 347 | } 348 | 349 | uint32_t bs_imageIndexContainingAddress(const uintptr_t address) { 350 | const uint32_t imageCount = _dyld_image_count(); 351 | const struct mach_header* header = 0; 352 | 353 | for(uint32_t iImg = 0; iImg < imageCount; iImg++) { 354 | header = _dyld_get_image_header(iImg); 355 | if(header != NULL) { 356 | // Look for a segment command with this address within its range. 357 | uintptr_t addressWSlide = address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg); 358 | uintptr_t cmdPtr = bs_firstCmdAfterHeader(header); 359 | if(cmdPtr == 0) { 360 | continue; 361 | } 362 | for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { 363 | const struct load_command* loadCmd = (struct load_command*)cmdPtr; 364 | if(loadCmd->cmd == LC_SEGMENT) { 365 | const struct segment_command* segCmd = (struct segment_command*)cmdPtr; 366 | if(addressWSlide >= segCmd->vmaddr && 367 | addressWSlide < segCmd->vmaddr + segCmd->vmsize) { 368 | return iImg; 369 | } 370 | } 371 | else if(loadCmd->cmd == LC_SEGMENT_64) { 372 | const struct segment_command_64* segCmd = (struct segment_command_64*)cmdPtr; 373 | if(addressWSlide >= segCmd->vmaddr && 374 | addressWSlide < segCmd->vmaddr + segCmd->vmsize) { 375 | return iImg; 376 | } 377 | } 378 | cmdPtr += loadCmd->cmdsize; 379 | } 380 | } 381 | } 382 | return UINT_MAX; 383 | } 384 | 385 | uintptr_t bs_segmentBaseOfImageIndex(const uint32_t idx) { 386 | const struct mach_header* header = _dyld_get_image_header(idx); 387 | 388 | // Look for a segment command and return the file image address. 389 | uintptr_t cmdPtr = bs_firstCmdAfterHeader(header); 390 | if(cmdPtr == 0) { 391 | return 0; 392 | } 393 | for(uint32_t i = 0;i < header->ncmds; i++) { 394 | const struct load_command* loadCmd = (struct load_command*)cmdPtr; 395 | if(loadCmd->cmd == LC_SEGMENT) { 396 | const struct segment_command* segmentCmd = (struct segment_command*)cmdPtr; 397 | if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { 398 | return segmentCmd->vmaddr - segmentCmd->fileoff; 399 | } 400 | } 401 | else if(loadCmd->cmd == LC_SEGMENT_64) { 402 | const struct segment_command_64* segmentCmd = (struct segment_command_64*)cmdPtr; 403 | if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { 404 | return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff); 405 | } 406 | } 407 | cmdPtr += loadCmd->cmdsize; 408 | } 409 | return 0; 410 | } 411 | 412 | @end 413 | -------------------------------------------------------------------------------- /cpapm/APM/Dependency/Backtrace/WTBacktrace.h: -------------------------------------------------------------------------------- 1 | // 2 | // WTBacktrace.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | #include 11 | 12 | @interface WTBacktrace : NSObject 13 | 14 | +(NSString *)withThread:(NSThread *)thread; 15 | +(NSString *)currentThread; 16 | +(NSString *)mainThread; 17 | +(NSString *)allThread; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /cpapm/APM/Dependency/Backtrace/WTBacktrace.m: -------------------------------------------------------------------------------- 1 | // 2 | // WTBacktrace.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "WTBacktrace.h" 10 | #import "BSBacktraceLogger.h" 11 | #import 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | @implementation WTBacktrace 21 | 22 | +(NSString *)withThread:(NSThread *)thread { 23 | return [BSBacktraceLogger bs_backtraceOfNSThread:thread]; 24 | } 25 | 26 | +(NSString *)currentThread { 27 | return [self withThread:[NSThread currentThread]]; 28 | } 29 | 30 | +(NSString *)mainThread { 31 | return [self withThread:[NSThread mainThread]]; 32 | } 33 | 34 | +(NSString *)allThread { 35 | 36 | thread_act_array_t threads = nil; 37 | mach_msg_type_number_t thread_count = 0; 38 | if (task_threads(mach_task_self_, &(threads), &thread_count) != KERN_SUCCESS) { 39 | return @""; 40 | } 41 | 42 | NSString *resultString = [NSString stringWithFormat:@"Call Backtrace of %d threads:\n", thread_count]; 43 | 44 | for (int i = 0; i < thread_count; i++) { 45 | NSInteger index = i; 46 | NSString *bt = _bs_backtraceOfThread(threads[index]); 47 | resultString = [resultString stringByAppendingString:bt]; 48 | } 49 | return resultString; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/DetectorProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // DetectorProtocol.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol DetectorProtocol 12 | 13 | +(void)prepareForDetector; 14 | 15 | -(BOOL)markAlive; 16 | 17 | -(BOOL)isAlive; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/NSObject+Detector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+Detector.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DetectorProtocol.h" 11 | #import "WTObjectProxy.h" 12 | 13 | @interface NSObject (Detector) 14 | 15 | + (void)swizzleMethod:(SEL)originalMethod withMethod:(SEL)targetMethod; 16 | 17 | @property (nonatomic, strong) WTObjectProxy *wtproxy; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/NSObject+Detector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+Detector.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "NSObject+Detector.h" 10 | #import 11 | #import 12 | 13 | @implementation NSObject (Detector) 14 | 15 | +(void)swizzleMethod:(SEL)originalMethod withMethod:(SEL)targetMethod { 16 | 17 | // check has hooked then hook the hooked method 18 | 19 | Class class = [self class]; 20 | 21 | Method originalM = class_getInstanceMethod(class, originalMethod); 22 | Method swizzledM = class_getInstanceMethod(class, targetMethod); 23 | 24 | BOOL didAddMethod = 25 | class_addMethod(class, 26 | originalMethod, 27 | method_getImplementation(swizzledM), 28 | method_getTypeEncoding(swizzledM)); 29 | 30 | if (didAddMethod) { 31 | class_replaceMethod(class, 32 | targetMethod, 33 | method_getImplementation(originalM), 34 | method_getTypeEncoding(originalM)); 35 | }else{ 36 | method_exchangeImplementations(originalM, swizzledM); 37 | } 38 | 39 | } 40 | 41 | @dynamic wtproxy; 42 | -(void)setWtproxy:(WTObjectProxy *)wtproxy { 43 | objc_setAssociatedObject(self, sel_getName(@selector(wtproxy)), wtproxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 44 | } 45 | 46 | -(WTObjectProxy *)wtproxy { 47 | return objc_getAssociatedObject(self, sel_getName(@selector(wtproxy))); 48 | } 49 | 50 | + (void)prepareForSniffer { 51 | 52 | } 53 | 54 | -(BOOL)isAlive { 55 | return true; 56 | } 57 | 58 | -(BOOL)markAlive { 59 | if (self.wtproxy != nil) { 60 | return false; 61 | } 62 | 63 | //skip system class 64 | NSString* className = NSStringFromClass([self class]); 65 | if ([className hasPrefix:@"_"] || [className hasPrefix:@"UI"] || [className hasPrefix:@"NS"]) { 66 | return false; 67 | } 68 | 69 | //view object needs a super view to be alive 70 | if ([self isKindOfClass:[UIView class]]) { 71 | UIView* v = (UIView*)self; 72 | if (v.superview == nil) { 73 | return false; 74 | } 75 | } 76 | 77 | //controller object needs a parent to be alive 78 | if ([self isKindOfClass:[UIViewController class]]) { 79 | UIViewController* c = (UIViewController*)self; 80 | if (c.navigationController == nil && c.presentingViewController == nil) { 81 | return false; 82 | } 83 | } 84 | 85 | //skip some weird system classes 86 | static NSMutableDictionary* ignoreList = nil; 87 | @synchronized (self) { 88 | if (ignoreList == nil) { 89 | ignoreList = @{}.mutableCopy; 90 | NSArray* arr = @[@"UITextFieldLabel", @"UIFieldEditor", @"UITextSelectionView", 91 | @"UITableViewCellSelectedBackground", @"UIView", @"UIAlertController"]; 92 | for (NSString* str in arr) { 93 | ignoreList[str] = @":)"; 94 | } 95 | } 96 | if ([ignoreList objectForKey:NSStringFromClass([self class])]) { 97 | return false; 98 | } 99 | } 100 | 101 | WTObjectProxy *proxy = [WTObjectProxy new]; 102 | self.wtproxy = proxy; 103 | [self.wtproxy prepareProxy:self]; 104 | return true; 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/UINavigationController+Detector.h: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationController+Detector.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UINavigationController (Detector) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/UINavigationController+Detector.m: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationController+Detector.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "UINavigationController+Detector.h" 10 | #import 11 | #import "NSObject+Detector.h" 12 | 13 | @implementation UINavigationController (Detector) 14 | 15 | +(void)prepareForDetector { 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | [self swizzleMethod:@selector(pushViewController:animated:) withMethod:@selector(swizzled_pushViewController:animated:)]; 19 | }); 20 | } 21 | 22 | - (void)swizzled_pushViewController:(UIViewController *)viewController animated:(BOOL)animated { 23 | 24 | [self swizzled_pushViewController:viewController animated:animated]; 25 | 26 | [viewController markAlive]; 27 | 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/UIView+Detector.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Detector.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (Detector) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/UIView+Detector.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Detector.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "UIView+Detector.h" 10 | #import 11 | #import "NSObject+Detector.h" 12 | 13 | @implementation UIView (Detector) 14 | 15 | +(void)prepareForDetector { 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | [self swizzleMethod:@selector(didMoveToSuperview) withMethod:@selector(swizzled_didMoveToSuperview)]; 19 | }); 20 | } 21 | 22 | - (void)swizzled_didMoveToSuperview 23 | { 24 | [self swizzled_didMoveToSuperview]; 25 | 26 | BOOL hasAliveParent = false; 27 | 28 | UIResponder* r = self.nextResponder; 29 | while (r) { 30 | if ([r wtproxy] != nil) { 31 | hasAliveParent = true; 32 | break; 33 | } 34 | r = r.nextResponder; 35 | } 36 | 37 | if (hasAliveParent) { 38 | [self markAlive]; 39 | } 40 | } 41 | 42 | - (BOOL)isAlive 43 | { 44 | BOOL alive = true; 45 | 46 | BOOL onUIStack = false; 47 | 48 | UIView* v = self; 49 | while (v.superview != nil) { 50 | v = v.superview; 51 | } 52 | if ([v isKindOfClass:[UIWindow class]]) { 53 | onUIStack = true; 54 | } 55 | 56 | //save responder 57 | if (self.wtproxy.weakResponder == nil) { 58 | UIResponder* r = self.nextResponder; 59 | while (r) { 60 | 61 | if (r.nextResponder == nil) { 62 | break; 63 | }else{ 64 | r = r.nextResponder; 65 | } 66 | 67 | if ([r isKindOfClass:[UIViewController class]]) { 68 | break; 69 | } 70 | } 71 | self.wtproxy.weakResponder = r; 72 | } 73 | 74 | if (onUIStack == false) { 75 | alive = false; 76 | 77 | //if controller is active, view should be considered alive too 78 | if ([self.wtproxy.weakResponder isKindOfClass:[UIViewController class]]) { 79 | alive = true; 80 | } 81 | } 82 | 83 | return alive; 84 | } 85 | 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/UIViewController+Detector.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Detector.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIViewController (Detector) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/UIViewController+Detector.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Detector.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+Detector.h" 10 | #import "NSObject+Detector.h" 11 | #import 12 | 13 | @implementation UIViewController (Detector) 14 | 15 | +(void)prepareForDetector { 16 | 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | [self swizzleMethod:@selector(presentViewController:animated:completion:) withMethod:@selector(swizzled_presentViewController:animated:completion:)]; 20 | }); 21 | } 22 | 23 | - (void)swizzled_presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion { 24 | [self swizzled_presentViewController:viewControllerToPresent animated:flag completion:completion]; 25 | 26 | [viewControllerToPresent markAlive]; 27 | } 28 | 29 | - (BOOL)isAlive 30 | { 31 | BOOL alive = true; 32 | 33 | BOOL visibleOnScreen = false; 34 | 35 | UIView* v = self.view; 36 | while (v.superview != nil) { 37 | v = v.superview; 38 | } 39 | if ([v isKindOfClass:[UIWindow class]]) { 40 | visibleOnScreen = true; 41 | } 42 | 43 | 44 | BOOL beingHeld = false; 45 | if (self.navigationController != nil || self.presentingViewController != nil) { 46 | beingHeld = true; 47 | } 48 | 49 | //not visible, not in view stack 50 | if (visibleOnScreen == false && beingHeld == false) { 51 | alive = false; 52 | } 53 | 54 | return alive; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/WTLeaksDetector.h: -------------------------------------------------------------------------------- 1 | // 2 | // WTLeaksDetector.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define Notif_WTDetector_Ping @"Notif_WTDetector_Ping" 12 | #define Notif_WTDetector_Pong @"Notif_WTDetector_Pong" 13 | 14 | @interface WTLeaksDetector : NSObject 15 | 16 | + (instancetype)sharedInstance; 17 | 18 | -(void)startDetector; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/WTLeaksDetector.m: -------------------------------------------------------------------------------- 1 | // 2 | // WTLeaksDetector.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "WTLeaksDetector.h" 10 | #import 11 | #import "NSObject+Detector.h" 12 | 13 | @interface WTLeaksDetector() 14 | 15 | @property (nonatomic, strong) NSTimer* pingTimer; 16 | 17 | @end 18 | 19 | @implementation WTLeaksDetector 20 | 21 | + (instancetype)sharedInstance { 22 | static WTLeaksDetector* instance = nil; 23 | 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | instance = [WTLeaksDetector new]; 27 | }); 28 | 29 | return instance; 30 | } 31 | 32 | -(void)startDetector { 33 | 34 | [UINavigationController prepareForDetector]; 35 | [UIViewController prepareForDetector]; 36 | 37 | [self startPingTimer]; 38 | 39 | [[NSNotificationCenter defaultCenter] removeObserver:self name:Notif_WTDetector_Pong object:nil]; 40 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectPong:) name:Notif_WTDetector_Pong object:nil]; 41 | } 42 | 43 | - (void)startPingTimer 44 | { 45 | if ([NSThread isMainThread] == false) { 46 | dispatch_async(dispatch_get_main_queue(), ^{ 47 | [self startPingTimer]; 48 | return ; 49 | }); 50 | } 51 | 52 | if (self.pingTimer) { 53 | return; 54 | } 55 | 56 | self.pingTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 57 | target:self 58 | selector:@selector(sendPing) 59 | userInfo:nil 60 | repeats:true]; 61 | 62 | [[NSRunLoop currentRunLoop] addTimer:self.pingTimer forMode:NSRunLoopCommonModes]; 63 | } 64 | 65 | - (void)sendPing { 66 | [[NSNotificationCenter defaultCenter] postNotificationName:Notif_WTDetector_Ping object:nil]; 67 | } 68 | 69 | - (void)detectPong:(NSNotification*)notif { 70 | NSObject* leakedObject = notif.object; 71 | 72 | if ([leakedObject isKindOfClass:[UIViewController class]]) { 73 | NSLog(@"\n\nDetect Possible Controller Leak: %@ \n\n", [leakedObject class]); 74 | }else{ 75 | NSLog(@"\n\nDetect Possible Leak: %@ \n\n", [leakedObject class]); 76 | } 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/WTObjectProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // WTObjectProxy.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WTObjectProxy : NSObject 12 | 13 | -(void)prepareProxy:(NSObject *)target; 14 | 15 | @property (nonatomic, weak) NSObject *weakTarget; 16 | @property (nonatomic, weak) NSObject *weakResponder;// uiview responder 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /cpapm/APM/Leaks/WTObjectProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // WTObjectProxy.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/11. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "WTObjectProxy.h" 10 | #import "WTLeaksDetector.h" 11 | #import "NSObject+Detector.h" 12 | 13 | #define kLeakCheckMaxFailCount 5 14 | 15 | @interface WTObjectProxy() 16 | 17 | @property (nonatomic, assign) BOOL markLeaks; 18 | @property (nonatomic, assign) NSInteger checkLeaksCount; 19 | 20 | @end 21 | 22 | @implementation WTObjectProxy 23 | 24 | -(void)prepareProxy:(NSObject *)target { 25 | 26 | self.weakTarget = target; 27 | 28 | [[NSNotificationCenter defaultCenter] removeObserver:self name:Notif_WTDetector_Ping object:nil]; 29 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectPing) name:Notif_WTDetector_Ping object:nil]; 30 | } 31 | 32 | -(void)detectPing { 33 | if (!self.weakTarget) { 34 | return; 35 | } 36 | if (_markLeaks) { 37 | return; 38 | } 39 | 40 | BOOL alive = [self.weakTarget isAlive]; 41 | if (alive == false) { 42 | _checkLeaksCount += 1; 43 | } 44 | 45 | if (self.checkLeaksCount >= kLeakCheckMaxFailCount) { 46 | _markLeaks = true; 47 | NSLog(@"*** %@ is still alive *** \n",self.weakTarget); 48 | [[NSNotificationCenter defaultCenter] postNotificationName:Notif_WTDetector_Pong object:self.weakTarget]; 49 | } 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /cpapm/APM/Net/CFNet.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFNet.h 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/12. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CFNet : NSObject 12 | 13 | @property (nonatomic, strong) NSMutableDictionary *responseMap; 14 | @property (nonatomic, strong) NSMutableDictionary *responseDataMap; 15 | 16 | // hook cfnetwork 17 | +(void)install; 18 | 19 | -(void)testCFGetClient; 20 | -(void)testCFGetClient2; 21 | 22 | -(void)testCFGetClientCompletion:(void(^)(NSData *))completionBlock; 23 | 24 | -(void)testCFGetClientCompletion:(void(^)(NSData *))completionBlock; 25 | -(void)testCFGetClient2Completion:(void(^)(NSData *))completionBlock; 26 | 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /cpapm/APM/Net/CFNet.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFNet.m 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/12. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import "CFNet.h" 10 | #import "Hook_CFNetwork.h" 11 | #import "fishhook.h" 12 | 13 | @implementation CFNet 14 | 15 | +(void)install { 16 | 17 | struct rebinding _rebinding = { "CFReadStreamSetClient", wt_CFReadStreamSetClient, (void *)&original_CFReadStreamSetClient}; 18 | struct rebinding _request_binding = { "CFReadStreamCreateForHTTPRequest", wt_CFReadStreamCreateForHTTPRequest, (void *)&original_CFReadStreamCreateForHTTPRequest}; 19 | struct rebinding _stream_open_binding = { "CFReadStreamOpen", wt_CFReadStreamOpen, (void *)&original_CFReadStreamOpen}; 20 | struct rebinding _stream_close_binding = { "CFReadStreamClose", wt_CFReadStreamClose, (void *)&original_CFReadStreamClose}; 21 | struct rebinding _stream_setproperty_binding = { "CFReadStreamSetProperty", wt_CFReadStreamSetProperty, (void *)&original_CFReadStreamSetProperty}; 22 | rebind_symbols( 23 | (struct rebinding[5]){ 24 | _rebinding, 25 | _request_binding, 26 | _stream_open_binding, 27 | _stream_close_binding, 28 | _stream_setproperty_binding 29 | }, 5); 30 | } 31 | 32 | // sync function 33 | -(void)CFTest { 34 | 35 | CFStringRef headerFieldName = CFSTR("this is a header"); 36 | CFStringRef headerFieldValue = CFSTR("value"); 37 | //创建url 38 | CFStringRef url1 = CFSTR("http://c.hiphotos.baidu.com/image/w%3D310/sign=b8f7695888d4b31cf03c92bab7d6276f/4e4a20a4462309f76248df09710e0cf3d7cad682.jpg"); 39 | CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url1, NULL); 40 | //设置请求方式 41 | CFStringRef requestMethod = CFSTR("GET"); 42 | //创建请求 43 | CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault,requestMethod, myURL, kCFHTTPVersion1_1); 44 | //设置头信息 45 | CFHTTPMessageSetHeaderFieldValue(myRequest, headerFieldName, headerFieldValue); 46 | //创建CFReadStreamRef对象来序列化并发送CFHTTP请求,注意CFReadStreamCreateForHTTPRequest在iOS 9.0开始已经有DEPRECATED警告, 47 | #pragma clang diagnostic push 48 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 49 | CFReadStreamRef myReadStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest); 50 | #pragma clang diagnostic pop 51 | //打开读取流 52 | CFReadStreamOpen(myReadStream); 53 | //存放响应数据 54 | NSMutableData *responseBytes = [NSMutableData data]; 55 | CFIndex numBytesRead = 0; 56 | //从流中读取数据,读完为止,其中CFReadStreamRead会阻塞代码 57 | do { 58 | UInt8 buf[1024]; 59 | numBytesRead = CFReadStreamRead(myReadStream, buf, sizeof(buf)); 60 | 61 | if (numBytesRead > 0) { 62 | [responseBytes appendBytes:buf length:numBytesRead]; 63 | } 64 | } while (numBytesRead > 0); 65 | //读取响应头信息 66 | #pragma clang diagnostic push 67 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 68 | CFHTTPMessageRef myResponse = (CFHTTPMessageRef) CFReadStreamCopyProperty(myReadStream, kCFStreamPropertyHTTPResponseHeader); 69 | #pragma clang diagnostic pop 70 | //读取statusCode 71 | CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(myResponse); 72 | //打印数据 73 | if(statusCode == 200) { 74 | //NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseBytes options:NSJSONReadingMutableLeaves error:nil]; 75 | NSLog(@"%@", responseBytes); 76 | } 77 | } 78 | 79 | -(void)testCFGetClient { 80 | CFStringRef urlStr = CFSTR("http://c.hiphotos.baidu.com/image/w%3D310/sign=b8f7695888d4b31cf03c92bab7d6276f/4e4a20a4462309f76248df09710e0cf3d7cad682.jpg"); 81 | [self testClientGet: urlStr completion:^(NSData * responseData) { 82 | NSLog(@"first_response"); 83 | // weakSelf.responseImageView.image = [UIImage imageWithData:responseData]; 84 | }]; 85 | } 86 | -(void)testCFGetClient2 { 87 | 88 | CFStringRef urlStr = CFSTR("http://e.hiphotos.baidu.com/image/pic/item/500fd9f9d72a6059099ccd5a2334349b023bbae5.jpg"); 89 | [self testClientGet: urlStr completion:^(NSData *responseData) { 90 | NSLog(@"second_response"); 91 | // weakSelf.secondResponseImageView.image = [UIImage imageWithData:responseData];; 92 | }]; 93 | } 94 | 95 | -(void)testCFGetClientCompletion:(void(^)(NSData *))completionBlock { 96 | CFStringRef urlStr = CFSTR("http://c.hiphotos.baidu.com/image/w%3D310/sign=b8f7695888d4b31cf03c92bab7d6276f/4e4a20a4462309f76248df09710e0cf3d7cad682.jpg"); 97 | [self testClientGet: urlStr completion:^(NSData * responseData) {; 98 | if (completionBlock) { 99 | completionBlock(responseData); 100 | } 101 | }]; 102 | } 103 | -(void)testCFGetClient2Completion:(void(^)(NSData *))completionBlock { 104 | CFStringRef urlStr = CFSTR("http://e.hiphotos.baidu.com/image/pic/item/500fd9f9d72a6059099ccd5a2334349b023bbae5.jpg"); 105 | [self testClientGet: urlStr completion:^(NSData *responseData) { 106 | if (completionBlock) { 107 | completionBlock(responseData); 108 | } 109 | }]; 110 | } 111 | 112 | -(void)testClientGet:(CFStringRef)urlStr completion:(void(^)(NSData *))completionBlock { 113 | // CFStringRef urlStr = CFSTR("http://c.hiphotos.baidu.com/image/w%3D310/sign=b8f7695888d4b31cf03c92bab7d6276f/4e4a20a4462309f76248df09710e0cf3d7cad682.jpg"); 114 | 115 | //GET请求 116 | CFStringRef method = CFSTR("GET"); 117 | 118 | //构造URL 119 | CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL); 120 | 121 | //http请求 122 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, method, url, kCFHTTPVersion1_1); 123 | 124 | //创建一个读取流 读取网络数据 125 | CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request); 126 | 127 | NSString *uniqueValue = [NSString stringWithFormat:@"%.2f_%@",[[NSDate date] timeIntervalSince1970], (__bridge NSString *)urlStr]; 128 | if (!self.responseMap) { 129 | self.responseMap = @{}.mutableCopy; 130 | } 131 | if (self.responseMap) { 132 | self.responseMap[uniqueValue] = completionBlock; 133 | } 134 | CFReadStreamSetProperty((CFReadStreamRef)readStream, CFSTR("CFStreamID"), (__bridge CFTypeRef)(uniqueValue)); 135 | 136 | // uniqueValue add to call back map 137 | CFStringRef requestHeader = CFSTR("CFStreamID"); 138 | CFStringRef requestHeaderValue = (__bridge CFStringRef)uniqueValue; 139 | CFHTTPMessageSetHeaderFieldValue(request, requestHeader, requestHeaderValue); 140 | NSMutableDictionary *proxyToUse = @{@"CFStreamID": uniqueValue}; 141 | CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, (__bridge CFTypeRef)(proxyToUse)); 142 | CFStreamClientContext ctxt = {0, (__bridge void *)(self), NULL, NULL, NULL}; 143 | 144 | 145 | //监听回调事件 146 | //  kCFStreamEventNone,(没有事件发生) 147 | // 148 | //  kCFStreamEventOpenCompleted,(流被成功打开) 149 | // 150 | //  kCFStreamEventHasBytesAvailable,(有数据可以读取) 151 | // 152 | //  kCFStreamEventCanAcceptBytes,(流可以接受写入数据(用于写入流)) 153 | // 154 | //  kCFStreamEventErrorOccurred,(在流上有错误发生) 155 | // 156 | //  kCFStreamEventEndEncountered ,(到达了流的结束位置) 157 | CFOptionFlags event = kCFStreamEventHasBytesAvailable|kCFStreamEventEndEncountered; 158 | 159 | //设值回调函数myCallBack 160 | 161 | CFReadStreamSetClient(readStream,event,CFReadStreamCallBack,&ctxt); 162 | 163 | CFReadStreamScheduleWithRunLoop(readStream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 164 | 165 | CFReadStreamOpen(readStream); 166 | 167 | } 168 | 169 | void CFReadStreamCallBack (CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo){ 170 | 171 | 172 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPProxy)); 173 | CFNet *client = (__bridge CFNet *)clientCallBackInfo; 174 | 175 | CFTypeRef uniqueValue = (__bridge CFTypeRef)(proxyInfo[@"CFStreamID"]); 176 | 177 | if (!client.responseDataMap) { 178 | client.responseDataMap = @{}.mutableCopy; 179 | } 180 | if (client.responseDataMap && [(__bridge NSString *)uniqueValue length] > 0) { 181 | if (client.responseDataMap[(__bridge NSString *)(uniqueValue)] == nil) { 182 | client.responseDataMap[(__bridge NSString *)(uniqueValue)] = [NSMutableData data]; 183 | } 184 | } 185 | 186 | if(type == kCFStreamEventHasBytesAvailable){ 187 | UInt8 buff[255]; 188 | long length = CFReadStreamRead(stream, buff, 255); 189 | NSMutableData *imageData = client.responseDataMap[(__bridge NSString *)(uniqueValue)]; 190 | if(!imageData){ 191 | imageData = [[NSMutableData alloc] init]; 192 | } 193 | [imageData appendBytes:buff length:length]; 194 | 195 | client.responseDataMap[(__bridge NSString *)(uniqueValue)] = imageData; 196 | }else if(type == kCFStreamEventEndEncountered){ 197 | 198 | void(^completionBlock)(NSData *) = client.responseMap[(__bridge NSString *)(uniqueValue)]; 199 | if (completionBlock) { 200 | NSData *resData = client.responseDataMap[(__bridge NSString *)(uniqueValue)]; 201 | // 9631bytes test:196403 202 | // 19356bytes test:746106 203 | completionBlock(resData); 204 | } 205 | 206 | CFReadStreamClose(stream); 207 | CFReadStreamUnscheduleFromRunLoop(stream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 208 | } 209 | } 210 | 211 | void postmyCallBack (CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo){ 212 | 213 | NSLog(@"myCallBack is %@",clientCallBackInfo); 214 | 215 | static NSMutableData* imageData = nil; 216 | 217 | if(type == kCFStreamEventHasBytesAvailable){ 218 | UInt8 buff[255]; 219 | long length = CFReadStreamRead(stream, buff, 255); 220 | NSLog(@"length is %ld",length); 221 | if(!imageData){ 222 | imageData = [[NSMutableData alloc] init]; 223 | } 224 | [imageData appendBytes:buff length:length]; 225 | 226 | 227 | }else if(type == kCFStreamEventEndEncountered){ 228 | 229 | NSString* resString = [[NSString alloc] initWithData:imageData encoding:NSUTF8StringEncoding]; 230 | NSLog(@"resString is %@",resString); 231 | CFReadStreamClose(stream); 232 | CFReadStreamUnscheduleFromRunLoop(stream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 233 | } 234 | } 235 | - (IBAction)testCFPostClient:(id)sender { 236 | [self testClientPOST]; 237 | } 238 | 239 | -(void)testClientPOST { 240 | CFStringRef urlStr = CFSTR("https://upload.api.weibo.com/2/statuses/upload.json"); 241 | 242 | //GET请求 243 | CFStringRef method= CFSTR("POST"); 244 | 245 | //构造URL 246 | CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL); 247 | 248 | //http请求 249 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, method, url, kCFHTTPVersion1_1); 250 | 251 | NSString *boundary = [NSString stringWithFormat:@"---------------------------14737809dasdasda2746641449"]; 252 | NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]; 253 | 254 | //OC的字符串要做C框架中使用需要__bridge桥接 255 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), (__bridge CFStringRef)(contentType)); 256 | 257 | NSData *bodyData = [self getRequestData]; 258 | CFHTTPMessageSetBody(request, (__bridge CFDataRef)(bodyData)); 259 | 260 | 261 | CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request); 262 | 263 | //设置流的context这里将self传入,用于以后的回调 264 | CFStreamClientContext ctxt = {0, (__bridge void *)(self), NULL, NULL, NULL}; 265 | 266 | CFOptionFlags event = kCFStreamEventHasBytesAvailable|kCFStreamEventEndEncountered; 267 | 268 | CFReadStreamSetClient(readStream,event,postmyCallBack,&ctxt); 269 | 270 | CFReadStreamScheduleWithRunLoop(readStream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 271 | 272 | CFReadStreamOpen(readStream); 273 | } 274 | 275 | // TODO: finish 276 | - (NSData *)getRequestData{ 277 | 278 | 279 | NSData *imageData = nil; 280 | 281 | 282 | //分隔符,注意要与上面请求头中的一致 283 | NSString *boundary = [NSString stringWithFormat:@"---------------------------14737809dasdasda2746641449"]; 284 | 285 | 286 | //定义可变Data; 287 | NSMutableData *body = [NSMutableData data]; 288 | 289 | //分隔符 290 | [body appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 291 | 292 | //需要发送的文字内容 293 | [body appendData:[@"Content-Disposition: form-data; name=\"status\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 294 | [body appendData:[@"需要发送的文字内容\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 295 | 296 | //分隔符 297 | [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 298 | 299 | //需要发送的图片内容 300 | [body appendData:[@"Content-Disposition: form-data; name=\"pic\"; filename=\"cat.png\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 301 | [body appendData:[@"Content-Type: image/png\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 302 | [body appendData:[NSData dataWithData:imageData]]; 303 | 304 | //分割符 305 | [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 306 | 307 | //access_token 向新浪微博发送内容,需要OAuth认证,这里必须带上access_token 308 | [body appendData:[@"Content-Disposition: form-data; name=\"access_token\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 309 | 310 | [body appendData:[@"2.00svaeojkrewe901dPLialB" dataUsingEncoding:NSUTF8StringEncoding]]; 311 | 312 | //结尾注意这里的分割符和前面不一样,--%@--后面多了两条--,如果少了会发送不成功哦 313 | [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 314 | 315 | return body; 316 | } 317 | 318 | @end 319 | -------------------------------------------------------------------------------- /cpapm/APM/Net/CFNetProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFNetProxy.h 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/15. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CFNetProxyModel.h" 11 | 12 | @interface CFNetProxy : NSObject 13 | 14 | @property (nonatomic, strong) NSMutableDictionary *requestMap; 15 | +(CFNetProxy *)sharedInstance; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /cpapm/APM/Net/CFNetProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFNetProxy.m 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/15. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import "CFNetProxy.h" 10 | #import "Hook_CFNetwork.h" 11 | 12 | @implementation CFNetProxy 13 | 14 | + (CFNetProxy *)sharedInstance 15 | { 16 | static CFNetProxy *instance = nil; 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | instance = [[CFNetProxy alloc] init]; 20 | instance.requestMap = @{}.mutableCopy; 21 | }); 22 | return instance; 23 | } 24 | @end 25 | -------------------------------------------------------------------------------- /cpapm/APM/Net/CFNetProxyModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFNetProxyModel.h 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/15. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /** 13 | proxy model remember cf hook call back and request send argv 14 | */ 15 | @interface CFNetProxyModel : NSObject 16 | 17 | @property (nonatomic, copy) NSString *requestId; // mark request 18 | 19 | @property (nonatomic, strong) NSValue *callbackPointer; // call back method pointer 20 | @property (nonatomic, strong) NSValue *contextPointer; // context pointer 21 | 22 | @property (nonatomic, strong) NSMutableDictionary *readStreamProperty; 23 | @property (nonatomic, strong) NSMutableDictionary *requestHeaderFields; 24 | @property (nonatomic, strong) NSMutableDictionary *responseHeaderFields; 25 | @property (nonatomic, strong) NSMutableData *responseData; 26 | @property (nonatomic, strong) NSMutableData *bufferData; 27 | 28 | +(NSString *)createReuestIdForRequest:(CFHTTPMessageRef)request; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /cpapm/APM/Net/CFNetProxyModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFNetProxyModel.m 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/15. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import "CFNetProxyModel.h" 10 | 11 | @implementation CFNetProxyModel 12 | 13 | +(NSString *)createReuestIdForRequest:(CFHTTPMessageRef)request { 14 | NSURL *url = (__bridge NSURL *)CFHTTPMessageCopyRequestURL(request); 15 | NSString *method = (__bridge NSString *)CFHTTPMessageCopyRequestMethod(request); 16 | NSTimeInterval nowTime = [[NSDate date] timeIntervalSince1970]; 17 | return [NSString stringWithFormat:@"%@_%@_%.2f",url.absoluteString, method, nowTime]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /cpapm/APM/Net/Hook_CFNetwork.h: -------------------------------------------------------------------------------- 1 | // 2 | // Hook_CFNetwork.h 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/12. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "CFNetProxy.h" 12 | #import "CFNet.h" 13 | 14 | #define CFRequest_KEY @"hook_requestId" 15 | 16 | // TODO: proxystream receive data and send data to target stream; 17 | static void hooked_proxyCallBack(CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo); 18 | void hooked_proxyCallBack(CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo){ 19 | 20 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPProxy)); 21 | NSString *requestId = proxyInfo[CFRequest_KEY]; 22 | NSMutableDictionary *proxyCallBackMap = [CFNetProxy sharedInstance].requestMap; 23 | 24 | CFReadStreamClientCallBack original_callback = NULL; 25 | if (clientCallBackInfo != NULL) { 26 | CFNetProxyModel *proxy = proxyCallBackMap[requestId]; 27 | if (proxy) { 28 | original_callback = [proxy.callbackPointer pointerValue]; 29 | }else{ 30 | NSLog(@"missed_data"); 31 | } 32 | } 33 | 34 | CFReadStreamRef readStream = stream; 35 | 36 | if(type == kCFStreamEventHasBytesAvailable){ 37 | 38 | CFNetProxyModel *proxy = proxyCallBackMap[requestId]; 39 | if (proxy) { 40 | // proxy receive data 41 | /** 42 | UInt8 buff[255]; 43 | long length = CFReadStreamRead(stream, buff, 255); 44 | NSMutableData *mutiData = proxy.bufferData; 45 | if(!mutiData){ 46 | mutiData = [[NSMutableData alloc] init]; 47 | } 48 | [mutiData appendBytes:buff length:length]; 49 | 50 | proxy.bufferData = mutiData; 51 | */ 52 | } 53 | 54 | }else if(type == kCFStreamEventEndEncountered){ 55 | 56 | // end data 57 | CFNetProxyModel *proxy = proxyCallBackMap[requestId]; 58 | if (proxy) { 59 | proxy.responseData = proxy.bufferData; 60 | } 61 | NSLog(@"proxy complete"); 62 | } 63 | 64 | if (original_callback != NULL) { 65 | original_callback(readStream, type, clientCallBackInfo); 66 | } 67 | } 68 | 69 | static Boolean (*original_CFReadStreamSetClient)(CFReadStreamRef, CFOptionFlags, CFReadStreamClientCallBack, CFStreamClientContext *); 70 | 71 | static Boolean wt_CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext); 72 | 73 | Boolean wt_CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext) { 74 | 75 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPProxy)); 76 | NSString *requestId = proxyInfo[CFRequest_KEY]; 77 | 78 | NSMutableDictionary *proxyCallBackMap = [CFNetProxy sharedInstance].requestMap; 79 | if (clientContext != NULL && clientContext->info != NULL) { 80 | NSValue *objectKey = [NSValue valueWithPointer:clientContext->info]; 81 | CFNetProxyModel *proxy = proxyCallBackMap[requestId]; 82 | if (proxy && proxy.callbackPointer == nil) { 83 | 84 | proxy.callbackPointer = [NSValue valueWithPointer:clientCB]; 85 | return original_CFReadStreamSetClient(stream, streamEvents, hooked_proxyCallBack, clientContext); 86 | } 87 | } 88 | 89 | return original_CFReadStreamSetClient(stream, streamEvents, clientCB, clientContext); 90 | } 91 | 92 | static CFReadStreamRef (*original_CFReadStreamCreateForHTTPRequest)(CFAllocatorRef, CFHTTPMessageRef); 93 | 94 | static CFReadStreamRef 95 | wt_CFReadStreamCreateForHTTPRequest(CFAllocatorRef __nullable alloc, CFHTTPMessageRef request); 96 | CFReadStreamRef wt_CFReadStreamCreateForHTTPRequest(CFAllocatorRef __nullable alloc, CFHTTPMessageRef request) { 97 | 98 | NSURL *url = (__bridge NSURL *)CFHTTPMessageCopyRequestURL(request); 99 | NSString *method = (__bridge NSString *)CFHTTPMessageCopyRequestMethod(request); 100 | NSString *requestId = [CFNetProxyModel createReuestIdForRequest:request]; 101 | CFReadStreamRef readStream = original_CFReadStreamCreateForHTTPRequest(alloc, request); 102 | 103 | CFReadStreamSetProperty(readStream, CFSTR("StreamID"), (__bridge CFTypeRef)requestId); 104 | 105 | // mark request 106 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPProxy)); 107 | NSMutableDictionary *userInfo = [proxyInfo mutableCopy]; 108 | if (userInfo == nil) { 109 | userInfo = @{}.mutableCopy; 110 | } 111 | userInfo[CFRequest_KEY] = requestId; 112 | CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, (__bridge CFTypeRef)(userInfo)); 113 | 114 | NSMutableDictionary *proxyCallBackMap = [CFNetProxy sharedInstance].requestMap; 115 | if (requestId.length > 0) { 116 | if (!proxyCallBackMap[requestId]) { 117 | CFNetProxyModel *model = [CFNetProxyModel new]; 118 | model.requestId = requestId; 119 | proxyCallBackMap[requestId] = model; 120 | } 121 | } 122 | return readStream; 123 | } 124 | 125 | // TODO: mark request status 126 | static Boolean (*original_CFReadStreamOpen)(CFReadStreamRef); 127 | static Boolean wt_CFReadStreamOpen(CFReadStreamRef stream); 128 | Boolean wt_CFReadStreamOpen(CFReadStreamRef stream) { 129 | 130 | return original_CFReadStreamOpen(stream); 131 | } 132 | 133 | // TODO: mark request status 134 | static Boolean (*original_CFReadStreamClose)(CFReadStreamRef); 135 | static Boolean wt_CFReadStreamClose(CFReadStreamRef stream); 136 | Boolean wt_CFReadStreamClose(CFReadStreamRef stream) { 137 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPProxy)); 138 | return original_CFReadStreamClose(stream); 139 | } 140 | 141 | static CFTypeRef (*original_CFReadStreamSetProperty)(CFReadStreamRef, CFStreamPropertyKey, CFTypeRef); 142 | static Boolean wt_CFReadStreamSetProperty(CFReadStreamRef stream, CFStreamPropertyKey propertyName, CFTypeRef propertyValue); 143 | Boolean wt_CFReadStreamSetProperty(CFReadStreamRef stream, CFStreamPropertyKey propertyName, CFTypeRef propertyValue) { 144 | 145 | if (propertyName == kCFStreamPropertyHTTPProxy) { 146 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPProxy)); 147 | NSMutableDictionary *userInfo = [proxyInfo mutableCopy]; 148 | if (userInfo == nil) { 149 | return original_CFReadStreamSetProperty(stream,propertyName, propertyValue); 150 | } 151 | id va = (__bridge id)propertyValue; 152 | if ([va isKindOfClass:[NSDictionary class]]) { 153 | NSDictionary *infoDict = va; 154 | for (NSString *key in [infoDict allKeys]) { 155 | id value = infoDict[key]; 156 | userInfo[key] = value; 157 | } 158 | } 159 | return original_CFReadStreamSetProperty(stream,propertyName, (__bridge CFTypeRef)userInfo); 160 | } 161 | 162 | return original_CFReadStreamSetProperty(stream,propertyName, propertyValue); 163 | } 164 | -------------------------------------------------------------------------------- /cpapm/APM/Net/Hook_CFNetwork.m: -------------------------------------------------------------------------------- 1 | // 2 | // Hook_CFNetwork.m 3 | // WellTangAPM 4 | // 5 | // Created by yangyouyong on 2018/1/12. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import "Hook_CFNetwork.h" 10 | 11 | -------------------------------------------------------------------------------- /cpapm/APM/Net/NetAnt.h: -------------------------------------------------------------------------------- 1 | // 2 | // NetAnt.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol NetAntDelegate 12 | 13 | -(void)netAntDidCatchRequest:(NSURLRequest *)request response:(NSURLResponse *)response data:(NSData *)data; 14 | 15 | @end 16 | 17 | @interface NetAnt : NSObject 18 | 19 | @property (nonatomic, assign) id delegate; 20 | 21 | +(BOOL)isWatching; 22 | 23 | +(void)addObserver:(id)delegate; 24 | 25 | +(void)removeObserver:(id)delegate; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /cpapm/APM/Net/NetAnt.m: -------------------------------------------------------------------------------- 1 | // 2 | // NetAnt.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "NetAnt.h" 10 | #import "NetProtocol.h" 11 | #import "NetAntURLSessionConfiguration.h" 12 | 13 | @implementation NetAnt 14 | 15 | +(BOOL)isWatching { 16 | return [NetProtocol delegates].count > 0; 17 | } 18 | 19 | +(void)addObserver:(id)delegate { 20 | if ([NetProtocol delegates].count <= 0) { 21 | [NetProtocol open]; 22 | [NetAntURLSessionConfiguration open]; 23 | } 24 | [NetProtocol addDelegate:delegate]; 25 | } 26 | 27 | +(void)removeObserver:(id)delegate { 28 | [NetProtocol removeDelegate:delegate]; 29 | if ([NetProtocol delegates].count <= 0) { 30 | [NetProtocol close]; 31 | [NetAntURLSessionConfiguration close]; 32 | } 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /cpapm/APM/Net/NetAntURLSessionConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // NetAntURLSessionConfiguration.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NetAntURLSessionConfiguration : NSObject 12 | 13 | @property (nonatomic,assign) BOOL isSwizzle; 14 | + (NetAntURLSessionConfiguration *)defaultConfiguration; 15 | 16 | +(void)open; 17 | +(void)close; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /cpapm/APM/Net/NetAntURLSessionConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // NetAntURLSessionConfiguration.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "NetAntURLSessionConfiguration.h" 10 | #import 11 | #import "NetProtocol.h" 12 | 13 | @implementation NetAntURLSessionConfiguration 14 | 15 | + (NetAntURLSessionConfiguration *)defaultConfiguration { 16 | 17 | static NetAntURLSessionConfiguration *staticConfiguration; 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | staticConfiguration=[[NetAntURLSessionConfiguration alloc] init]; 21 | }); 22 | return staticConfiguration; 23 | } 24 | 25 | +(void)open { 26 | NetAntURLSessionConfiguration *defaultInstance = [NetAntURLSessionConfiguration defaultConfiguration]; 27 | if (![defaultInstance isSwizzle]) { 28 | [defaultInstance load]; 29 | } 30 | } 31 | 32 | +(void)close { 33 | NetAntURLSessionConfiguration *defaultInstance = [NetAntURLSessionConfiguration defaultConfiguration]; 34 | if ([defaultInstance isSwizzle]) { 35 | [defaultInstance unload]; 36 | } 37 | } 38 | 39 | - (instancetype)init { 40 | self = [super init]; 41 | if (self) { 42 | self.isSwizzle=NO; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)load { 48 | 49 | self.isSwizzle=YES; 50 | Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration"); 51 | [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]]; 52 | 53 | } 54 | 55 | - (void)unload { 56 | 57 | self.isSwizzle=NO; 58 | Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration"); 59 | [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]]; 60 | 61 | } 62 | 63 | - (void)swizzleSelector:(SEL)selector fromClass:(Class)original toClass:(Class)stub { 64 | 65 | Method originalMethod = class_getInstanceMethod(original, selector); 66 | Method stubMethod = class_getInstanceMethod(stub, selector); 67 | if (!originalMethod || !stubMethod) { 68 | [NSException raise:NSInternalInconsistencyException format:@"Couldn't load NEURLSessionConfiguration."]; 69 | } 70 | method_exchangeImplementations(originalMethod, stubMethod); 71 | } 72 | 73 | //如果需要导入其他的自定义NSURLProtocol请在这里增加,当然在使用NSURLSessionConfiguration时增加也可以 74 | - (NSArray *)protocolClasses { 75 | 76 | return @[[NetProtocol class]]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /cpapm/APM/Net/NetProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // NetProtocol.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NetAnt.h" 11 | 12 | @interface NetProtocol : NSURLProtocol 13 | 14 | +(void)open; 15 | +(void)close; 16 | 17 | +(void)addDelegate:(id)delegate; 18 | +(void)removeDelegate:(id)delegate; 19 | 20 | +(NSArray *)delegates; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /cpapm/APM/Net/NetProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // NetProtocol.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "NetProtocol.h" 10 | 11 | @interface NetProtocol() 12 | 13 | @property (nonatomic, strong) NSURLConnection *connection; 14 | @property (nonatomic, strong) NSURLRequest *ant_request; 15 | @property (nonatomic, strong) NSURLResponse *ant_response; 16 | @property (nonatomic, strong) NSMutableData *ant_data; 17 | 18 | @end 19 | 20 | static NSMutableArray *delegates = nil; 21 | static NSString *greenCard = @"greenCard"; 22 | 23 | @implementation NetProtocol 24 | 25 | +(void)open { 26 | [NSURLProtocol registerClass:[self class]]; 27 | } 28 | 29 | +(void)close { 30 | [NSURLProtocol unregisterClass:[self class]]; 31 | } 32 | 33 | +(void)addDelegate:(id)delegate { 34 | if (!delegates) { 35 | delegates = @[].mutableCopy; 36 | } 37 | BOOL contains = [delegates containsObject:delegate]; 38 | if (!contains) { 39 | [delegates addObject:delegate]; 40 | } 41 | } 42 | 43 | +(void)removeDelegate:(id)delegate { 44 | [delegates removeObject:delegate]; 45 | } 46 | 47 | +(NSArray *)delegates { 48 | return delegates; 49 | } 50 | 51 | #pragma mark -- override 52 | +(BOOL)canInitWithRequest:(NSURLRequest *)request { 53 | 54 | if (![request.URL.scheme isEqualToString:@"http"] && 55 | ![request.URL.scheme isEqualToString:@"https"]) { 56 | return NO; 57 | } 58 | 59 | if ([NSURLProtocol propertyForKey:greenCard inRequest:request] ) { 60 | return NO; 61 | } 62 | return YES; 63 | } 64 | 65 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { 66 | 67 | NSMutableURLRequest *mutableReqeust = [request mutableCopy]; 68 | [NSURLProtocol setProperty:@YES 69 | forKey:greenCard 70 | inRequest:mutableReqeust]; 71 | return [mutableReqeust copy]; 72 | } 73 | 74 | -(void)startLoading { 75 | 76 | NSURLRequest *request = [NetProtocol canonicalRequestForRequest:self.request]; 77 | 78 | #pragma clang diagnostic push 79 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 80 | self.connection = [NSURLConnection connectionWithRequest:request delegate:self]; 81 | #pragma clang diagnostic pop 82 | 83 | self.ant_request = self.request; 84 | } 85 | 86 | -(void)stopLoading { 87 | [self.connection cancel]; 88 | for (id delegate in delegates) { 89 | [delegate netAntDidCatchRequest:self.ant_request response:self.ant_response data:self.ant_data]; 90 | } 91 | } 92 | 93 | #pragma mark - connection delegate & data 94 | 95 | -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 96 | [self.client URLProtocol:self didFailWithError:error]; 97 | } 98 | 99 | -(BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection { 100 | return true; 101 | } 102 | 103 | -(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 104 | [self.client URLProtocol:self didReceiveAuthenticationChallenge:challenge]; 105 | } 106 | 107 | -(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 108 | [self.client URLProtocol:self didCancelAuthenticationChallenge:challenge]; 109 | } 110 | 111 | #pragma mark - data 112 | 113 | -(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response { 114 | if (response != nil) { 115 | self.ant_response = response; 116 | [self.client URLProtocol:self wasRedirectedToRequest:request redirectResponse:response]; 117 | } 118 | return request; 119 | } 120 | 121 | -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 122 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed]; 123 | self.ant_response = response; 124 | } 125 | 126 | -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 127 | [self.client URLProtocol:self didLoadData:data]; 128 | if (self.ant_data == nil) { 129 | self.ant_data = [data mutableCopy]; 130 | }else{ 131 | [self.ant_data appendData:data]; 132 | } 133 | } 134 | 135 | -(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { 136 | return cachedResponse; 137 | } 138 | 139 | -(void)connectionDidFinishLoading:(NSURLConnection *)connection { 140 | [self.client URLProtocolDidFinishLoading:self]; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /cpapm/APM/Net/SocketBasedRequest/CHttpRequest.c: -------------------------------------------------------------------------------- 1 | // 2 | // CHttpRequest.c 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/29. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #include "CHttpRequest.h" 10 | 11 | //int main(int argc, char const *argv[]) 12 | //{ 13 | // int sockfd, portno; 14 | // struct sockaddr_in addr; 15 | // 16 | // portno = 80; 17 | // sockfd = socket(AF_INET, SOCK_STREAM, 0); 18 | // if(sockfd < 0) { 19 | // error("ERROR opening socket"); 20 | // } 21 | // 22 | // memset((char*)&addr, 0, sizeof(addr)); 23 | // 24 | // addr.sin_port = htons(portno); 25 | // addr.sin_family = AF_INET; 26 | // inet_pton(AF_INET, "183.63.251.70", &addr.sin_addr); 27 | // 28 | // if(connect(sockfd, (struct sockaddr *)&addr, sizeof addr) < 0) { 29 | // close(sockfd); 30 | // error("ERROR connecting to server"); 31 | // } 32 | // 33 | // char request[] = "GET /SingleWindow/SingleWindowMessageService.svc/json/ HTTP/1.1\r\nHost: 183.63.251.70\r\nConnection: close\r\n\r\n"; 34 | // char response[1024]; 35 | // 36 | // send(sockfd, request, sizeof(request), 0); 37 | // 38 | // while(recv(sockfd, response,sizeof(response), 0) > 0) { 39 | // printf(response); 40 | // } 41 | // 42 | // close(sockfd); 43 | // return 0; 44 | // 45 | //} 46 | 47 | 48 | -------------------------------------------------------------------------------- /cpapm/APM/Net/SocketBasedRequest/CHttpRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHttpRequest.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/29. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #ifndef CHttpRequest_h 10 | #define CHttpRequest_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | static inline const char *strAppend(const char *, const char *) __attribute__ ((always_inline)); 24 | static inline const char *getIpWithDomain(const char *); 25 | 26 | 27 | void error(const char *msg) 28 | { 29 | perror(msg); 30 | exit(1); 31 | } 32 | 33 | typedef void (*ResponseCallBack)(const char*, char *, int); 34 | 35 | static void getRequest(const char* requestIdentifier, const char *protocol, const char *domain, const char *path , ResponseCallBack responseCallBack); 36 | void getRequest(const char* requestIdentifier, const char *protocol, const char *domain, const char *path , ResponseCallBack responseCallBack) { 37 | 38 | int sockfd; 39 | struct sockaddr addr; 40 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 41 | if(sockfd < 0) { 42 | error("ERROR opening socket"); 43 | } 44 | 45 | void *identifier = malloc(strlen(requestIdentifier) + 1); 46 | char *cId = (char *)memcpy(identifier, requestIdentifier, strlen(requestIdentifier)); 47 | cId[strlen(requestIdentifier)] = '\0'; 48 | 49 | memset((char*)&addr, 0, sizeof(addr)); 50 | 51 | struct addrinfo *info; 52 | 53 | // 默认80 端口 54 | if (getaddrinfo(domain, "80", NULL, &info) != 0) { 55 | printf("get domain error"); 56 | } 57 | addr = *info->ai_addr; 58 | 59 | if(connect(sockfd, &addr, sizeof addr) < 0) { 60 | close(sockfd); 61 | error("ERROR connecting to server"); 62 | } 63 | 64 | 65 | // format request eg: "GET /ip HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: runscope/0.1\r\nHost: httpbin.org\r\nContent-Length: 0\r\n\r\n"; 66 | 67 | const char *request = strAppend(NULL,(const char *)"GET "); 68 | request = strAppend(request, path); // path contains parms 69 | request = strAppend(request, " HTTP/1.1\r\n"); // cat protocol 70 | request = strAppend(request, "Host:"); // cat host 71 | request = strAppend(request, domain); 72 | request = strAppend(request, "\r\n"); 73 | request = strAppend(request, "Connection: close\r\n\r\n"); 74 | 75 | char response[1024]; 76 | char splitResponse[1024]; 77 | send(sockfd, request, strlen(request), 0); 78 | 79 | while(recv(sockfd, response,sizeof(response), 0) > 0) { 80 | // parse first line 81 | // parse headers 82 | // 倒序查找最后一组 \r\n 的位置 slice body 83 | printf("response: %s\n",response); 84 | 85 | ////////////////////////parse body begin///////////////////// 86 | 87 | char *pstr = NULL; 88 | 89 | strncpy(splitResponse, response, strlen(response)); 90 | char *separator = "\r"; 91 | int startIndex = 0; 92 | int endIndex = 0; 93 | 94 | char charlist[1024]; 95 | int i = 0; 96 | char *substr= strtok(response, separator); 97 | while (substr != NULL) { 98 | strcpy(charlist,substr); 99 | startIndex += strlen(substr); 100 | substr = strtok(NULL,separator); 101 | int responseLength = strlen(response); 102 | if (startIndex < responseLength - 2) { 103 | char nextTwoString[3]; 104 | nextTwoString[2] = '\0'; 105 | strncpy(nextTwoString, response, startIndex); 106 | printf("nextTwo:%s",nextTwoString); 107 | if (strcmp(nextTwoString, "\r\n") == 0) { 108 | printf("bodybegin"); 109 | strncpy(charlist, response, strlen(response) - startIndex); 110 | charlist[strlen(response) - startIndex + 1] = '\0'; 111 | } 112 | } 113 | i ++; 114 | } 115 | 116 | 117 | // pstr = strtok((char *)temp, "\r\n"); 118 | // while (pstr != NULL) { 119 | // 120 | // pstr = strtok((char *)temp, "\r\n"); 121 | // } 122 | printf("finilized body:%s",charlist); 123 | printf("startIndex: %d",startIndex); 124 | printf("length: %d",strlen(splitResponse)); 125 | i = strlen(charlist); 126 | while (i > 0) { 127 | char str = charlist[i]; 128 | if (str == '}') { 129 | endIndex = i; 130 | break; 131 | } 132 | i --; 133 | } 134 | printf("endIndex: %d",endIndex); 135 | int bodyLenght = endIndex - startIndex; 136 | char destiny[1024]; 137 | strncpy(destiny , charlist, strlen(splitResponse) - endIndex); 138 | destiny[bodyLenght + 1] = '\0'; 139 | // strncpy(charlist, response, strlen(response) - startIndex); 140 | // char *ss = strstr((char *)response, "\n\r"); 141 | 142 | ////////////////////////parse body end///////////////////// 143 | 144 | // 解析response header 145 | if (responseCallBack) { 146 | responseCallBack((const char *)cId, destiny, 0); // append data outside 147 | } 148 | } 149 | 150 | if (responseCallBack) { 151 | responseCallBack((const char *)cId, NULL, 1); // append data outside 152 | } 153 | 154 | close(sockfd); 155 | 156 | } 157 | 158 | const char* strAppend(const char *s1, const char *s2) 159 | { 160 | if (s1 == NULL) { 161 | void *result = malloc(strlen(s2)+1); 162 | strcpy((char *)result, s2); 163 | return (char *)result; 164 | } 165 | void *result = malloc(strlen(s1)+strlen(s2)+1); 166 | if (result == NULL) exit (1); 167 | 168 | strcpy((char *)result, s1); 169 | strcat((char *)result, s2); 170 | 171 | return (char *)result; 172 | } 173 | 174 | const char *getIpWithDomain(const char *domain) { 175 | struct hostent *hs; 176 | struct sockaddr_in server; 177 | hs = gethostbyname(domain); 178 | if (hs != NULL) { 179 | server.sin_addr = *((struct in_addr*)hs->h_addr_list[0]); 180 | return inet_ntoa(server.sin_addr); 181 | } 182 | return NULL; 183 | } 184 | //static void syncGetRequest(const char *protocol, const char *domain, const char *path); 185 | 186 | #endif /* CHttpRequest_h */ 187 | -------------------------------------------------------------------------------- /cpapm/APM/Net/SocketBasedRequest/HTTPRequest.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPRequest 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | typedef int socket_t; 23 | 24 | #define INVALID_SOCKET -1 25 | 26 | namespace http 27 | { 28 | inline int getLastError() 29 | { 30 | return errno; 31 | } 32 | 33 | 34 | // encode url 35 | inline std::string urlEncode(const std::string& str) 36 | { 37 | static const std::map entities = { 38 | {' ', "%20"}, 39 | {'!', "%21"}, 40 | {'"', "%22"}, 41 | {'*', "%2A"}, 42 | {'\'', "%27"}, 43 | {'(', "%28"}, 44 | {')', "%29"}, 45 | {';', "%3B"}, 46 | {':', "%3A"}, 47 | {'@', "%40"}, 48 | {'&', "%26"}, 49 | {'=', "%3D"}, 50 | {'+', "%2B"}, 51 | {'$', "%24"}, 52 | {',', "%2C"}, 53 | {'/', "%2F"}, 54 | {'?', "%3F"}, 55 | {'%', "%25"}, 56 | {'#', "%23"}, 57 | {'<', "%3C"}, 58 | {'>', "%3E"}, 59 | {'[', "%5B"}, 60 | {'\\', "%5C"}, 61 | {']', "%5D"}, 62 | {'^', "%5E"}, 63 | {'`', "%60"}, 64 | {'{', "%7B"}, 65 | {'|', "%7C"}, 66 | {'}', "%7D"}, 67 | {'~', "%7E"} 68 | }; 69 | 70 | static const char hexChars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 71 | 72 | std::string result; 73 | 74 | for (auto i = str.begin(); i != str.end(); ++i) 75 | { 76 | uint32_t cp = *i & 0xff; 77 | 78 | if (cp <= 0x7f) // length = 1 79 | { 80 | auto entity = entities.find(*i); 81 | if (entity == entities.end()) 82 | { 83 | result += static_cast(cp); 84 | } 85 | else 86 | { 87 | result += entity->second; 88 | } 89 | } 90 | else if ((cp >> 5) == 0x6) // length = 2 91 | { 92 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 93 | if (++i == str.end()) break; 94 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 95 | } 96 | else if ((cp >> 4) == 0xe) // length = 3 97 | { 98 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 99 | if (++i == str.end()) break; 100 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 101 | if (++i == str.end()) break; 102 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 103 | } 104 | else if ((cp >> 3) == 0x1e) // length = 4 105 | { 106 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 107 | if (++i == str.end()) break; 108 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 109 | if (++i == str.end()) break; 110 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 111 | if (++i == str.end()) break; 112 | result += std::string("%") + hexChars[(*i & 0xf0) >> 4] + hexChars[*i & 0x0f]; 113 | } 114 | } 115 | 116 | return result; 117 | } 118 | 119 | struct Response 120 | { 121 | bool succeeded = false; 122 | int code = 0; 123 | std::vector headers; 124 | std::vector body; 125 | }; 126 | 127 | class Request 128 | { 129 | public: 130 | Request(const std::string& url) 131 | { 132 | size_t protocolEndPosition = url.find("://"); 133 | 134 | if (protocolEndPosition != std::string::npos) 135 | { 136 | protocol = url.substr(0, protocolEndPosition); 137 | std::transform(protocol.begin(), protocol.end(), protocol.begin(), ::tolower); 138 | 139 | std::string::size_type pathPosition = url.find('/', protocolEndPosition + 3); 140 | 141 | if (pathPosition == std::string::npos) 142 | { 143 | domain = url.substr(protocolEndPosition + 3); 144 | } 145 | else 146 | { 147 | domain = url.substr(protocolEndPosition + 3, pathPosition - protocolEndPosition - 3); 148 | path = url.substr(pathPosition); 149 | } 150 | 151 | std::string::size_type portPosition = domain.find(':'); 152 | 153 | if (portPosition != std::string::npos) 154 | { 155 | port = domain.substr(portPosition + 1); 156 | domain.resize(portPosition); 157 | } 158 | } 159 | } 160 | 161 | ~Request() 162 | { 163 | if (socketFd != INVALID_SOCKET) 164 | { 165 | int result = close(socketFd); 166 | 167 | if (result < 0) 168 | { 169 | int error = getLastError(); 170 | std::cerr << "Failed to close socket, error: " << error << std::endl; 171 | } 172 | } 173 | } 174 | 175 | Request(const Request& request) = delete; 176 | Request(Request&& request) = delete; 177 | Request& operator=(const Request& request) = delete; 178 | Request& operator=(Request&& request) = delete; 179 | 180 | Response send(const std::string& method, 181 | const std::map& parameters, 182 | const std::vector& headers = {}) 183 | { 184 | std::string body; 185 | bool first = true; 186 | 187 | for (const auto& parameter : parameters) 188 | { 189 | if (!first) body += "&"; 190 | first = false; 191 | 192 | body += urlEncode(parameter.first) + "=" + urlEncode(parameter.second); 193 | } 194 | 195 | return send(method, body, headers); 196 | } 197 | 198 | Response send(const std::string& method, 199 | const std::string& body = "", 200 | const std::vector& headers = {}) 201 | { 202 | Response response; 203 | 204 | if (protocol != "http") 205 | { 206 | std::cerr << "Only HTTP protocol is supported" << std::endl; 207 | return response; 208 | } 209 | 210 | if (socketFd != INVALID_SOCKET) 211 | { 212 | int result = ::close(socketFd); 213 | 214 | socketFd = INVALID_SOCKET; 215 | 216 | if (result < 0) 217 | { 218 | int error = getLastError(); 219 | std::cerr << "Failed to close socket, error: " << error << std::endl; 220 | return response; 221 | } 222 | } 223 | 224 | socketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 225 | 226 | 227 | if (socketFd == INVALID_SOCKET) 228 | { 229 | int error = getLastError(); 230 | std::cerr << "Failed to create socket, error: " << error << std::endl; 231 | return response; 232 | } 233 | 234 | addrinfo* info; 235 | if (getaddrinfo(domain.c_str(), port.empty() ? nullptr : port.c_str(), nullptr, &info) != 0) 236 | { 237 | int error = getLastError(); 238 | std::cerr << "Failed to get address info of " << domain << ", error: " << error << std::endl; 239 | return response; 240 | } 241 | 242 | sockaddr addr = *info->ai_addr; 243 | 244 | freeaddrinfo(info); 245 | 246 | if (::connect(socketFd, &addr, sizeof(addr)) < 0) 247 | { 248 | int error = getLastError(); 249 | 250 | std::cerr << "Failed to connect to " << domain << ":" << port << ", error: " << error << std::endl; 251 | return response; 252 | } 253 | else 254 | { 255 | std::cerr << "Connected to " << domain << ":" << port << std::endl; 256 | } 257 | 258 | std::string requestData = method + " " + path + " HTTP/1.1\r\n"; 259 | 260 | for (const std::string& header : headers) 261 | { 262 | requestData += header + "\r\n"; 263 | } 264 | 265 | requestData += "Host: " + domain + "\r\n"; 266 | requestData += "Content-Length: " + std::to_string(body.size()) + "\r\n"; 267 | 268 | requestData += "\r\n"; 269 | requestData += body; 270 | 271 | int flags = 0; 272 | 273 | ssize_t remaining = static_cast(requestData.size()); 274 | ssize_t sent = 0; 275 | ssize_t size; 276 | 277 | do 278 | { 279 | size = ::send(socketFd, requestData.data() + sent, remaining, flags); 280 | 281 | if (size < 0) 282 | { 283 | int error = getLastError(); 284 | std::cerr << "Failed to send data to " << domain << ":" << port << ", error: " << error << std::endl; 285 | return response; 286 | } 287 | 288 | remaining -= size; 289 | sent += size; 290 | } 291 | while (remaining > 0); 292 | 293 | uint8_t TEMP_BUFFER[65536]; 294 | const std::vector clrf = {'\r', '\n'}; 295 | std::vector responseData; 296 | bool firstLine = true; 297 | bool parsedHeaders = false; 298 | int contentSize = -1; 299 | bool chunkedResponse = false; 300 | size_t expectedChunkSize = 0; 301 | bool removeCLRFAfterChunk = false; 302 | 303 | do 304 | { 305 | size = recv(socketFd, reinterpret_cast(TEMP_BUFFER), sizeof(TEMP_BUFFER), flags); 306 | 307 | if (size < 0) 308 | { 309 | int error = getLastError(); 310 | std::cerr << "Failed to read data from " << domain << ":" << port << ", error: " << error << std::endl; 311 | return response; 312 | } 313 | else if (size == 0) 314 | { 315 | // disconnected 316 | break; 317 | } 318 | 319 | responseData.insert(responseData.end(), std::begin(TEMP_BUFFER), std::begin(TEMP_BUFFER) + size); 320 | 321 | if (!parsedHeaders) 322 | { 323 | for (;;) 324 | { 325 | std::vector::iterator i = std::search(responseData.begin(), responseData.end(), clrf.begin(), clrf.end()); 326 | 327 | // didn't find a newline 328 | if (i == responseData.end()) break; 329 | 330 | std::string line(responseData.begin(), i); 331 | responseData.erase(responseData.begin(), i + 2); 332 | 333 | // empty line indicates the end of the header section 334 | if (line.empty()) 335 | { 336 | parsedHeaders = true; 337 | break; 338 | } 339 | else if (firstLine) // first line 340 | { 341 | firstLine = false; 342 | 343 | std::string::size_type pos, lastPos = 0, length = line.length(); 344 | std::vector parts; 345 | 346 | // tokenize first line 347 | while (lastPos < length + 1) 348 | { 349 | pos = line.find(' ', lastPos); 350 | if (pos == std::string::npos) pos = length; 351 | 352 | if (pos != lastPos) 353 | { 354 | parts.push_back(std::string(line.data() + lastPos, 355 | static_cast::size_type>(pos) - lastPos)); 356 | } 357 | 358 | lastPos = pos + 1; 359 | } 360 | 361 | if (parts.size() >= 2) 362 | { 363 | response.code = std::stoi(parts[1]); 364 | } 365 | } 366 | else // headers 367 | { 368 | response.headers.push_back(line); 369 | 370 | std::string::size_type pos = line.find(':'); 371 | 372 | if (pos != std::string::npos) 373 | { 374 | std::string headerName = line.substr(0, pos); 375 | std::string headerValue = line.substr(pos + 1); 376 | 377 | // ltrim 378 | headerValue.erase(headerValue.begin(), 379 | std::find_if(headerValue.begin(), headerValue.end(), 380 | std::not1(std::ptr_fun(std::isspace)))); 381 | 382 | // rtrim 383 | headerValue.erase(std::find_if(headerValue.rbegin(), headerValue.rend(), 384 | std::not1(std::ptr_fun(std::isspace))).base(), 385 | headerValue.end()); 386 | 387 | if (headerName == "Content-Length") 388 | { 389 | contentSize = std::stoi(headerValue); 390 | } 391 | else if (headerName == "Transfer-Encoding" && headerValue == "chunked") 392 | { 393 | chunkedResponse = true; 394 | } 395 | } 396 | } 397 | } 398 | } 399 | 400 | if (parsedHeaders) 401 | { 402 | if (chunkedResponse) 403 | { 404 | bool dataReceived = false; 405 | for (;;) 406 | { 407 | if (expectedChunkSize > 0) 408 | { 409 | auto toWrite = std::min(expectedChunkSize, responseData.size()); 410 | response.body.insert(response.body.end(), responseData.begin(), responseData.begin() + toWrite); 411 | responseData.erase(responseData.begin(), responseData.begin() + toWrite); 412 | expectedChunkSize -= toWrite; 413 | 414 | if (expectedChunkSize == 0) removeCLRFAfterChunk = true; 415 | if (responseData.empty()) break; 416 | } 417 | else 418 | { 419 | if (removeCLRFAfterChunk) 420 | { 421 | if (responseData.size() >= 2) 422 | { 423 | removeCLRFAfterChunk = false; 424 | responseData.erase(responseData.begin(), responseData.begin() + 2); 425 | } 426 | else break; 427 | } 428 | 429 | auto i = std::search(responseData.begin(), responseData.end(), clrf.begin(), clrf.end()); 430 | 431 | if (i == responseData.end()) break; 432 | 433 | std::string line(responseData.begin(), i); 434 | responseData.erase(responseData.begin(), i + 2); 435 | 436 | expectedChunkSize = std::stoul(line, 0, 16); 437 | 438 | if (expectedChunkSize == 0) 439 | { 440 | dataReceived = true; 441 | break; 442 | } 443 | } 444 | } 445 | 446 | if (dataReceived) 447 | { 448 | break; 449 | } 450 | } 451 | else 452 | { 453 | response.body.insert(response.body.end(), responseData.begin(), responseData.end()); 454 | responseData.clear(); 455 | 456 | // got the whole content 457 | if (contentSize == -1 || response.body.size() >= contentSize) 458 | { 459 | break; 460 | } 461 | } 462 | } 463 | } 464 | while (size > 0); 465 | 466 | response.succeeded = true; 467 | 468 | return response; 469 | } 470 | 471 | private: 472 | std::string protocol; 473 | std::string domain; 474 | std::string port = "80"; 475 | std::string path; 476 | socket_t socketFd = INVALID_SOCKET; 477 | }; 478 | } 479 | -------------------------------------------------------------------------------- /cpapm/APM/Net/SocketBasedRequest/XXNetwork.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXNetwork.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/26. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^CompletionBlock)(NSData *, NSError *); 12 | 13 | @interface XXNetwork : NSObject 14 | 15 | @property (nonatomic, strong, readonly) NSMutableDictionary *requestTaskMap; 16 | 17 | + (XXNetwork *)sharedInstance; 18 | 19 | -(void)GET:(NSString *)url 20 | parms:(NSDictionary *)parms 21 | completion:(CompletionBlock)completionBlock; 22 | 23 | @end 24 | 25 | @interface RequestTask: NSObject 26 | 27 | @property (nonatomic, copy) CompletionBlock completionBlock; 28 | @property (nonatomic, strong) NSString *requestIdentifier; 29 | @property (nonatomic, strong, readonly) NSData *responseData; 30 | 31 | -(void)appendData:(NSData *)data; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /cpapm/APM/Net/SocketBasedRequest/XXNetwork.mm: -------------------------------------------------------------------------------- 1 | // 2 | // XXNetwork.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/26. 6 | // Copyright © 2018年 welltang. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "HTTPRequest.hpp" 12 | #import "XXNetwork.h" 13 | #import "CHttpRequest.h" 14 | 15 | void responseCallBack(const char *requestIdentifier, char *resData, int status){ 16 | // completion 17 | NSString *identifier = [NSString stringWithUTF8String:requestIdentifier]; 18 | RequestTask *task = [XXNetwork sharedInstance].requestTaskMap[identifier]; 19 | if (status != 1) { 20 | NSData *data = [NSData dataWithBytes:resData length:strlen(resData)]; 21 | [task appendData:data]; 22 | }else{ 23 | CompletionBlock completion = task.completionBlock; 24 | if (completion) { 25 | completion(task.responseData, nil); 26 | } 27 | } 28 | 29 | } 30 | 31 | @interface XXNetwork() 32 | 33 | @property (nonatomic, strong) NSOperationQueue *queue; 34 | @property (nonatomic, strong, readwrite) NSMutableDictionary *requestTaskMap; 35 | 36 | @end 37 | 38 | @implementation XXNetwork 39 | 40 | + (XXNetwork *)sharedInstance 41 | { 42 | static XXNetwork *sharedInstance = nil; 43 | static dispatch_once_t onceToken; 44 | dispatch_once(&onceToken, ^{ 45 | sharedInstance = [[XXNetwork alloc] init]; 46 | sharedInstance.queue = [[NSOperationQueue alloc] init]; 47 | sharedInstance.requestTaskMap = @{}.mutableCopy; 48 | }); 49 | 50 | return sharedInstance; 51 | } 52 | 53 | 54 | -(void)GET:(NSString *)url 55 | parms:(NSDictionary *)parms 56 | completion:(CompletionBlock)completionBlock { 57 | 58 | NSDate *now = [NSDate date]; 59 | NSString *requestTaskIdentifier = [url stringByAppendingString:[NSString stringWithFormat:@"_%.2f",[now timeIntervalSince1970]]]; 60 | RequestTask *requestTask = [RequestTask new]; 61 | requestTask.requestIdentifier = requestTaskIdentifier; 62 | requestTask.completionBlock = [completionBlock copy]; 63 | self.requestTaskMap[requestTaskIdentifier] = requestTask; 64 | 65 | NSBlockOperation *requestOperation = [NSBlockOperation blockOperationWithBlock:^{ 66 | 67 | /* 68 | std::string requestUrl = [url UTF8String]; 69 | std::string method = "GET"; 70 | std::string arguments = ""; // parms format 71 | 72 | http::Request request(requestUrl); 73 | 74 | http::Response response = request.send(method, arguments, { 75 | "Content-Type: application/x-www-form-urlencoded", 76 | "User-Agent: runscope/0.1" 77 | }); 78 | 79 | if (response.succeeded) 80 | { 81 | NSData *responseData = [NSData dataWithBytes:response.body.data() length:response.body.size()]; 82 | CompletionBlock cpBlock = self.requestTaskMap[requestTaskIdentifier]; 83 | if (cpBlock) { 84 | cpBlock(responseData, nil); 85 | self.requestTaskMap[requestTaskIdentifier] = nil; 86 | } 87 | } 88 | else 89 | { 90 | CompletionBlock cpBlock = self.requestTaskMap[requestTaskIdentifier]; 91 | if (cpBlock) { 92 | NSError *error = [NSError errorWithDomain:@"Request failed" 93 | code:0 94 | userInfo:nil]; 95 | cpBlock(nil, error); 96 | self.requestTaskMap[requestTaskIdentifier] = nil; 97 | } 98 | } 99 | */ 100 | 101 | NSString *sliceString = @"://"; 102 | NSRange range = [url rangeOfString:sliceString]; 103 | NSString *clippedString = [url substringFromIndex:(range.location + range.length)]; 104 | NSRange domainEndRange = [clippedString rangeOfString:@"/"]; 105 | NSString *domain = [clippedString substringToIndex:domainEndRange.location]; 106 | NSString *path = [clippedString substringFromIndex:domainEndRange.location]; 107 | // if (![[path substringFromIndex:(path.length - 2)] isEqualToString:@"/"]) { 108 | // path = [path stringByAppendingString:@"/"]; 109 | // } 110 | 111 | 112 | // link requestId and call back data 113 | 114 | getRequest([requestTaskIdentifier UTF8String],"HTTP/1.1", [domain UTF8String], [path UTF8String], responseCallBack); 115 | 116 | }]; 117 | 118 | [self.queue addOperation:requestOperation]; 119 | } 120 | 121 | @end 122 | 123 | @interface RequestTask() 124 | 125 | @property (nonatomic, strong, readwrite) NSData *responseData; 126 | @property (nonatomic, strong) NSMutableData *processData; 127 | 128 | @end 129 | 130 | @implementation RequestTask 131 | 132 | -(NSData *)responseData { 133 | return [NSData dataWithData:self.processData]; 134 | } 135 | 136 | -(void)appendData:(NSData *)data { 137 | if (self.processData) { 138 | [self.processData appendData:data]; 139 | }else{ 140 | self.processData = [data mutableCopy]; 141 | } 142 | } 143 | 144 | @end 145 | -------------------------------------------------------------------------------- /cpapm/APP/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /cpapm/APP/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "CrashCatcher.h" 11 | #import "WTLeaksDetector.h" 12 | #import "CFNet.h" 13 | 14 | #import "XXNetwork.h" 15 | 16 | @interface AppDelegate () 17 | 18 | @end 19 | 20 | @implementation AppDelegate 21 | 22 | 23 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 24 | 25 | // [[PLeakSniffer sharedInstance] installLeakSniffer]; 26 | [[CrashCatcher sharedInstance] setOpen:true]; 27 | [[WTLeaksDetector sharedInstance] startDetector]; 28 | [CFNet install]; 29 | NSLog(@"home: %@",NSHomeDirectory()); 30 | 31 | for (int i = 0; i < 1; i++) { 32 | NSLog(@"request: %d",i); 33 | [[XXNetwork sharedInstance] GET:@"http://httpbin.org/ip" parms:nil completion:^(NSData *responseData, NSError *error) { 34 | id json = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil]; 35 | if (json == nil) { 36 | NSString *str = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; 37 | NSLog(@"%@",str); 38 | } 39 | NSLog(@"response: %@",json); 40 | }]; 41 | } 42 | 43 | return YES; 44 | } 45 | 46 | 47 | - (void)applicationWillResignActive:(UIApplication *)application { 48 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 49 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 50 | } 51 | 52 | 53 | - (void)applicationDidEnterBackground:(UIApplication *)application { 54 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 55 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 56 | } 57 | 58 | 59 | - (void)applicationWillEnterForeground:(UIApplication *)application { 60 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 61 | } 62 | 63 | 64 | - (void)applicationDidBecomeActive:(UIApplication *)application { 65 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 66 | } 67 | 68 | 69 | - (void)applicationWillTerminate:(UIApplication *)application { 70 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 71 | } 72 | 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /cpapm/APP/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /cpapm/APP/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /cpapm/APP/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 35 | 42 | 49 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 136 | 146 | 153 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 221 | 228 | 235 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | -------------------------------------------------------------------------------- /cpapm/APP/Crash/CrashViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CrashViewController.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/3. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CrashViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APP/Crash/CrashViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CrashViewController.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/3. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "CrashViewController.h" 10 | #import "EVGView.h" 11 | 12 | typedef struct CrashSize 13 | { 14 | int a; 15 | int b; 16 | }CrashSize; 17 | 18 | @interface CrashViewController () 19 | 20 | @end 21 | 22 | @implementation CrashViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | // Do any additional setup after loading the view. 27 | } 28 | 29 | - (void)didReceiveMemoryWarning { 30 | [super didReceiveMemoryWarning]; 31 | // Dispose of any resources that can be recreated. 32 | } 33 | - (IBAction)stackCrash:(UIButton *)sender { 34 | NSArray *arr = @[@1]; 35 | 36 | NSNumber *a = arr[2]; 37 | } 38 | 39 | - (IBAction)signalSEVGCrash:(UIButton *)sender { 40 | EVGView *view = [[EVGView alloc] init]; 41 | } 42 | 43 | - (IBAction)signalBRTCrash:(UIButton *)sender { 44 | CrashSize *size = {2,3}; 45 | free(size); //导致SIGABRT的错误,因为内存中根本就没有这个空间,哪来的free,就在栈中的对象而已 46 | size->a = 10; 47 | } 48 | 49 | - (IBAction)signalBUSCrash:(UIButton *)sender { 50 | //SIGBUS,内存地址未对齐 51 | //EXC_BAD_ACCESS(code=1,address=0x1000dba58) 52 | // char *a = "123456789"; 53 | // *a = 'H'; 54 | NSString *a = @"234"; 55 | a = @[@"1"]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /cpapm/APP/Crash/EVGView.h: -------------------------------------------------------------------------------- 1 | // 2 | // EVGView.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/3. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EVGView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APP/Crash/EVGView.m: -------------------------------------------------------------------------------- 1 | // 2 | // EVGView.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2018/1/3. 6 | // Copyright © 2018年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "EVGView.h" 10 | 11 | @implementation EVGView 12 | 13 | //对象已经被释放,内存不合法,此块内存地址又没被覆盖,所以此内存内垃圾内存,所以调用方法的时候会导致SIGSEGV的错误 14 | -(instancetype)init { 15 | self = [super init]; 16 | if (self) { 17 | UIView *tempView = [[UIView alloc]init]; 18 | [tempView release]; 19 | 20 | [tempView setNeedsDisplay]; 21 | } 22 | return self; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /cpapm/APP/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /cpapm/APP/Leaks/LeaksViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LeaksViewController.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LeaksViewController : UIViewController 12 | 13 | @end 14 | 15 | @interface LeaksView: UIView 16 | 17 | @property (nonatomic, strong) id delegate; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /cpapm/APP/Leaks/LeaksViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LeaksViewController.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "LeaksViewController.h" 10 | 11 | @interface LeaksViewController () 12 | 13 | @property (nonatomic, strong) LeaksView *leaksView; 14 | 15 | @end 16 | 17 | @implementation LeaksViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | self.leaksView = [LeaksView new]; 22 | [self.view addSubview:self.leaksView]; 23 | self.leaksView.backgroundColor = [UIColor brownColor]; 24 | self.leaksView.delegate = self; 25 | } 26 | 27 | -(void)dealloc { 28 | NSLog(@"----dealloc--%@", [self class]); 29 | } 30 | 31 | - (void)didReceiveMemoryWarning { 32 | [super didReceiveMemoryWarning]; 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | @end 37 | 38 | @implementation LeaksView 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /cpapm/APP/Net/NetViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NetViewController.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NetViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APP/Net/NetViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NetViewController.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "NetViewController.h" 10 | #import "NetAnt.h" 11 | #import "CFNet.h" 12 | 13 | @interface NetViewController () 14 | 15 | @property (nonatomic, strong) CFNet *cf_net; 16 | @property (nonatomic, strong) UIImageView *preResponseImageView; 17 | 18 | @end 19 | 20 | @implementation NetViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view. 25 | } 26 | 27 | - (void)didReceiveMemoryWarning { 28 | [super didReceiveMemoryWarning]; 29 | // Dispose of any resources that can be recreated. 30 | } 31 | 32 | - (IBAction)NetTask:(UIButton *)sender { 33 | [NetAnt addObserver:self]; 34 | [self btSessionRequestAction]; 35 | } 36 | - (IBAction)CFNetTask:(UIButton *)sender { 37 | if (!self.cf_net) { 38 | self.cf_net = [CFNet new]; 39 | } 40 | __weak typeof(self) weakSelf = self; 41 | [self.cf_net testCFGetClientCompletion:^(NSData *responseData) { 42 | // weakSelf.firstImageView.image = [[UIImage alloc] initWithData:responseData]; 43 | [weakSelf appendImage:responseData]; 44 | }]; 45 | } 46 | - (IBAction)CFNetTask2:(id)sender { 47 | if (!self.cf_net) { 48 | self.cf_net = [CFNet new]; 49 | } 50 | __weak typeof(self) weakSelf = self; 51 | [self.cf_net testCFGetClient2Completion:^(NSData *responseData) { 52 | // weakSelf.secondImageView.image = [[UIImage alloc] initWithData:responseData]; 53 | [weakSelf appendImage:responseData]; 54 | }]; 55 | } 56 | - (IBAction)CFNetBenchmark:(id)sender { 57 | 58 | for (int i = 0; i < 10; i ++) { 59 | if (i % 3 == 0) { 60 | [self CFNetTask:nil]; 61 | } 62 | [self CFNetTask2:nil]; 63 | } 64 | } 65 | 66 | - (void)btSessionRequestAction{ 67 | 68 | NSURL *url = [NSURL URLWithString:@"http://www.example.com"]; 69 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 70 | // config.protocolClasses=@[[NEHTTPEye class]];//在NEURLSessionConfiguration里面注册了,所以不用在这里重复注册了 71 | 72 | NSURLSession *urlsession = [NSURLSession sessionWithConfiguration:config]; 73 | 74 | NSURLSessionDataTask *datatask = [urlsession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 75 | if(error) { 76 | NSLog(@"error: %@", error); 77 | } 78 | else { 79 | NSLog(@"Success"); 80 | } 81 | }]; 82 | [datatask resume]; 83 | 84 | 85 | NSURLSessionDataTask *datatask1 = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://www.example1.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 86 | if(error) { 87 | NSLog(@"error: %@", error); 88 | } 89 | else { 90 | NSLog(@"Success"); 91 | } 92 | }]; 93 | [datatask1 resume]; 94 | 95 | } 96 | 97 | 98 | 99 | // catch any network based on NSURLProtocol 100 | -(void)netAntDidCatchRequest:(NSURLRequest *)request response:(NSURLResponse *)response data:(NSData *)data { 101 | id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; 102 | if (obj == nil) { 103 | obj = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 104 | } 105 | NSLog(@"catch_request:%@\nresponse:%@,\ndata:%@",request.URL.absoluteString, response.URL.absoluteString, obj); 106 | } 107 | 108 | -(void)appendImage:(NSData *)responseData { 109 | 110 | CGFloat screen_widht = [UIScreen mainScreen].bounds.size.width; 111 | CGFloat width = screen_widht / 6.0; 112 | 113 | UIImageView *imageV = [UIImageView new]; 114 | UIImage *image = [[UIImage alloc] initWithData:responseData]; 115 | imageV.backgroundColor = [UIColor lightGrayColor]; 116 | imageV.image = image; 117 | if (self.preResponseImageView) { 118 | CGFloat maxX = CGRectGetMaxX(self.preResponseImageView.frame); 119 | CGFloat maxY = CGRectGetMaxY(self.preResponseImageView.frame); 120 | CGFloat x = maxX + width >= screen_widht ? 0 : maxX; 121 | CGFloat y = maxX + width >= screen_widht ? maxY : maxY - width; 122 | 123 | imageV.frame = CGRectMake(x, y, width, width); 124 | }else{ 125 | imageV.frame = CGRectMake(0 , 500, width, width); 126 | // [self.view addSubview:imageV]; 127 | // self.preResponseImageView = imageV; 128 | } 129 | [self.view addSubview:imageV]; 130 | self.preResponseImageView = imageV; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /cpapm/APP/Trace/TraceViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TraceViewController.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TraceViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /cpapm/APP/Trace/TraceViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TraceViewController.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "TraceViewController.h" 10 | #import "ANT.h" 11 | 12 | @interface TraceViewController () 13 | @property (nonatomic, strong) ANT *ant; 14 | @property (nonatomic, strong) UIButton *traceTest; 15 | @end 16 | 17 | @implementation TraceViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view. 22 | } 23 | - (IBAction)ANTTask:(UIButton *)sender { 24 | self.ant = [ANT new]; 25 | self.ant.delegate = self; 26 | [self.ant openWithThreadhold:0.35]; 27 | [self blockFoo]; 28 | [self globalBlockFoo]; 29 | NSLog(@"invoke"); 30 | } 31 | 32 | 33 | -(void)blockFoo { 34 | NSString *s = @"1"; 35 | 36 | for (int i = 0; i <999; i++) { 37 | for (int j =0 ; j < 999; j ++) { 38 | s = [s stringByAppendingString:@"1"]; 39 | } 40 | } 41 | } 42 | 43 | -(void)globalBlockFoo { 44 | __block NSInteger pp = 0; 45 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 46 | for (int i = 0; i <999; i++) { 47 | for (int j =0 ; j < 999; j ++) { 48 | pp += 1; 49 | } 50 | } 51 | }); 52 | } 53 | 54 | - (void)didReceiveMemoryWarning { 55 | [super didReceiveMemoryWarning]; 56 | // Dispose of any resources that can be recreated. 57 | } 58 | 59 | -(void)ANT:(ANT *)ant catchWithThreadHold:(double)threadHold mainThreadBacktrace:(NSString *)mainBacktrace allThreadBacktrace:(NSString *)allBacktrace { 60 | NSLog(@"----mainTrace----"); 61 | NSLog(@"%@",mainBacktrace); 62 | NSLog(@"----allTrace----"); 63 | NSLog(@"%@",allBacktrace); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /cpapm/APP/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /cpapm/APP/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "OriginalObjectMethod.h" 11 | #import "OriginalObjectMethod+Swizzle.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | // Do any additional setup after loading the view, typically from a nib. 23 | } 24 | - (IBAction)hookCheck:(UIButton *)sender { 25 | 26 | OriginalObjectMethod *original = [OriginalObjectMethod new]; 27 | [original checkSwizzledMethod]; 28 | } 29 | 30 | 31 | @end 32 | 33 | /* 34 | 35 | 36 | #import "ViewController.h" 37 | 38 | @interface ViewController () 39 | 40 | @end 41 | 42 | @implementation ViewController 43 | 44 | - (void)viewDidLoad { 45 | [super viewDidLoad]; 46 | // Do any additional setup after loading the view, typically from a nib. 47 | } 48 | 49 | - (IBAction)testCF:(UIButton *)sender { 50 | [self CFTest]; 51 | } 52 | 53 | - (void)didReceiveMemoryWarning { 54 | [super didReceiveMemoryWarning]; 55 | // Dispose of any resources that can be recreated. 56 | } 57 | 58 | // sync function 59 | -(void)CFTest { 60 | 61 | CFStringRef headerFieldName = CFSTR("this is a header"); 62 | CFStringRef headerFieldValue = CFSTR("value"); 63 | //创建url 64 | CFStringRef url1 = CFSTR("http://c.hiphotos.baidu.com/image/w%3D310/sign=b8f7695888d4b31cf03c92bab7d6276f/4e4a20a4462309f76248df09710e0cf3d7cad682.jpg"); 65 | CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url1, NULL); 66 | //设置请求方式 67 | CFStringRef requestMethod = CFSTR("GET"); 68 | //创建请求 69 | CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault,requestMethod, myURL, kCFHTTPVersion1_1); 70 | //设置头信息 71 | CFHTTPMessageSetHeaderFieldValue(myRequest, headerFieldName, headerFieldValue); 72 | //创建CFReadStreamRef对象来序列化并发送CFHTTP请求,注意CFReadStreamCreateForHTTPRequest在iOS 9.0开始已经有DEPRECATED警告, 73 | #pragma clang diagnostic push 74 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 75 | CFReadStreamRef myReadStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest); 76 | #pragma clang diagnostic pop 77 | //打开读取流 78 | CFReadStreamOpen(myReadStream); 79 | //存放响应数据 80 | NSMutableData *responseBytes = [NSMutableData data]; 81 | CFIndex numBytesRead = 0; 82 | //从流中读取数据,读完为止,其中CFReadStreamRead会阻塞代码 83 | do { 84 | UInt8 buf[1024]; 85 | numBytesRead = CFReadStreamRead(myReadStream, buf, sizeof(buf)); 86 | 87 | if (numBytesRead > 0) { 88 | [responseBytes appendBytes:buf length:numBytesRead]; 89 | } 90 | } while (numBytesRead > 0); 91 | //读取响应头信息 92 | #pragma clang diagnostic push 93 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 94 | CFHTTPMessageRef myResponse = (CFHTTPMessageRef) CFReadStreamCopyProperty(myReadStream, kCFStreamPropertyHTTPResponseHeader); 95 | #pragma clang diagnostic pop 96 | //读取statusCode 97 | CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(myResponse); 98 | //打印数据 99 | if(statusCode == 200) { 100 | //NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseBytes options:NSJSONReadingMutableLeaves error:nil]; 101 | NSLog(@"%@", responseBytes); 102 | } 103 | } 104 | 105 | - (IBAction)testCFGetClient:(id)sender { 106 | CFStringRef urlStr = CFSTR("http://c.hiphotos.baidu.com/image/w%3D310/sign=b8f7695888d4b31cf03c92bab7d6276f/4e4a20a4462309f76248df09710e0cf3d7cad682.jpg"); 107 | __weak typeof(self) weakSelf = self; 108 | [self testClientGet: urlStr completion:^(NSData * responseData) { 109 | weakSelf.responseImageView.image = [UIImage imageWithData:responseData]; 110 | }]; 111 | } 112 | - (IBAction)testCFGetClient2:(id)sender { 113 | 114 | CFStringRef urlStr = CFSTR("http://e.hiphotos.baidu.com/image/pic/item/500fd9f9d72a6059099ccd5a2334349b023bbae5.jpg"); 115 | __weak typeof(self) weakSelf = self; 116 | [self testClientGet: urlStr completion:^(NSData *responseData) { 117 | weakSelf.secondResponseImageView.image = [UIImage imageWithData:responseData];; 118 | }]; 119 | } 120 | 121 | -(void)testClientGet:(CFStringRef)urlStr completion:(void(^)(NSData *))completionBlock { 122 | // CFStringRef urlStr = CFSTR("http://c.hiphotos.baidu.com/image/w%3D310/sign=b8f7695888d4b31cf03c92bab7d6276f/4e4a20a4462309f76248df09710e0cf3d7cad682.jpg"); 123 | 124 | //GET请求 125 | CFStringRef method = CFSTR("GET"); 126 | 127 | //构造URL 128 | CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL); 129 | 130 | //http请求 131 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, method, url, kCFHTTPVersion1_1); 132 | 133 | //创建一个读取流 读取网络数据 134 | CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request); 135 | 136 | NSString *uniqueValue = [NSString stringWithFormat:@"%.2f_%@",[[NSDate date] timeIntervalSince1970], (__bridge NSString *)urlStr]; 137 | if (!self.responseMap) { 138 | self.responseMap = @{}.mutableCopy; 139 | } 140 | if (self.responseMap) { 141 | self.responseMap[uniqueValue] = completionBlock; 142 | } 143 | CFReadStreamSetProperty((CFReadStreamRef)readStream, CFSTR("CFStreamID"), (__bridge CFTypeRef)(uniqueValue)); 144 | // uniqueValue add to call back map 145 | CFStringRef requestHeader = CFSTR("CFStreamID"); 146 | CFStringRef requestHeaderValue = (__bridge CFStringRef)uniqueValue; 147 | CFHTTPMessageSetHeaderFieldValue(request, requestHeader, requestHeaderValue); 148 | NSMutableDictionary *proxyToUse = @{@"CFStreamID": uniqueValue}; 149 | CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, (__bridge CFTypeRef)(proxyToUse)); 150 | //设置流的context这里将self传入,用于以后的回调 151 | CFStreamClientContext ctxt = {0, (__bridge void *)(self), NULL, NULL, NULL}; 152 | 153 | 154 | //监听回调事件 155 | //  kCFStreamEventNone,(没有事件发生) 156 | // 157 | //  kCFStreamEventOpenCompleted,(流被成功打开) 158 | // 159 | //  kCFStreamEventHasBytesAvailable,(有数据可以读取) 160 | // 161 | //  kCFStreamEventCanAcceptBytes,(流可以接受写入数据(用于写入流)) 162 | // 163 | //  kCFStreamEventErrorOccurred,(在流上有错误发生) 164 | // 165 | //  kCFStreamEventEndEncountered ,(到达了流的结束位置) 166 | CFOptionFlags event = kCFStreamEventHasBytesAvailable|kCFStreamEventEndEncountered; 167 | 168 | //设值回调函数myCallBack 169 | 170 | //    回调函数格式 171 | //    void callBack(CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo) 172 | 173 | CFReadStreamSetClient(readStream,event,myCallBack,&ctxt); 174 | 175 | CFReadStreamScheduleWithRunLoop(readStream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 176 | 177 | CFReadStreamOpen(readStream); 178 | 179 | } 180 | 181 | void myCallBack (CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo){ 182 | 183 | // NSLog(@"myCallBack is %@",clientCallBackInfo); 184 | 185 | // static NSMutableData* imageData = nil; 186 | NSDictionary *proxyInfo = CFBridgingRelease(CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPProxy)); 187 | ViewController *client = (__bridge ViewController *)clientCallBackInfo; 188 | 189 | CFTypeRef uniqueValue = (__bridge CFTypeRef)(proxyInfo[@"CFStreamID"]); 190 | 191 | if (!client.responseDataMap) { 192 | client.responseDataMap = @{}.mutableCopy; 193 | } 194 | // if (client.responseDataMap && [(__bridge NSString *)uniqueValue length] > 0) { 195 | // client.responseDataMap[(__bridge NSString *)(uniqueValue)] = [NSMutableData data]; 196 | // } 197 | // NSLog(@"uniqueId:%@,time:%.2f",(__bridge NSString *)uniqueValue, [[NSDate date] timeIntervalSince1970]); 198 | 199 | if(type == kCFStreamEventHasBytesAvailable){ 200 | 201 | UInt8 buff[255]; 202 | long length = CFReadStreamRead(stream, buff, 255); 203 | NSMutableData *imageData = client.responseDataMap[(__bridge NSString *)(uniqueValue)]; 204 | if(!imageData){ 205 | imageData = [[NSMutableData alloc] init]; 206 | } 207 | [imageData appendBytes:buff length:length]; 208 | 209 | client.responseDataMap[(__bridge NSString *)(uniqueValue)] = imageData; 210 | }else if(type == kCFStreamEventEndEncountered){ 211 | 212 | void(^completionBlock)(NSData *) = client.responseMap[(__bridge NSString *)(uniqueValue)]; 213 | if (completionBlock) { 214 | NSData *resData = client.responseDataMap[(__bridge NSString *)(uniqueValue)]; 215 | completionBlock(resData); 216 | // remove this request map key values 217 | } 218 | 219 | CFReadStreamClose(stream); 220 | CFReadStreamUnscheduleFromRunLoop(stream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 221 | } 222 | } 223 | 224 | void postmyCallBack (CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo){ 225 | 226 | NSLog(@"myCallBack is %@",clientCallBackInfo); 227 | 228 | static NSMutableData* imageData = nil; 229 | 230 | if(type == kCFStreamEventHasBytesAvailable){ 231 | UInt8 buff[255]; 232 | long length = CFReadStreamRead(stream, buff, 255); 233 | NSLog(@"length is %ld",length); 234 | if(!imageData){ 235 | imageData = [[NSMutableData alloc] init]; 236 | } 237 | [imageData appendBytes:buff length:length]; 238 | 239 | 240 | }else if(type == kCFStreamEventEndEncountered){ 241 | 242 | NSString* resString = [[NSString alloc] initWithData:imageData encoding:NSUTF8StringEncoding]; 243 | NSLog(@"resString is %@",resString); 244 | CFReadStreamClose(stream); 245 | CFReadStreamUnscheduleFromRunLoop(stream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 246 | } 247 | } 248 | - (IBAction)testCFPostClient:(id)sender { 249 | [self testClientPOST]; 250 | } 251 | 252 | -(void)testClientPOST { 253 | CFStringRef urlStr = CFSTR("https://upload.api.weibo.com/2/statuses/upload.json"); 254 | 255 | //GET请求 256 | CFStringRef method= CFSTR("POST"); 257 | 258 | //构造URL 259 | CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL); 260 | 261 | //http请求 262 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, method, url, kCFHTTPVersion1_1); 263 | 264 | NSString *boundary = [NSString stringWithFormat:@"---------------------------14737809dasdasda2746641449"]; 265 | NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]; 266 | 267 | //OC的字符串要做C框架中使用需要__bridge桥接 268 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), (__bridge CFStringRef)(contentType)); 269 | 270 | NSData *bodyData = [self getRequestData]; 271 | CFHTTPMessageSetBody(request, (__bridge CFDataRef)(bodyData)); 272 | 273 | 274 | CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request); 275 | 276 | //设置流的context这里将self传入,用于以后的回调 277 | CFStreamClientContext ctxt = {0, (__bridge void *)(self), NULL, NULL, NULL}; 278 | 279 | CFOptionFlags event = kCFStreamEventHasBytesAvailable|kCFStreamEventEndEncountered; 280 | 281 | CFReadStreamSetClient(readStream,event,postmyCallBack,&ctxt); 282 | 283 | CFReadStreamScheduleWithRunLoop(readStream,CFRunLoopGetCurrent(),kCFRunLoopCommonModes); 284 | 285 | CFReadStreamOpen(readStream); 286 | } 287 | 288 | - (NSData *)getRequestData{ 289 | 290 | UIImage *image = [UIImage imageNamed:@"addressIcon"]; 291 | NSData *imageData = UIImagePNGRepresentation(image); 292 | 293 | 294 | //分隔符,注意要与上面请求头中的一致 295 | NSString *boundary = [NSString stringWithFormat:@"---------------------------14737809dasdasda2746641449"]; 296 | 297 | 298 | //定义可变Data; 299 | NSMutableData *body = [NSMutableData data]; 300 | 301 | //分隔符 302 | [body appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 303 | 304 | //需要发送的文字内容 305 | [body appendData:[@"Content-Disposition: form-data; name=\"status\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 306 | [body appendData:[@"需要发送的文字内容\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 307 | 308 | //分隔符 309 | [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 310 | 311 | //需要发送的图片内容 312 | [body appendData:[@"Content-Disposition: form-data; name=\"pic\"; filename=\"cat.png\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 313 | [body appendData:[@"Content-Type: image/png\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 314 | [body appendData:[NSData dataWithData:imageData]]; 315 | 316 | //分割符 317 | [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 318 | 319 | //access_token 向新浪微博发送内容,需要OAuth认证,这里必须带上access_token 320 | [body appendData:[@"Content-Disposition: form-data; name=\"access_token\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 321 | 322 | [body appendData:[@"2.00svaeojkrewe901dPLialB" dataUsingEncoding:NSUTF8StringEncoding]]; 323 | 324 | //结尾注意这里的分割符和前面不一样,--%@--后面多了两条--,如果少了会发送不成功哦 325 | [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 326 | 327 | return body; 328 | } 329 | 330 | @end 331 | 332 | 333 | */ 334 | -------------------------------------------------------------------------------- /cpapm/APP/main.mm: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // cpapm 4 | // 5 | // Created by yangyouyong on 2017/12/28. 6 | // Copyright © 2017年 cpbee. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------