iOS 启动优化 二进制重排

1,247 阅读2分钟

iOS 启动优化 二进制重排

配置

  1. 在Build Settings中添加编译选项Other C Flags增加-fsanitize-coverage=func,trace-pc-guard 如果是OC Swift混编, 则在Other Swift Flags增加-sanitize-coverage=func,`-sanitize=undefined

  2. 统计程序启动的函数执行情况在首页的viewDidAppear函数中加上生成orderFile的函数,然后运行app,会在app的沙盒的tmp目录下生成,htclangtrace.order的文件 ,获取tmp 下的htclangtrace.order 导入工程

-(void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  
  [HTClangTrace generateOrderFile]; 
} 
  1. 在Build Settings中搜索Order File 设置为htclangtrace.order的路径 具体获取 HTClangTrace.h 文件如下
//
//  HTClangTrace.m
//  HTScore_iOS
//
//  Created by muxinjian on 2020/6/3.
//  Copyright © 2020 huatu. All rights reserved.
//


#import "HTClangTrace.h"
#import <dlfcn.h>
#import <libkern/OSAtomic.h>

@implementation HTClangTrace

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.
    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;
} SymbolNode;
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    if (!*guard) return;
    void *PC = __builtin_return_address(0);
    SymbolNode *node = malloc(sizeof(SymbolNode));
    *node = (SymbolNode){PC, NULL};
    OSAtomicEnqueue(&symbolList, node, offsetof(SymbolNode, next));
}

+ (void)generateOrderFile {
    NSMutableArray <NSString *> *symbolNames = [NSMutableArray array];
    while (YES) {
        SymbolNode * node = OSAtomicDequeue(&symbolList, offsetof(SymbolNode, next));
        if (node == NULL) {
            break;
        }
        Dl_info info;
        dladdr(node->pc, &info);
        NSString *name = @(info.dli_sname);
        // 判断是不是oc方法,是的话直接加入符号数组
        BOOL isInstanceMethod = [name hasPrefix:@"-["];
        BOOL isClassMethod = [name hasPrefix:@"+["];
        BOOL isObjc = isInstanceMethod || isClassMethod;
        NSString * symbolName = isObjc ? name: [@"_" stringByAppendingString:name];
        [symbolNames addObject:symbolName];
    }
    // 取反:将先调用的函数放到前面
    NSEnumerator * emt = [symbolNames reverseObjectEnumerator];
    // 去重:由于一个函数可能执行多次,__sanitizer_cov_trace_pc_guard会执行多次,就加了重复的了,所以去重一下
    NSMutableArray<NSString *> *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
    NSString *name;
    while (name = [emt nextObject]) {
        if (![funcs containsObject:name]) {
            [funcs addObject:name];
        }
    }
    // 由于trace了所有执行的函数,这里我们就把本函数移除掉
    [funcs removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]];
    // 写order文件
    NSString *funcStr = [funcs componentsJoinedByString:@"\n"];
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"htclangtrace.order"];
    NSData *fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];

    if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]){
         NSLog(@"orderPath:%@",filePath);
    }else{
         NSLog(@"生成orderPath失败");
    }
}
#pragma mark - Util

+ (BOOL)isObjcMethodBySymbolName:(NSString *)symbolName {
    
    BOOL isInstanceMethod = [symbolName hasPrefix:@"-["];
    BOOL isClassMethod = [symbolName hasPrefix:@"+["];
    return isInstanceMethod || isClassMethod;
}

@end