啟動優(yōu)化 二進(jìn)制重排

一、啟動優(yōu)化

冷啟動:第一次啟動App
熱啟動

啟動優(yōu)化一般講的是冷啟動

啟動階段:main函數(shù)之前、main函數(shù)之后

main 階段:
1、懶加載
2、發(fā)揮CPU的價值(多線程進(jìn)行初始化)
3、啟動時避免使用Xib、stroyboard

階段一、main函數(shù)之前

打印啟動時間
  • 添加 DYLD_PRINT_STATISTICS
  • dylib loading time: 加載動態(tài)庫 (優(yōu)化:建議不要大于6個)
  • rebase/binding time: 修正內(nèi)部偏移指針/外部符號綁定 (優(yōu)化:減少OC類) 優(yōu)化少
  • ObjC setup time: OC類的注冊 (優(yōu)化:減少OC類) 優(yōu)化少
  • initializer time: 加載load方法 (優(yōu)化:使用懶加載)

階段二、main函數(shù)之后

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];
    
}

二、二進(jìn)制重排

二進(jìn)制重排是在main函數(shù)之前

物理內(nèi)存
虛擬內(nèi)存 : 解決安全問題、解決內(nèi)存使用率問題

  • 解決安全問題:映射表(頁表)(虛擬頁表)
  • 解決內(nèi)存使用率問題:內(nèi)存分頁管理。缺頁中斷,然后加載到物理內(nèi)存,加載之前會簽名加載的頁;如果啟動的時候要加載的代碼分別在不同的頁,那么缺頁中斷時間就比較長,這時就出現(xiàn)了二進(jìn)制重排(把啟動要加載的代碼放在前面幾頁)。使用內(nèi)存分頁后,就會導(dǎo)致代碼的加載都是從0開始的,為了防止黑客,就出現(xiàn)了ASLR。

內(nèi)存分頁技術(shù)
MacOS 、linux (4K為一頁)
iOS(16K為一頁)

PageFault(缺頁中斷)

1、command+I

2、選擇System Trace

3、點擊一下,第一個頁面出現(xiàn)后,再點擊一下

4、搜索Main Thread

5、選擇Main Thread、選擇Virtual Memory。File Backed Page in 就是PageFault

二進(jìn)制重排優(yōu)化是在鏈接階段對即將生成的可執(zhí)行文件進(jìn)行重新排列

order_file

1、打開objc4-750源碼


libobjc.order存放的是方法的調(diào)用順序,可以用終端cat打開

2、Build Settings中搜索order file

load方法的執(zhí)行順序

生成LinkMap文件

  • Build Settings 中搜索 link map,No改為Yes,然后Command+B,build一下,就會生成LinkMap文件

打開LinkMap文件

  • Address: 函數(shù)真實實現(xiàn)的地址(匯編代碼的地址)(代碼的地址)
  • Size:函數(shù)的大小,寫的代碼的多與少
  • File:所在文件
  • Name: 方法名


  • 0x0000000100d30000(ASLR)+00004848(偏移) = 0x100d34848


  • +[ViewController load]方法里面打斷點,看到的地址和0x0000000100d30000(ASLR)+00004848(偏移) = 0x100d34848相等,dis -s 0x100d34848查看匯編代碼
  • 0x100d348480x100d3484c、0x100d34850,這些是代碼的地址

添加dyz.order文件

  • 在項目的根目錄創(chuàng)建一個.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參數(shù)
  • 粘貼如下代碼到項目
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);
}
  • 調(diào)試
    暫停,然后附加,然后x讀start內(nèi)存


    讀頭 x 0x10d4d7490
    讀尾 x 0x10d4d74c8-0x4,讀尾需要減四個字節(jié),因為一個方法占四個字節(jié),而這個0x10d4d74c8地址是尾方法結(jié)束的地址,所以如果需要讀尾方法的地址,就需要減掉四個字節(jié)

  • 靜態(tài)插莊!
    所有方法、函數(shù)、block 內(nèi)部都加入一行代碼!調(diào)用 __sanitizer_cov_trace_pc_guard

  • dis -s 0x00000001043e9a54

  • dis -s 0x00000001043e9a54-116 減去偏移值

  • 拿到所有符號。導(dǎo)入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循環(huán),中會調(diào)用__sanitizer_cov_trace_pc_guard,會導(dǎo)致一直打印-[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;
//定義符號結(jié)構(gòu)體
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};
    //進(jìn)入
    OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
}

@end


解決方法 Other C Flags的參數(shù),等號后面加上func,例如-fsanitize-coverage=func,trace-pc-guard

  • 取反、去重、函數(shù)符號前面添加下劃線(除了OC方法)、添加load
  • 添加load:注銷__sanitizer_cov_trace_pc_guard中的if (!*guard) return;,load的守衛(wèi)是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__]];
    //將數(shù)組變成字符串
    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);
}
  • 下載下來,顯示包內(nèi)容




  • 項目中如果是OC和Swift混編
    1、在Build Settings搜索other swift flags,如果是OC項目,里面沒有Swift文件,那么搜索不到Other Swift Flags,只有項目里面有了Swift文件才會搜索到。



    2、添加參數(shù)-sanitize-coverage=func-sanitize=undefined

    3、導(dǎo)入Swift頭文件#import "TraceDemo-Swift.h",調(diào)用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的參數(shù) -fsanitize-coverage=func,trace-pc-guard
  • 去掉 Other Swift Flags的參數(shù) -sanitize-coverage=func-sanitize=undefined
  • 注銷__sanitizer_cov_trace_pc_guard_init和__sanitizer_cov_trace_pc_guard方法
  • 結(jié)束,打包上線
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容