借助lldb的bt命令Debug

前言

对每个程序员来说调试(Debug)是一个不可或缺的技能

偵錯(英语:Debug),又稱除錯,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程(wiki-调试

背景

做SDK开发,难免会有些app接入SDK后并没有达到所需要的效果,这时候就需要你具备一定的debug的能力找到问题的所在,并提示/建议app开发人员进行修改.

比如: app开发人员并没有按照SDK接入文档进行Coding,又或是app开发人员按照SDK接入文档进行Coding(但由于app的业务逻辑比较复杂某些细节忽略了)导致没有达到所需的效果.

使用我们sdk进行流量引导的app必须在app启动时优先调用sdk+[WSPX start]

案例

  • 某民直播APP,在接入我方SDK后发现:在进入首页的直播平台后,会有几条请求无法被SDK引导(以下简称流量泄露)
  • 某民直播APP开发人员,一再确认已按照SDK接入文档进行Coding

调试前已有的资料

  • 流量泄露时对应的抓包数据
  • 某民直播提供的IPA包

问题重现的操作步骤:

  • 激活sdk,重启app
  • 使用Wireshark对手机进行抓包
  • 首页随便逛逛,点开一个直播间

    查看抓包数据

    wireShark抓包图:

wireShark抓包图

follow tcp flow of FvlrC6LHMC9LspI_jVj-qzDhktiq:
wireShark详细

根据抓包数据能得到的有用数据

  • 从图中红色圈起来的部分我们可以知道该http请求发往的Destination不是36.250.87.88
  • 绿色圈起来的部分是我们SDK有意让其不走我们的的节点36.250.87.88
  • 除了有意使其不走节点36.250.87.88,剩下的就是泄露的流量
  • UAFvlrC6LHMC9LspI_jVj-qzDhktiq这条请求确实是某民app发出的
  • Content-Type可以看出这是个zip包之类的资源(判定是一个需要下载的资源)

判定泄露类型:

SDK现有的技术壁垒

  • 不支持wkwebview的流量引导
  • 不支持cfsocket和cfstream写的
  • 不支持对Apple的一些服务的流量引导
  • 如果有session的创建比sdk的session创建的早,则会导致泄漏
  • 如果有第三方也对我们hook的API也做了hook,有可能会导致泄漏
  • UA判定不是wkwebview发出的流量
  • 应该不是cfsocket和cfstream的流量,下载数据,是我的话应该会选择downloadSession来做的
  • 不是Apple的一些后台服务发出的流量
  • 可能session创建时没有挂上sdk的nsurlprotocol
  • 第三方也对我们hook的API做了hook

借助OC语言的特性使用methodSwizzling对可疑session相关的API进行hook并打印session的实例地址

#import "NSURLSession+Hooked.h"
#import "MethodSwizzling.h"
@implementation NSURLSession (Hooked)

+ (void)load {
    if ([WSPXConfig enableHook]) {

    exchangeInstanceMethod([NSURLSession class], @selector(downloadTaskWithRequest:), @selector(debugHookedDownloadTaskWithRequest:));

    exchangeInstanceMethod([NSURLSession class], @selector(dataTaskWithRequest:), @selector(debugHookedDataTaskWithRequest:));
    exchangeInstanceMethod([NSURLSession class], @selector(dataTaskWithURL:), @selector(debugHookedDataTaskWithURL:));
    exchangeInstanceMethod([NSURLSession class], @selector(uploadTaskWithRequest:fromFile:), @selector(debugHookedUploadTaskWithRequest:fromFile:));
    exchangeInstanceMethod([NSURLSession class], @selector(uploadTaskWithRequest:fromData:), @selector(debugHookedUploadTaskWithRequest:fromData:));
    exchangeInstanceMethod([NSURLSession class], @selector(uploadTaskWithStreamedRequest:), @selector(debugHookedUploadTaskWithStreamedRequest:));
    exchangeInstanceMethod([NSURLSession class], @selector(downloadTaskWithURL:), @selector(debugHookedDownloadTaskWithURL:));
    exchangeInstanceMethod([NSURLSession class], @selector(downloadTaskWithResumeData:), @selector(debugHookedDownloadTaskWithResumeData:));
        exchangeMethodClass([NSURLSession class],
                            @selector(sessionWithConfiguration:delegate:delegateQueue:),
                            @selector(debugHookedSessionWithConfiguration:delegate:delegateQueue:));
    }
}

+ (NSURLSession *)debugHookedSessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id<NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue {
    NSURLSession* finalSession = [self debugHookedSessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
    NSLog(@"Hallo-->session debugHookedSessionWithConfiguration -> session(%@), delegate(%@)\n",finalSession, delegate);
    return finalSession;
}
/* Creates a data task with the given request.  The request may have a body stream. */
- (NSURLSessionDataTask *)debugHookedDataTaskWithRequest:(NSURLRequest *)request; {
    NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
    return [self debugHookedDataTaskWithRequest:request];
}

/* Creates a data task to retrieve the contents of the given URL. */
- (NSURLSessionDataTask *)debugHookedDataTaskWithURL:(NSURL *)url; {
    NSLog(@"Hallo-->session(%@), -->url(%@) \n%s", self, url, __PRETTY_FUNCTION__);
    return [self debugHookedDataTaskWithURL:url];
}

/* Creates an upload task with the given request.  The body of the request will be created from the file referenced by fileURL */
- (NSURLSessionUploadTask *)debugHookedUploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL; {
    NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
    return [self debugHookedUploadTaskWithRequest:request fromFile:fileURL];
}

/* Creates an upload task with the given request.  The body of the request is provided from the bodyData. */
- (NSURLSessionUploadTask *)debugHookedUploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData; {
    NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
    return [self debugHookedUploadTaskWithRequest:request fromData:bodyData];
}

/* Creates an upload task with the given request.  The previously set body stream of the request (if any) is ignored and the URLSession:task:needNewBodyStream: delegate will be called when the body payload is required. */
- (NSURLSessionUploadTask *)debugHookedUploadTaskWithStreamedRequest:(NSURLRequest *)request; {
   NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
    return [self debugHookedUploadTaskWithStreamedRequest:request];
}

/* Creates a download task with the given request. */
- (NSURLSessionDownloadTask *)debugHookedDownloadTaskWithRequest:(NSURLRequest *)request; {
    NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
    return [self debugHookedDownloadTaskWithRequest:request];
}

/* Creates a download task to download the contents of the given URL. */
- (NSURLSessionDownloadTask *)debugHookedDownloadTaskWithURL:(NSURL *)url; {
    NSLog(@"Hallo-->session(%@), -->url(%@) \n%s", self, url, __PRETTY_FUNCTION__);
    return [self debugHookedDownloadTaskWithURL:url];
}

/* Creates a download task with the resume data.  If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */
- (NSURLSessionDownloadTask *)debugHookedDownloadTaskWithResumeData:(NSData *)resumeData;{
    NSLog(@"Hallo-->session(%@), \n%s", self, __PRETTY_FUNCTION__);
    return [self debugHookedDownloadTaskWithResumeData:resumeData];
}
@end

借助MonkeyDev动态库注入工具将上面的代码注入某民的可执行文件

  • 具体如何使用该工具我就不再赘述,不懂的话可以去看Wiki

复现问题

  • 使用Wireshark对手机进行抓包
  • 点开首页随便逛逛,点开一个直播间
  • 如愿Wireshark中出现了那个泄露的关键字符串FvlrC6LHMC9LspI_jVj-qzDhktiq
  • 也如愿收获了一大堆log

在log寻找线索

  • 在log中搜索关键字FvlrC6LHMC9LspI_jVj-qzDhktiq找到如下这条
    2018-08-02 15:27:46.070729+0800 QuanMinTV-Mini[1529:185056] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSURLRequest: 0x174214f90> { URL: http://attachment.shouyintv.cn/FvlrC6LHMC9LspI_jVj-qzDhktiq })
    
  • 根据上面得到的那条log,拿到0x1741d8150,并对log文件进行过滤得到如下结果:
     2018-08-02 15:09:49.403756+0800 QuanMinTV-Mini[1529:182446] Hallo-->session debugHookedSessionWithConfiguration -> session(<__NSURLSessionLocal: 0x1741d8150>), delegate(<AFHTTPSessionManager: 0x1701d9140, baseURL: (null), session: (null), operationQueue: <NSOperationQueue: 0x17422cb80>{name = 'NSOperationQueue 0x17422cb80'}>)
    2018-08-02 15:09:49.418955+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17001aa40> { URL: https://api-shouyin.quanmin.tv/public/version/check/2/4346?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=QBMHVSNPSZJQUOOFNEVPZXFPAHNNXIWE&os=30&osversion=ios_10.3.1&sign=B2DEA231ED08CD9BDDABD0C75B7714FB&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:49.424917+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x1740194c0> { URL: https://api-shouyin.quanmin.tv/public/app/msg?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=NHWKXUEVMDFVAJINOJRHPOWUNYSWZSBP&os=30&osversion=ios_10.3.1&sign=4787B043ECCD08701DAAA511A1A57E11&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:49.426879+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17001ab50> { URL: https://api-shouyin.quanmin.tv/public/config/gift/version?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=EPTGXBVCVGWNNMOAEJPDFCMKGIMRTKTY&os=30&osversion=ios_10.3.1&sign=96C4834DAB7554ECCC1A4CB1100D7A8B&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:49.428882+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17001ac10> { URL: https://api-shouyin.quanmin.tv/bag/propConfigVersion?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=DIYIROKGPMHUYPIEMJUMWDPDWJPBQYIP&os=30&osversion=ios_10.3.1&sign=417C6DC17E0B63EFF3D4A22FBEE0C60F&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:49.431908+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x174019700> { URL: https://api-shouyin.quanmin.tv/public/app/act?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=QCATFARGNZSQTESFECNREAJFSYGCYBVN&os=30&osversion=ios_10.3.1&sign=8C631191D38D45F6BFD5BAE1504DB99D&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:49.932752+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17401a440> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=KQVXMQLGZUVPBYNKMYRRQKFSYAKAWJBQ&os=30&osversion=ios_10.3.1&sign=98A1A861B795A6FE5E5FA9D8F81CCD08&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:52.778470+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x174203ca0> { URL: https://api-shouyin.quanmin.tv/bag/propConfig?cid=6&client=app&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=KLPMQQQLQPRGWLBERPQBDEHOHTCVZRNV&os=30&osversion=ios_10.3.1&sign=C8B31DD95B00DCB64ADA533C6D275CF8&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:55.811342+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x170216310> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=KQXUGSPOATHXRNJGGVWSIRVVXGLEFHXM&os=30&osversion=ios_10.3.1&sign=0D0762A1D2C39E2904035AE8C248F826&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:09:58.884550+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x1702132b0> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=BYRNKTDIGMVYPVYYGXSOXENFPQIUBGHN&os=30&osversion=ios_10.3.1&sign=D00DF763A33CC62288FA219D98DBBBA4&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:10:02.214492+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17401bb90> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=FKQONYAGQJHWBPJEQVXHTXOXYEDMMNBV&os=30&osversion=ios_10.3.1&sign=B784F426048B7DC5EF1B86988725D822&toid=-1&ua=iPhone7%2C2 })
    onLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x170218e00> { URL: https://api-shouyin.quanmin.tv/user/honor/single?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=USEAFOCLRVOYRAHHGOYDIHUKOFITNSHQ&os=30&osversion=ios_10.3.1&owid=1639748150&sign=A22A2E14B358545B355D8C454DAC828F&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:27:45.693859+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17021aaa0> { URL: https://api-shouyin.quanmin.tv/public/config/gift/version?cid=6&conn=WIFI&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=MSARXVQPBUSSTRXVLBLM2018-08-02 15:27:45.695986+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x1742141d0> { URL: https://api-shouyin.quanmin.tv/bag/propConfigVersion?cid=6&conn=WIFI&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=WGKLJVVVABHMMKTOHJJXVRBVNCIVDMDF&os=30&osversion=ios_10.3.1&sign=F20484F9E26A949403144286937FFD2F&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:27:45.980003+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x174213ec0> { URL: https://api-shouyin.quanmin.tv/bag/propConfig?cid=6&client=app&conn=WIFI&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=BZMZIXOWSUALQDCKSCZUBAQYMLTRSNYX&os=30&osversion=ios_10.3.1&sign=E4D305CACD884D959D8A242C8C28DC06&toid=-1&ua=iPhone7%2C2 })
    2018-08-02 15:27:46.070729+0800 QuanMinTV-Mini[1529:185056] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSURLRequest: 0x174214f90> { URL: http://attachment.shouyintv.cn/FvlrC6LHMC9LspI_jVj-qzDhktiq })
    
  • 从上面的log中第一条可以判定该条session是由AFHTTPSessionManager创建的并不是我们sdk创建的,或者说该session在我们sdk在hooksessionWithConfiguration:delegate:delegateQueue:之前就已经创建了

  • 因为以前有看过AFHTTPSessionManager的session初始化方法所以我直接在Xcode中使用符号断点在-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]下了个断点

  • 并且也在我们sdk启动的方法下了个符号断点+[WSPX start]
  • 如果Xcode先停在了-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]那么可以确定就是因为session没有挂上sdk的nsurlprotocol,导致该session无法被我们sdk进行流量引导
  • 如下是断点-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]触发时使用bt查看堆栈

       (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
      * frame #0: 0x00000001007ad6c4 QuanMinTV-Mini`-[DVLHttpManager initWithBaseUrl:]
        frame #1: 0x00000001001eaac0 QuanMinTV-Mini`-[ZXHttpTask init] + 132
        frame #2: 0x00000001001eaa1c QuanMinTV-Mini`___lldb_unnamed_symbol2824$$QuanMinTV-Mini + 36
        frame #3: 0x0000000105f8da10 libdispatch.dylib`_dispatch_client_callout + 16
        frame #4: 0x0000000105f8e778 libdispatch.dylib`dispatch_once_f + 116
        frame #5: 0x00000001001ea9f4 QuanMinTV-Mini`+[ZXHttpTask sharedManager] + 112
        frame #6: 0x00000001001eb268 QuanMinTV-Mini`+[ZXHttpTask GET:parameters:taskResponse:] + 164
        frame #7: 0x00000001007a48d4 QuanMinTV-Mini`-[ZXUserModel checkUpate:] + 344
        frame #8: 0x00000001007a2604 QuanMinTV-Mini`-[ZXUserModel delayFetchThings] + 56
        frame #9: 0x000000010079e9f8 QuanMinTV-Mini`-[ZXUserModel fetchData] + 56
        frame #10: 0x00000001007a0080 QuanMinTV-Mini`-[ZXUserModel userInfo] + 148
        frame #11: 0x0000000100623fb8 QuanMinTV-Mini`-[AppDelegate setupBugly] + 408
        frame #12: 0x0000000100623798 QuanMinTV-Mini`-[AppDelegate application:didFinishLaunchingWithOptions:] + 184
        frame #13: 0x00000001940c72dc UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 380
        frame #14: 0x00000001942d3800 UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3452
        frame #15: 0x00000001942d92a8 UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1684
        frame #16: 0x00000001942edde0 UIKit`__84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke.3151 + 48
        frame #17: 0x00000001942d653c UIKit`-[UIApplication workspaceDidEndTransaction:] + 168
        frame #18: 0x000000018facf884 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 36
        frame #19: 0x000000018facf6f0 FrontBoardServices`-[FBSSerialQueue _performNext] + 176
        frame #20: 0x000000018facfaa0 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 56
        frame #21: 0x000000018ded5424 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
        frame #22: 0x000000018ded4d94 CoreFoundation`__CFRunLoopDoSources0 + 540
        frame #23: 0x000000018ded29a0 CoreFoundation`__CFRunLoopRun + 744
        frame #24: 0x000000018de02d94 CoreFoundation`CFRunLoopRunSpecific + 424
        frame #25: 0x00000001940c045c UIKit`-[UIApplication _run] + 652
        frame #26: 0x00000001940bb130 UIKit`UIApplicationMain + 208
        frame #27: 0x0000000100a0f8d4 QuanMinTV-Mini`___lldb_unnamed_symbol16136$$QuanMinTV-Mini + 88
        frame #28: 0x000000018ce1159c libdyld.dylib`start + 4
    
  • 从上面的调用栈可以整理出如下的调用关系1:

    [AppDelegate application:didFinishLaunchingWithOptions:]
    [AppDelegate setupBugly]
    [ZXUserModel userInfo]
    [ZXUserModel fetchData]
    [ZXUserModel delayFetchThings]
    [ZXUserModel checkUpate:]
    [ZXHttpTask GET:parameters:taskResponse:]
    [ZXHttpTask sharedManager]
    .
    .
    .
    [ZXHttpTask init]
    [DVLHttpManager initWithBaseUrl:]
    [AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]
    
  • 如下是符号断点+[WSPX start]触发时使用bt查看的堆栈
    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1
    * frame #0: 0x0000000100d46a34 QuanMinTV-Mini`+[WSPX start]
    frame #1: 0x00000001008c027c QuanMinTV-Mini`+[QMFreeFlowManager start] + 40
    frame #2: 0x0000000100623830 QuanMinTV-Mini`-[AppDelegate application:didFinishLaunchingWithOptions:] + 336
    frame #3: 0x00000001940c72dc UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 380
    frame #4: 0x00000001942d3800 UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3452
    frame #5: 0x00000001942d92a8 UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1684
    frame #6: 0x00000001942edde0 UIKit`__84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke.3151 + 48
    frame #7: 0x00000001942d653c UIKit`-[UIApplication workspaceDidEndTransaction:] + 168
    frame #8: 0x000000018facf884 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 36
    frame #9: 0x000000018facf6f0 FrontBoardServices`-[FBSSerialQueue _performNext] + 176
    frame #10: 0x000000018facfaa0 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 56
    frame #11: 0x000000018ded5424 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
    frame #12: 0x000000018ded4d94 CoreFoundation`__CFRunLoopDoSources0 + 540
    frame #13: 0x000000018ded29a0 CoreFoundation`__CFRunLoopRun + 744
    frame #14: 0x000000018de02d94 CoreFoundation`CFRunLoopRunSpecific + 424
    frame #15: 0x00000001940c045c UIKit`-[UIApplication _run] + 652
    frame #16: 0x00000001940bb130 UIKit`UIApplicationMain + 208
    frame #17: 0x0000000100a0f8d4 QuanMinTV-Mini`___lldb_unnamed_symbol16136$$QuanMinTV-Mini + 88
    frame #18: 0x000000018ce1159c libdyld.dylib`start + 4
    
  • 从上面的调用栈可以整理出如下的调用关系2:
    [WSPX start]
    [QMFreeFlowManager start]
    [AppDelegate application:didFinishLaunchingWithOptions:]
    
  • 说明一下:断点是先触发了-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]然后放行再触发了+[WSPX start]

排查结论:

  • 根据断点触发的先后顺序,可以确定是因为session没有挂上sdk的nsurlprotocol,导致该session无法被我们sdk进行流量引导
  • 然后将调用关系1和调用关系2结合得出:[QMFreeFlowManager start][AppDelegate setupBugly]调用的晚导致导致泄露了。
  • 将上述结论发给某民直播的开发者,让其将[QMFreeFlowManager start]放在[AppDelegate setupBugly]之前调用

Note:

  • 这里使用bt就是为了找到某民直播app因为业务逻辑比较复杂导致[QMFreeFlowManager start]并没有按照接入文档进行coding的证据,这样子和甲方沟通起来就比较硬气一些了。
Zerlz wechat
扫码关注一个很懒的程序员!
Winter is coming, give me a penny!