iOS启动优化之编译器插桩
一、什么是编译器插桩
编译器插桩是指在编译期间修改已有的代码或者生成新的代码。
二、准备知识
Tracing PCs是用来跟踪CPU将要执行的指令代码;我们可以借此拿到程序启动时需要的用到的函数,然后减少缺页中断的产生,从而达到启动优化的效果。如何配置如下面这个图所示:
工程中配置使用它-fsanitized-coverage=trace-pc-guard,编译器会在每个代码边缘插入以下代码: __sanitizer_cov_trace_pc_guard(&guard_variable)。
在工程上配置了-fsanitized-coverage=trace-pc-guard后再编译工程就会报错,需要定义两个函数__sanitizer_cov_trace_pc_guard_init和__sanitizer_cov_trace_pc_guard。
三 、具体举例
1. 新建一个空的过程,然后添加配置:
Target -> Build Setting -> Custom Complier Flags -> Other C Flags 添加 fsanitize-coverage=func,trace-pc-guard
2.在Viewcontroller.m的文件中添加代码
#include <stdint.h> #include <stdio.h> #include <sanitizer/coverage_interface.h> void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { static uint64_t N; // Counter for the guards. if (start == stop || *start) return; // Initialize only once. printf("INIT: %p %p\n", start, stop); for (uint32_t *x = start; x < stop; x++) *x = ++N; // Guards should start from 1. } void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { if (!*guard) return; void *PC = __builtin_return_address(0); char PcDescr[1024]; printf("guard: %p %x PC %s\n", guard, *guard, PcDescr); }
3.跑代码看输出结果
__sanitizer_cov_trace_pc_guard_init:获取整个工程中所有函数/方法的数量;start stop是存储的是工程中符号的个数
__sanitizer_cov_trace_pc_guard:拿到工程中这些函数/方法的地址;系统每调用一个方法,都会回调这个__sanitizer_cov_trace_pc_guard;在汇编中,编译器会在每一个方法之前插入__sanitizer_cov_trace_pc_guard;
由此可以知道app启动的程序,方便二进制重排
四 获取工程启动时需要用到的函数(三种方式)
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { //第一种方式 NSLog(@"%s",__func__); //第二种方式 if (!*guard) return; //调用__sanitizer_cov_trace_pc_guard的函数的函数地址 void *PC = __builtin_return_address(0); //可以获得一个函数的名称以及地址 Dl_info info; dladdr(PC, &info); NSLog(@"dil_fname %s",info.dli_fname);//文件的路径 NSLog(@"dil_fname %p",info.dli_fbase);//文件的地址 NSLog(@"dil_fname %s",info.dli_sname);//符号的名字,这个是我们需要的 NSLog(@"dil_fname %p",info.dli_saddr);//符号的地址 //第三种方式 // typedef struct { // void * pc; // int abc; // } SYNode; SYNode *node = malloc(sizeof(SYNode)); *node = (SYNode){PC,0};
五、生成的APP启动函数顺序的order文件
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //创建数组 NSMutableArray *symbleNames = [NSMutableArray array]; while (YES) { SYNode *node = OSAtomicDequeue(&symbolList, offsetof(SYNode, abc)); if (node == NULL) { break;; } Dl_info info; dladdr(node->pc, &info); // printf("%s\n",info.dli_sname); //转为OC字符串方便操作 NSString *name = @(info.dli_sname); //写法一 添加 //OC方法 直接添加到数组 //hasPrefix 表示以+[开头 // if ([name hasPrefix:@"+["] || [name hasPrefix:@"-["]) { // [symbleNames addObject:name]; // continue; // } // [symbleNames addObject:[@"_" stringByAppendingString:name]]; //写法二 BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["]; NSString * symbleName = isObjc ? name : [@"_" stringByAppendingString:name]; //取反 [symbleNames addObject:symbleName]; symbleNames = (NSMutableArray *)[[symbleNames reverseObjectEnumerator] allObjects]; NSLog(@"%@",symbleNames); } //去重 NSEnumerator *em = [symbleNames reverseObjectEnumerator];//反向遍历 //新数组存储 NSMutableArray *funcs = [NSMutableArray arrayWithCapacity:symbleNames.count]; NSString *name; while (name = [em nextObject]) { if (![funcs containsObject:name]) { [funcs addObject:name]; } } NSLog(@"%@",funcs); //数组转为字符串 NSString *funcStr = [funcs componentsJoinedByString:@"\n"]; //文件路径 写文件 NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"lg.order"]; //文件内容 NSData *file = [funcStr dataUsingEncoding:NSUTF8StringEncoding]; //写入文件 [[NSFileManager defaultManager] createFileAtPath:filePath contents:file attributes:nil]; //获取沙盒主目录路径 NSLog(@"%@",NSHomeDirectory()); NSLog(@"%@",funcStr); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术