懒人版二进制重排

5,931 阅读4分钟

0. 序言

这是启动速度提升的第三篇:

  1. 《我是如何让微博绿洲的启动速度提升30%的》
  2. 《我是如何让微博绿洲的启动速度提升30%的(二)》

第一篇讲了动态库转静态库和二进制重排带来的启动优化以及其原理。

第二篇讲了动态库转静态库到底带来了哪些改变,以及实践中遇到的问题应该如何解决。

本篇将介绍懒人版的Clang插桩导出主工程和三方库启动相关的符号表,原理可以参考《我是如何让微博绿洲的启动速度提升30%的》

1. YCSymbolTracker

第一篇文章介绍了Clang插桩可以导出主工程启动相关的符号表,有朋友问到了如果涉及到CocoaPods导入的三方库,该如何进行插桩呢?

最近我把这一块整理了一下,做了一个CocoaPods的库 YCSymbolTracker ,下载库可以得到同款Demo。(觉得不错的话,可以点一个Star★,biu~)

1.1 导出主工程启动相关的符号表

我们一步一步来,首先看一下这个库如何导出主工程启动相关的符号表。

platform :ios, '8.0'
use_frameworks!
target 'Demo' do
  pod 'YCSymbolTracker', '~> 0.1.0'
end

AppDelegate中配置导出地址:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.rootViewController = [[YCViewController alloc] init];
    [self.window makeKeyAndVisible];
    [SwiftDemo swiftTestLoad];
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"demo.order"];
    NSLog(@"%@", filePath);
    [YCSymbolTracker exportSymbolsWithFilePath:filePath];
    return YES;
}

拿到控制台输出的地址,打开文件:

我们看到了主工程启动相关的符号表。

1.2 三方库启动相关的符号表

我们添加一个三方库SDWebImage,你也可以尝试其他库。

platform :ios, '8.0'
use_frameworks!
target 'Demo' do
  pod 'SDWebImage'
  pod 'YCSymbolTracker', '~> 0.1.0'
end

AppDelegate中添加SDWebImage相关的代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.rootViewController = [[YCViewController alloc] init];
    [self.window makeKeyAndVisible];
    [SwiftDemo swiftTestLoad];
    
    [SDWebImageManager.sharedManager cancelAll]; // 添加的代码
    
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"demo.order"];
    NSLog(@"%@", filePath);
    [YCSymbolTracker exportSymbolsWithFilePath:filePath];
    
    return YES;
}

我们再导出一次:

我们发现我们的order文件没有发生变化。这是因为我们的Clang插桩只覆盖了主工程,没有包括三方库!

在Pod工程 SDWebImageBuild SettingsOther C Flags-fsanitize-coverage=func,trace-pc-guard 配置。

如果是Swift的三方库 Other Swift Flags 添加 -sanitize=undefined -sanitize-coverage=func 配置。

这时如果我们直接执行构建的话会报错。

Undefined symbol: ___sanitizer_cov_trace_pc_guard_init
Undefined symbol: ___sanitizer_cov_trace_pc_guard

如果看了第一篇文章,或者熟悉这个问题的同学应该马上就明白了,找不到这个符号。我们的 YCSymbolTracker 库中其实是有这个符号的,但是由于它们都是动态库(独立), SDWebImage 不能直接访问到 YCSymbolTracker 中的函数。但是我们不能修改三方库的代码,或者修改三方库的podspec文件来修改依赖关系。那么如何让 SDWebImage 依赖 YCSymbolTracker 从而访问到这两个函数呢?

选择Pods工程的 SDWebImage ,在Frameworks and Libraries中添加 YCSymbolTracker

修改 YCSymbolTracker 是否Embed

这样我们就为 SDWebImage 添加了 YCSymbolTracker 作为依赖,可以在 Build PhaseDependencies 中验证:

然后再配置一下Framework Search Path的值${PODS_CONFIGURATION_BUILD_DIR}/YCSymbolTracker

配置好之后我们再跑一次:

嗒哒~现在我们可以看到[SDWebImageManager.sharedManager cancelAll];执行后相关的符号了!也就是说我们拿到了主工程和三方库启动相关的符号表了。

2. 懒人版三方库插桩

主工程和三方库的插桩导出符号表的原理我们已经明白了,但是一个工程可能导入了很多三方库,我不可能一个一个去弄吧。不用担心,我提供了懒人脚本来完成这个事情。

在Podfile最后添加:

post_install do |installer|
    require './Pods/YCSymbolTracker/YCSymbolTracker/symbol_tracker.rb'
    symbol_tracker(installer)
end

注意: Demo和正式版路径不太一样,这里以正式版的路径为例。

2.1 动态库

如果Podfile中采用use_frameworks!,则(几乎)所有库是动态库。

执行完pod install之后就会执行上面的ruby脚本,脚本中会完成三方库的Clang插桩配置和依赖配置。

2.2 静态库

如果Podfile中不采用use_frameworks!,则(几乎)所有库是静态库。

静态库我们都知道,会被合并到主工程的Mach-O文件中,所以可以直接在Pod工程 SDWebImageBuild SettingsOther C Flags-fsanitize-coverage=func,trace-pc-guard 配置,而不会报找不到符号的错。

执行完pod install之后就会执行上面的ruby脚本,所以脚本中没有添加依赖配置,只添加Clang插桩配置。

2.3 注意事项

导出order文件后,就不再需要YCSymbolTracker库了,删除之后请重新执行pod install

大项目因启动相关符号较多,导出速度可能很慢,请耐心等待。

导出 绿洲 的启动符号,我也卡在启动页等待了很久,不要着急。


还有什么问题欢迎大家提出来~ Have FUN~

如果觉得本文对你有所帮助,给我点个赞吧~