Clang官方文档:llvm的子库
背景
二进制重排,可减少编译阶段的Page fault(缺页)产生的数量,对启动起到一定的优化作用,缺页会导致物理内存不连续
Page fault 在 Instruments的表现:搜索 Main Thread 底部输出日志筛选:Virtual Memory 查看File Backed Page In 其中耗时时长及count表现出缺页带来的影响
一、项目工程 Build Setting 中设置
关于OC:
搜索 Other C Flags 设置如下( 推荐第二个 )
-fsanitize-coverage=trace-pc-guard
//仅拦截方法设置,加参数func,防止死循环,取一个后再加入一个(无穷无尽)
-fsanitize-coverage=func,trace-pc-guard
关于Swift:
搜索 Other Swift Flags 设置如下
-sanitize-coverage=func
-sanitize=undefined
二、任意类中写入代码
引入头文件:
#import <dlfcn.h>
#import <libkern/OSAtomic.h>
#include <sanitizer/coverage_interface.h>
触发2个C函数:
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop)
void __sanitizer_cov_trace_pc_guard(uint32_t *guard)
写入以下代码:
//定义原子队列
static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
//定义符号结构体
typedef struct {
void *pc;
void *next;
} QSNode;
//里面反应了项目中符号的个数!!
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
uint32_t *stop) {
static uint64_t N;
if (start == stop || *start) return;
// printf("INIT: %p %p\n", start, stop);
for (uint32_t *x = start; x < stop; x++)
*x = ++N;
}
//HOOK一切的回调函数!!
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
void *PC = __builtin_return_address(0);
//创建结构体
QSNode * node = malloc(sizeof(QSNode));
*node = (QSNode){PC,NULL};
//结构体入栈
OSAtomicEnqueue(&symbolList, node, offsetof(QSNode, next));
}
//生成order文件!!
- (void)outOrderFile {
//定义数组
NSMutableArray<NSString *> * symbleNames = [NSMutableArray array];
while (YES) {//循环体内!进行了拦截!!
QSNode * node = OSAtomicDequeue(&symbolList, offsetof(QSNode,next));
if (node == NULL) { break; }
Dl_info info;
dladdr(node->pc, &info);
NSString * name = @(info.dli_sname);//转字符串
/*!
给函数名称添加 _
Obj-C 的类方法,带前缀 +[
Obj-C 的实例方法,带前缀 -[
C 方法、block调用、swift方法调用,手动加前缀, _
*/
BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];
NSString * symbolName = isObjc ? name : [@"_" stringByAppendingString:name];
[symbleNames addObject:symbolName];
}
//反向遍历数组
// symbleNames = (NSMutableArray<NSString *> *)[[symbleNames reverseObjectEnumerator] allObjects];
// 数组去重:symbleNames = (NSMutableArray<NSString *> *)[symbleNames valueForKeyPath:@"@distinctUnionOfObjects.self"];
// NSLog(@"%@",symbleNames);
NSEnumerator * em = [symbleNames reverseObjectEnumerator];
NSMutableArray * funcs = [NSMutableArray arrayWithCapacity:symbleNames.count];
NSString * name;
while (name = [em nextObject]) {
if (![funcs containsObject:name]) {//数组没有name
[funcs addObject:name];
}
}
//去掉自己!
[funcs removeObject:[NSString stringWithFormat:@"%s",__func__]];
//编成字符串格式,写入文件
/*!
1.如果是模拟器编译,文件路径:filePath可以直接找到;
2.真机编译(上线使用真机编译文件),在Xcode工具栏:Window -> Devices and Simulators (快捷键:Command + Shift + 2), 真机中选中当前的app(下方有“+”、“-”、设置按钮图标),点击设置选择Download Container下载内容,右击显示包内容:AppData/tmp/dataReset.order
*/
NSString * funcStr = [funcs componentsJoinedByString:@"\n"];
NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dataReset.order"];
NSData * file = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:file attributes:nil];
NSLog(@"\n\n====文件路径:%@====数量:%ld",filePath,funcs.count);
NSLog(@"\n\n%@",funcStr);
}
三、使用order文件及补充知识内容
参考Demo:JABaseUIKit
应用启动时间打印:
Eidt Sehemes
Run -> Arguments -> Environments Variables
添加:DYLD_PRINT_STATISTICS ,可设置值为YES或1,也可不设值
启动控制台输出结果:
Total pre-main time: 721.74 milliseconds (100.0%)
1. dylib loading time: 601.12 milliseconds (83.2%)
动态库载入时长(不宜过多,建议6个,过多可以考虑动态库合并)
2. rebase/binding time: 26.70 milliseconds (3.6%)
重定位和符号绑定(Mach-O)
虚拟内存:(rebase: ASLR(程序启动的随机偏移地址) + offset(偏移量) )
了解虚拟内存与物理内存的发展史
3. ObjC setup time: 66.36 milliseconds (9.1%)
OC(动态语言)类的注册(runtime维护映射SEL、IMP信息表),多余废弃的类移除
4. initializer time: 27.54 milliseconds (3.8%)
执行load、构造函数(c/c++等)的耗时
5. Debug时产生,不需要关注
slowest intializers :
libSystem.B.dylib : 2.92 milliseconds (0.4%)
libMainThreadChecker.dylib : 16.67 milliseconds (2.3%)
Command + i,选择运行程序(打开Instruments)
第一次启动:冷启动,耗时长,杀掉立即重启:热启动,耗时较短,物理内存中存在缓存
顶部搜索:Main Thread
底部输出日志筛选:Virtual Memory
File Backed Page In = Page fault(缺页) 耗时优化(查看时长和count)
新建一个.order文件(上面步骤代码中生成的:dataReset.order)并在 Build setting 中(Order File)设置.order文件夹所在的路径(./dataReset.order一般放置项目工程同级目录下)
order文件格式如:
_main
+[ViewController load]
+[AppDelegate load]
_objctest(不存在时会自动优化)
在Build setting中Write Link Map File设置为YES,编译完成后搜索Path to Link Map File中的Debug/Release文件路径查看-LinkMap--.text 文件,即为重排后的二进制文件(# Address Size File Name),快速小技巧:编译完成后,在Products文件下会生成.app文件,Show In Finder 后查看上层目录,找到Buiid或者Build下层Intermediates.noindex文件夹