啟動階段: Main 之前 ,Main之后
查看啟動時間
增加環(huán)境變量 Edit schmene -> Arguments -> Environment Variables -> DYLD_PRINT_STATISTICS
dylib loading time: 348.01 milliseconds (26.0%) // 動態(tài)庫加載時間
rebase/binding time: 222.60 milliseconds (16.6%) // rebase:修正偏移指針ASLR binding:fishHook 里面的符號綁定 減少OC類
ObjC setup time: 44.97 milliseconds (3.3%) // runtime類注冊
initializer time: 721.15 milliseconds (53.9%) // Load方法加載 (優(yōu)化 懶加載 可以放在initializer)
slowest intializers :
libSystem.B.dylib : 4.55 milliseconds (0.3%)
libglInterpose.dylib : 435.84 milliseconds (32.6%) //調(diào)試相關(guān)
marsbridgenetwork : 46.28 milliseconds (3.4%)
WeChat : 259.34 milliseconds (19.4%) //主程序
Main階段 優(yōu)化常規(guī)方式
- 懶加載
- 發(fā)揮CPU價值 (多線程進(jìn)行初始化)
- 啟動頁少用XIB和Strotboard
Main 函數(shù)之后
main函數(shù) 到 第一個界面 使用工具類BLStopwatch 查看 在Application 打點(diǎn)開始 在第一個界面 打點(diǎn)結(jié)束
打點(diǎn)信息可以看到哪個方法比較耗時 就優(yōu)化哪個函數(shù)
Main函數(shù)之前
需要只是點(diǎn) 虛擬內(nèi)存 、 物理內(nèi)存 fishHook 、 Clang Hook所有方法、二進(jìn)制重排
二進(jìn)制重排 原理

Page Fault:當(dāng)進(jìn)程訪問一個虛擬內(nèi)存Page而對應(yīng)的物理內(nèi)存卻不存在時,會觸發(fā)一次缺頁中斷(Page Fault),需要操作系統(tǒng)將其調(diào)入主存后再進(jìn)行訪問
Page Fault 為什么會出現(xiàn):一個可執(zhí)行文件可能很大,放在磁盤上,由局部性原理一次只將其中一部分讀進(jìn)內(nèi)存
Instruments 調(diào)試工具 System Trace 查看Main函數(shù)之前耗時
File Backed Page In(Page Fault) 耗時
重排核心問題
- 重排效果怎么樣 - 獲取啟動階段的page fault次數(shù)
- 重排成功了沒 - 拿到當(dāng)前二進(jìn)制的函數(shù)布局
- 如何重排 - 讓鏈接器按照指定順序生成Mach-O
- 重排的內(nèi)容 - 獲取啟動時候用到的函數(shù)
1、重排效果怎么樣 - 獲取啟動階段的page fault次數(shù)
Instruments 調(diào)試工具 System Trace 查看Main函數(shù)之前耗時
File Backed Page In(Page Fault) 耗時
2、重排成功了沒 - 獲取當(dāng)前二進(jìn)制的函數(shù)布局
查看 函數(shù)執(zhí)行順序
Build Setting -> link Map File 設(shè)置為YES
查看 文件
/Intermediates.noindex/TraceDemo.build/Debug-iphonesimulator/TraceDemo.build/TraceDemo-LinkMap-normal-x86_64.txt
3、如何重排 - 讓鏈接器按照指定順序生成Mach-O
生成 .Order 文件 放根目錄 里面放著函數(shù)執(zhí)行順序
Build Setting -> Order File 設(shè)置.Order 文件
4、重排的內(nèi)容 - 獲取啟動時候用到的函數(shù)
使用 clang 生成 order文件
插莊 clang HOOK所有方法
http://clang.llvm.org/docs/SanitizerCoverage.html 官網(wǎng)
OC 配置
build Setting -> other c Flags -fsanitize-coverage=func,trace-pc-guard
Swift 配置
build Setting -> other swift flags 加入以下兩個配置
-sanitize-coverage=func
-sanitize=undefined
需要實(shí)現(xiàn) 一下函數(shù)
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.
}
// This callback is inserted by the compiler on every edge in the
// control flow (some optimizations apply).
// Typically, the compiler will emit the code like this:
// if(*guard)
// __sanitizer_cov_trace_pc_guard(guard);
// But for large functions it will emit a simple call:
// __sanitizer_cov_trace_pc_guard(guard);
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);
}
每個函數(shù)、方法 、load、initialize、block 都會先調(diào)用__sanitizer_cov_trace_pc_guard 函數(shù)
利用 clang 生成 .order文件
使用 clang 獲取 執(zhí)行過的函數(shù) 、 方法 之后生成.oder文件
#import <dlfcn.h>
#import <libkern/OSAtomic.h>
-(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:@"hank.order"];
NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
NSLog(@"%@",funcStr);
}
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.
}
//原子隊(duì)列
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.
/* 精確定位 哪里開始 到哪里結(jié)束! 在這里面做判斷寫條件!*/
void *PC = __builtin_return_address(0);
SYNode *node = malloc(sizeof(SYNode));
*node = (SYNode){PC,NULL};
//進(jìn)入
OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
// Dl_info info;
// dladdr(node->pc, &info);
// printf("fname:%s \nfbase:%p \nsname:%s \nsaddr:%p\n",
// info.dli_fname,
// info.dli_fbase,
// info.dli_sname,
// info.dli_saddr);
//
}