启动优化 二进制重排

作者: Code_人生 | 来源:发表于2020-04-21 14:49 被阅读0次

    一、启动优化

    冷启动:第一次启动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方法
    • 结束,打包上线

    相关文章

      网友评论

        本文标题:启动优化 二进制重排

        本文链接:https://www.haomeiwen.com/subject/uvlkphtx.html