启动优化 二进制重排
一、启动优化
冷启动:第一次启动App
热启动
启动优化一般讲的是冷启动
启动阶段:main函数之前、main函数之后
main 阶段:
1、懒加载
2、发挥CPU的价值(多线程进行初始化)
3、启动时避免使用Xib、stroyboard
阶段一、main函数之前
打印启动时间
- 添加
DYLD_PRINT_STATISTICS
- dylib loading time: 加载动态库 (优化:建议不要大于6个)
- rebase/binding time: 修正内部偏移指针/外部符号绑定 (优化:减少OC类) 优化少
- ObjC setup time: OC类的注册 (优化:减少OC类) 优化少
- initializer time: 加载load方法 (优化:使用懒加载)
阶段二、main函数之后
main 开始 到 第一个界面。
打点,使用BLStopwatch.h和BLStopwatch.m这个类
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[BLStopwatch sharedStopwatch] start];
int a = 0;
for (int i = 0; i < 10000000; i++) {
a++;
}
[[BLStopwatch sharedStopwatch] splitWithDescription:@"didFinishLaunchingWithOptions"];
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
//刷新时间:
[[BLStopwatch sharedStopwatch] refreshMedianTime];
int a = 0;
for (int i = 0; i < 10000000; i++) {
a++;
};
[[BLStopwatch sharedStopwatch] splitWithDescription:@"viewDidLoad"];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
//刷新时间:
[[BLStopwatch sharedStopwatch] refreshMedianTime];
int a = 0;
for (int i = 0; i < 10000000; i++) {
a++;
};
[[BLStopwatch sharedStopwatch] splitWithDescription:@"viewDidAppear"];
[[BLStopwatch sharedStopwatch] stopAndPresentResultsThenReset];
}
二、二进制重排
二进制重排是在main函数之前
物理内存
虚拟内存 : 解决安全问题、解决内存使用率问题
- 解决安全问题:映射表(页表)(虚拟页表)
- 解决内存使用率问题:内存分页管理。缺页中断,然后加载到物理内存,加载之前会签名加载的页;如果启动的时候要加载的代码分别在不同的页,那么缺页中断时间就比较长,这时就出现了二进制重排(把启动要加载的代码放在前面几页)。使用内存分页后,就会导致代码的加载都是从0开始的,为了防止黑客,就出现了ASLR。
内存分页技术
MacOS 、linux (4K为一页)
iOS(16K为一页)
PageFault(缺页中断)
1、command+I 2、选择System Trace 3、点击一下,第一个页面出现后,再点击一下 4、搜索Main Thread5、选择Main Thread、选择Virtual Memory。File Backed Page in
就是PageFault
二进制重排优化是在链接阶段对即将生成的可执行文件进行重新排列
order_file
1、打开objc4-750
源码
libobjc.order
存放的是方法的调用顺序,可以用终端cat打开2、Build Settings
中搜索order file
load方法的执行顺序
生成LinkMap文件
-
Build Settings
中搜索link map
,No改为Yes,然后Command+B
,build一下,就会生成LinkMap文件
打开LinkMap文件
- Address: 函数真实实现的地址(汇编代码的地址)(代码的地址)
- Size:函数的大小,写的代码的多与少
- File:所在文件
- Name: 方法名
-
0x0000000100d30000(ASLR)+00004848(偏移) = 0x100d34848
- 在
+[ViewController load]
方法里面打断点,看到的地址和0x0000000100d30000(ASLR)+00004848(偏移) = 0x100d34848
相等,dis -s 0x100d34848
查看汇编代码 -
0x100d34848
、0x100d3484c
、0x100d34850
,这些是代码的地址
添加dyz.order文件
-
在项目的根目录创建一个.order文件,写入如下代码
-
Build Settings
中搜索order file,添加dyz.order文件的地址(./dyz.order
或者${SRCROOT}/dyz.order
)
-
command + shift + k
清空一下缓存,command+B
编译一下 -
再次查看LinkMap文件
Clang插庄
-
Build Settings
搜索 other c flags,添加-fsanitize-coverage=trace-pc-guard
参数
- 粘贴如下代码到项目
extern "C" 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);
[图片上传中...(Snip20200420_13.png-54b663-1587378588681-0)]
for (uint32_t *x = start; x < stop; x++)
*x = ++N; // Guards should start from 1.
}
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if (!*guard) return; // Duplicate the guard check.
// If you set *guard to 0 this code will not be called again for this edge.
// Now you can get the PC and do whatever you want:
// store it somewhere or symbolize it and print right away.
// The values of `*guard` are as you set them in
// __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive
// and use them to dereference an array or a bit vector.
void *PC = __builtin_return_address(0);
char PcDescr[1024];
// This function is a part of the sanitizer run-time.
// To use it, link with AddressSanitizer or other sanitizer.
__sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));
printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}
-
调试
暂停,然后附加,然后x读start内存
读头x 0x10d4d7490
读尾x 0x10d4d74c8-0x4
,读尾需要减四个字节,因为一个方法占四个字节,而这个0x10d4d74c8
地址是尾方法结束的地址,所以如果需要读尾方法的地址,就需要减掉四个字节
-
静态插庄!
所有方法、函数、block 内部都加入一行代码!调用__sanitizer_cov_trace_pc_guard
-
dis -s 0x00000001043e9a54
-
dis -s 0x00000001043e9a54-116
减去偏移值
-
拿到所有符号。导入
dlfcn.h
头文件
#import "ViewController.h"
#import <dlfcn.h>
@interface ViewController ()
@end
@implementation ViewController
+(void)initialize
{
}
void(^block1)(void) = ^(void) {
};
void test(){
block1();
}
+(void)load
{
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
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; // Duplicate the guard check.
void *PC = __builtin_return_address(0);
Dl_info info;
dladdr(PC, &info);
printf("dli_fname:%s \n dli_fbase:%p \n dli_sname:%s \n dli_saddr:%p \n",info.dli_fname,info.dli_fbase,info.dli_sname,info.dli_saddr);
// char PcDescr[1024];
// __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));
// printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}
@end
- while循环,中会调用
__sanitizer_cov_trace_pc_guard
,会导致一直打印-[ViewController touchesBegan:withEvent:]
#import "ViewController.h"
#import <dlfcn.h>
#import <libkern/OSAtomic.h>
@interface ViewController ()
@end
@implementation ViewController
+(void)initialize
{
}
void(^block1)(void) = ^(void) {
};
void test(){
block1();
}
+(void)load
{
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
while (YES) {
SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
if (node == NULL) {
break;
}
Dl_info info;
dladdr(node->pc, &info);
printf("%s \n",info.dli_sname);
}
}
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.
}
//原子队列
static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
//定义符号结构体
typedef struct {
void *pc;
void *next;
}SYNode;
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if (!*guard) return; // Duplicate the guard check.
void *PC = __builtin_return_address(0);
SYNode *node = malloc(sizeof(SYNode));
*node = (SYNode){PC,NULL};
//进入
OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
}
@end
解决方法
Other C Flags
的参数,等号后面加上func
,例如-fsanitize-coverage=func,trace-pc-guard
- 取反、去重、函数符号前面添加下划线(除了OC方法)、添加load
- 添加load:注销
__sanitizer_cov_trace_pc_guard
中的if (!*guard) return;
,load的守卫是0
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSMutableArray <NSString *> * symbolNames = [NSMutableArray array];
while (YES) {
SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
if (node == NULL) {
break;
}
Dl_info info;
dladdr(node->pc, &info);
NSString * name = @(info.dli_sname);
BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];
NSString * symbolName = isObjc ? name: [@"_" stringByAppendingString:name];
[symbolNames addObject:symbolName];
}
//取反
NSEnumerator * emt = [symbolNames reverseObjectEnumerator];
//去重
NSMutableArray<NSString *> *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
NSString * name;
while (name = [emt nextObject]) {
if (![funcs containsObject:name]) {
[funcs addObject:name];
}
}
//干掉自己!
[funcs removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]];
//将数组变成字符串
NSString * funcStr = [funcs componentsJoinedByString:@"\n"];
NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dyz.order"];
NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
NSLog(@"%@",filePath);
}
-
下载下来,显示包内容
-
项目中如果是OC和Swift混编
1、在Build Settings
搜索other swift flags
,如果是OC项目,里面没有Swift文件,那么搜索不到Other Swift Flags
,只有项目里面有了Swift文件才会搜索到。
2、添加参数-sanitize-coverage=func
和-sanitize=undefined
3、导入Swift头文件#import "TraceDemo-Swift.h"
,调用Swift方法[SwiftTest swiftTestLoad];
4、Swift方法同样可以hook到
最后
- 把得到的
dyz.order
文件拷贝到项目的根目录下。 -
Build Settings
中搜索order file,添加dyz.order文件的地址(./dyz.order
或者${SRCROOT}/dyz.order
) -
Build Settings
中搜索link map
,如果是Yes则改回No - 去掉 Other C Flags的参数
-fsanitize-coverage=func,trace-pc-guard
- 去掉 Other Swift Flags的参数
-sanitize-coverage=func
和-sanitize=undefined
- 注销__sanitizer_cov_trace_pc_guard_init和__sanitizer_cov_trace_pc_guard方法
- 结束,打包上线