启动优化 二进制重排

一、启动优化

冷启动:第一次启动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 Thread

5、选择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查看汇编代码
  • 0x100d348480x100d3484c0x100d34850,这些是代码的地址

添加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方法
  • 结束,打包上线
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容