├── Linked_Order_Analyze.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── Linked_Order_Analyze ├── Analyze.h ├── MMTracePCGuard.h ├── main.m ├── Util.h ├── Util.m ├── MMTracePCGuard.m └── Analyze.m ├── LICENSE ├── .gitignore └── README.md /Linked_Order_Analyze.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Linked_Order_Analyze/Analyze.h: -------------------------------------------------------------------------------- 1 | // 2 | // Analyze.h 3 | // Linked_Order_Analyze 4 | // 5 | // Created by 李扬 on 2021/1/17. 6 | // 7 | 8 | #import 9 | 10 | @interface Analyze : NSObject 11 | 12 | + (void)start; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Linked_Order_Analyze/MMTracePCGuard.h: -------------------------------------------------------------------------------- 1 | // 2 | // MMTracePCGuard.h 3 | // Linked_Order_Analyze 4 | // 5 | // Created by 李扬 on 2021/1/17. 6 | // Copyright © 2021 taou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MMTracePCGuard : NSObject 12 | 13 | + (void)analyze; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Linked_Order_Analyze/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Linked_Order_Analyze 4 | // 5 | // Created by 李扬 on 2021/1/17. 6 | // 7 | 8 | #import 9 | #import "Analyze.h" 10 | 11 | int main(int argc, const char * argv[]) { 12 | @autoreleasepool { 13 | [Analyze start]; 14 | } 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /Linked_Order_Analyze.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Linked_Order_Analyze/Util.h: -------------------------------------------------------------------------------- 1 | // 2 | // Util.h 3 | // Linked_Order_Analyze 4 | // 5 | // Created by 李扬 on 2021/1/17. 6 | // 7 | 8 | #import 9 | 10 | @interface Util : NSObject 11 | 12 | + (unsigned long long)decimalFromHexStr:(NSString *)hexStr; 13 | 14 | + (NSString *)hexFromDecimal:(unsigned long long)decimal; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Linked_Order_Analyze/Util.m: -------------------------------------------------------------------------------- 1 | // 2 | // Util.m 3 | // Linked_Order_Analyze 4 | // 5 | // Created by 李扬 on 2021/1/17. 6 | // 7 | 8 | #import "Util.h" 9 | 10 | @implementation Util 11 | 12 | + (unsigned long long)decimalFromHexStr:(NSString *)hexStr 13 | { 14 | unsigned long long result = 0; 15 | NSScanner *scanner = [NSScanner scannerWithString:hexStr]; 16 | 17 | [scanner setScanLocation:0]; // bypass '#' character 18 | [scanner scanHexLongLong:&result]; 19 | 20 | return result; 21 | } 22 | 23 | + (NSString *)hexFromDecimal:(unsigned long long)decimal 24 | { 25 | return [NSString stringWithFormat:@"0x%llX", decimal]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 李扬 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | # 39 | # Pods/ 40 | # 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build/ 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. 54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linked_Order_Analyze 2 | App启动时间优化 二进制重排技术 线下量化预分析工具 3 | 4 | 目的:在没有上线之前可以分析出对App启动优化节省的大致时间,起到指导作用。(_具体能优化多少,以线上数据为准_) 5 | 6 | 建议:每隔三个月执行一次二进制重排更新,确保 PageFault 次数维持在一个较低的稳定的水平 7 | 8 | --- 9 | 10 | ### 大体思路: 11 | ``` 12 | 1. 读取 Linked Map 文件,获取 __Text 代码区的起始地址和块大小,分析得到需要分配的虚拟内存页个数 13 | 2. 获取 __Text 代码区 所有symbol的地址、大小和具体符号,存入字典 14 | 3. 根据字典中的起始地址和所有symbol大小的总和,校验是否和1描述相符 (_发现有重复 symbol 和 symbol地址跳跃现象_) 15 | 4. 分析 二进制重排文件order所包含的symbol,和2中字典做映射,得到symbol对应的地址和内存占用大小 16 | 5. 分析 order文件中所包含的symbol,得到需要占用的原始虚拟内存页个数 17 | 6. 将 order文件中所包含的symbol内存占用大小相加,分析得到 重排后所占虚拟内存页个数 18 | 7. 原始虚拟内存页个数 - 重排后所占虚拟内存页个数 = 节省的虚拟内存页个数 19 | 8. 节省的虚拟内存页个数 * 每一个内存缺页中断大致的处理时间 = 节省的内存缺页中断处理总时间 20 | ``` 21 | 22 | --- 23 | 24 | ### 使用之前: 25 | 1. 使用xcode编译出 linked_map.txt 和 lb.order 文件,并放在同一个目录 26 | 2. 修改下面的路径到上述两个文件的根目录 27 | ``` 28 | // 链接文件和order文件根目录 29 | static NSString * const BASE_PATH = @"/Users/liyang/Desktop/1"; 30 | ``` 31 | 32 | --- 33 | 34 | ### 如何编译出 linked_map.txt 文件 35 | 36 | 1. 在 __Target -> Build Settings__ 下,找到 __Write Link Map File__ 来设置输出与否 , 默认是 no . 37 | 38 | 2. 修改完毕之后,__clean__ 一下,运行工程,__Products -> Show in Finder__,在mach-o文件上上层目录 __Intermediates.noindex__文件下找到一个txt文件。将其重命名为linked_map.txt 39 | 40 | --- 41 | 42 | ### 如何编译出 lb.order 文件 43 | 44 | 1. 在目标工程 __Target -> Build Settings -> Other C Flags__ 添加 __-fsanitize-coverage=func,trace-pc-guard__。 45 | 46 | 如果有swfit代码,也要在 __Other Swift Flags__ 添加 __-sanitize-coverage=func__ 和__-sanitize=undefined__ 47 | 48 | (如果有源码编译的Framework也要添加这些配置。CocoaPods引入的第三方库不建议添加此配置) 49 | 50 | 2. 将 __MMTracePCGuard__ 文件放入到目标工程 51 | 3. 将 __+[MMTracePCGuard analyze]__ 方法放到目标工程认为可以启动结束的位置调用,执行 __clean->build->run__。根据自身工程复杂度的情况,等待几分钟或者十几分钟,就会在沙盒 Documents 的 temp 目录生成 lb.order 文件 52 | 53 | 54 | --- 55 | 56 | ### 最终输出示例: 57 | ``` 58 | ---> 分析结果: 59 | linked map __Text(链接文件): 60 | 起始地址:0x100006A60 61 | 结束地址:0x1021E75E8 62 | 分配的虚拟内存页个数:2169 63 | order symbol(重排文件): 64 | 需要重排的符号个数:4630 65 | 分布的虚拟内存页个数:392 66 | 二进制重排后分布的虚拟内存页个数:99 67 | 内存缺页中断减少的个数:293 68 | 预估节省的时间:146ms 69 | ``` 70 | 71 | --- 72 | 73 | ### 如果做反向验证 74 | 75 | 用二进制重排之后的工程,再次分别编译出 linked_map.txt 和 lb.order 文件,使用此工具再次运行检查。 76 | 77 | 可以得到如下结果 78 | 79 | ``` 80 | ---> 分析结果: 81 | linked map __Text(链接文件): 82 | 起始地址:0x100006A60 83 | 结束地址:0x1021E75E8 84 | 分配的虚拟内存页个数:2169 85 | order symbol(重排文件): 86 | 需要重排的符号个数:4630 87 | 分布的虚拟内存页个数:99 88 | 二进制重排后分布的虚拟内存页个数:99 89 | 内存缺页中断减少的个数:0 90 | 预估节省的时间:0ms 91 | ``` 92 | 93 | 可以看出重排后的二进制文件已经不需要再次进行重排了。 94 | 95 | 至此,二进制重排线下预评估结束 96 | 97 | --- 98 | -------------------------------------------------------------------------------- /Linked_Order_Analyze/MMTracePCGuard.m: -------------------------------------------------------------------------------- 1 | // 2 | // MMTracePCGuard.m 3 | // Linked_Order_Analyze 4 | // 5 | // Created by 李扬 on 2021/1/17. 6 | // Copyright © 2021 taou. All rights reserved. 7 | // 8 | 9 | #import "MMTracePCGuard.h" 10 | #import 11 | #import 12 | 13 | void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, 14 | uint32_t *stop) { 15 | static uint64_t N; // Counter for the guards. 16 | if (start == stop || *start) return; // Initialize only once. 17 | printf("INIT: %p %p\n", start, stop); 18 | for (uint32_t *x = start; x < stop; x++) 19 | *x = ++N; // Guards should start from 1. 20 | } 21 | 22 | //原子队列 23 | static OSQueueHead symboList = OS_ATOMIC_QUEUE_INIT; 24 | //定义符号结构体 25 | typedef struct{ 26 | void * pc; 27 | void * next; 28 | }SymbolNode; 29 | 30 | void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { 31 | //if (!*guard) return; // Duplicate the guard check. 32 | 33 | void *PC = __builtin_return_address(0); 34 | 35 | SymbolNode * node = malloc(sizeof(SymbolNode)); 36 | *node = (SymbolNode){PC,NULL}; 37 | 38 | //入队 39 | // offsetof 用在这里是为了入队添加下一个节点找到 前一个节点next指针的位置 40 | OSAtomicEnqueue(&symboList, node, offsetof(SymbolNode, next)); 41 | } 42 | 43 | //void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { 44 | // if (!*guard) return; // Duplicate the guard check. 45 | // 46 | // void *PC = __builtin_return_address(0); 47 | // Dl_info info; 48 | // dladdr(PC, &info); 49 | // 50 | // printf("-=-=-=-=-=-=-=-=nsname=%s\n\n",info.dli_sname); 51 | // 52 | //// char PcDescr[1024]; 53 | //// printf("guard: %p %x PC %s\n", guard, *guard, PcDescr); 54 | //} 55 | 56 | @implementation MMTracePCGuard 57 | 58 | + (void)analyze 59 | { 60 | NSMutableArray * symbolNames = [NSMutableArray array]; 61 | while (true) { 62 | //offsetof 就是针对某个结构体找到某个属性相对这个结构体的偏移量 63 | SymbolNode * node = OSAtomicDequeue(&symboList, offsetof(SymbolNode, next)); 64 | if (node == NULL) break; 65 | Dl_info info; 66 | dladdr(node->pc, &info); 67 | 68 | NSString * name = @(info.dli_sname); 69 | 70 | // 添加 _ 71 | BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["]; 72 | NSString * symbolName = isObjc ? name : [@"_" stringByAppendingString:name]; 73 | 74 | //去重 75 | if (![symbolNames containsObject:symbolName]) { 76 | [symbolNames addObject:symbolName]; 77 | } 78 | } 79 | 80 | //取反 81 | NSArray * symbolAry = [[symbolNames reverseObjectEnumerator] allObjects]; 82 | NSLog(@"trace pc guard:\n---------------------------------------------------------------------------------\n"); 83 | NSLog(@"%@",symbolAry); 84 | NSLog(@"---------------------------------------------------------------------------------"); 85 | 86 | //将结果写入到文件 87 | NSString * funcString = [symbolAry componentsJoinedByString:@"\n"]; 88 | NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"lb.order"]; 89 | NSData * fileContents = [funcString dataUsingEncoding:NSUTF8StringEncoding]; 90 | BOOL result = [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil]; 91 | if (result) { 92 | NSLog(@"%@",filePath); 93 | }else{ 94 | NSLog(@"文件写入出错"); 95 | } 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Linked_Order_Analyze/Analyze.m: -------------------------------------------------------------------------------- 1 | // 2 | // Analyze.m 3 | // Linked_Order_Analyze 4 | // 5 | // Created by 李扬 on 2021/1/17. 6 | // 7 | 8 | #import "Analyze.h" 9 | #import "Util.h" 10 | 11 | /* 12 | 二进制重排技术 线下量化分析总体思路: 13 | 1. 读取 Linked Map 文件,获取 __Text 代码区的起始地址和块大小,分析得到需要分配的虚拟内存页个数 14 | 2. 获取 __Text 代码区 所有symbol的地址、大小和具体符号,存入字典 15 | 3. 根据字典中的起始地址和所有symbol大小的总和,校验是否和1描述相符 (发现有重复 symbol 和 symbol地址跳跃现象) 16 | 4. 分析 二进制重排文件order所包含的symbol,和2中字典做映射,得到order中symbol对应的地址和内存占用大小 17 | 5. 分析 order文件中所包含的symbol,得到需要占用的原始虚拟内存页个数 18 | 6. 将 order文件中所包含的symbol内存占用大小相加,分析得到重排后所占虚拟内存页个数 19 | 7. 原始虚拟内存页个数 - 重排后所占虚拟内存页个数 = 节省的虚拟内存页个数 20 | 8. 节省的虚拟内存页个数 * 每一个内存缺页中断大致的处理时间 = 节省的内存缺页中断处理总时间 21 | */ 22 | // 链接文件和order文件根目录 注意:换成自己的路径字符串 23 | static NSString * const BASE_PATH = @"/Users/liyang/Desktop/1"; 24 | // 链接文件名 25 | static NSString * const LINKED_MAP = @"linked_map.txt"; 26 | // order 文件名 27 | static NSString * const LB_ORDER = @"lb.order"; 28 | // iOS平台虚拟内存页大小(16k) 29 | static unsigned long long const VM_PAGE_SIZE_LINK = 16 * 1024; 30 | 31 | // 链接文件 __Text 映射字典 key:symbol value:[起始地址, 占用内存大小] 32 | static NSMutableDictionary *> *linkedTextDictM; 33 | // 排序文件 __Text 映射字典 key:symbol value:[起始地址, 占用内存大小] 34 | static NSMutableDictionary *> *orderTextDictM; 35 | 36 | // 处理每个内存缺页中断所需要的时间:0.1ms ~ 1ms之间,取中位数 0.5ms 37 | static double const PAGE_FAULT_CONST_ESTIMATE_TIME = 0.5; 38 | 39 | @implementation Analyze 40 | 41 | + (void)start 42 | { 43 | // 链接文件完整路径 44 | NSString *linked_map_path = [NSString stringWithFormat:@"%@/%@", BASE_PATH, LINKED_MAP]; 45 | // 排序文件完整路径 46 | NSString *order_path = [NSString stringWithFormat:@"%@/%@", BASE_PATH, LB_ORDER]; 47 | 48 | // 链接文件内容 49 | NSError *linkedMapContentError; 50 | NSString* linkedMapFileContents = [NSString stringWithContentsOfFile:linked_map_path encoding:NSASCIIStringEncoding error:&linkedMapContentError]; 51 | if (linkedMapContentError) 52 | { 53 | abort(); 54 | return; 55 | }; 56 | 57 | // __text 开始地址 十进制 58 | __block unsigned long long sectionTextStartAddressDecimalValue = 0; 59 | // __text 结束地址 十进制 60 | __block unsigned long long sectionTextEndAddressDecimalValue = 0; 61 | // __Text 所占用虚拟内存页个数 62 | __block unsigned long long sectionTextLinkedVMPageCount = 0; 63 | 64 | // 1. 分析 linked map 文件,得出分配多少个虚拟内存页;section __Text __text 开始地址 十进制;section __Text __text 结束地址 十进制 65 | NSRegularExpression *regexLinkedMap = [NSRegularExpression 66 | regularExpressionWithPattern:@"(0x[A-F\\d]*)\\s+(0x[A-F\\d]*)\\s+__TEXT\\s+__text" 67 | options:0 68 | error:nil]; 69 | [regexLinkedMap enumerateMatchesInString:linkedMapFileContents options:0 range:NSMakeRange(0, linkedMapFileContents.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { 70 | 71 | if ([result numberOfRanges] != 3) return; 72 | 73 | // 分析 linked_map 文件,判断出虚拟内存页个数 74 | // 匹配的 section __Text __text 字符串行 75 | NSString *sectionTextLine = [linkedMapFileContents substringWithRange:result.range]; 76 | NSLog(@"----__Text:%@", sectionTextLine); 77 | 78 | // 匹配 section __Text __text 首地址 79 | NSString *sectionTextStartAddressHexStr = [linkedMapFileContents substringWithRange:[result rangeAtIndex:1]]; 80 | sectionTextStartAddressDecimalValue = [Util decimalFromHexStr:sectionTextStartAddressHexStr]; 81 | // 匹配 section __Text __text 内存映射大小 82 | NSString *sectionTextSizeHexStr = [linkedMapFileContents substringWithRange:[result rangeAtIndex:2]]; 83 | 84 | // 分析分配的虚拟内存页个数 85 | unsigned long long section_text_decimal_size = [Util decimalFromHexStr:sectionTextSizeHexStr]; 86 | sectionTextLinkedVMPageCount = section_text_decimal_size / VM_PAGE_SIZE_LINK + 1; 87 | 88 | NSLog(@"----linked __text vm count:%llu", sectionTextLinkedVMPageCount); 89 | 90 | sectionTextEndAddressDecimalValue = sectionTextStartAddressDecimalValue + section_text_decimal_size; 91 | }]; 92 | 93 | // 2. 分析 linked map 文件,在开始地址和结束地址之间额symble字典 94 | // __Text 所有 symbol 占用虚拟内存大小总和,发现有symbol重复和地址跳跃的现象 95 | __block unsigned long long linkedTextSumAllSymbolSize = 0; 96 | // __Text 所有 symbol 个数,和 sumAllSymbolSize 一起反推 1 中数据的正确性。个数是一直的 97 | __block unsigned long long linkedTextSymbolLineCount = 0; 98 | 99 | // __Text symbol 重复的情况 100 | __block NSMutableArray *linkedTextSymbolDuplicatedArrM = [NSMutableArray array]; 101 | // 预计算下一行 symbol 起始地址,以确定地址跳跃情况 102 | __block unsigned long long linkedTextNextSymbolPreComputeStartDecialValue = 0; 103 | // __Text symbol start + size != next start 地址跳跃情况 104 | __block NSMutableArray *linkedTextSymbolStartAddressJumpedArrM = [NSMutableArray array]; 105 | 106 | linkedTextDictM = [NSMutableDictionary dictionary]; 107 | NSRegularExpression *regexLinkedMapLines = [NSRegularExpression 108 | regularExpressionWithPattern:@"(0x[A-F\\d]*)\\s+(0x[A-F\\d]*)\\s+\\[.*\\] (.+)" 109 | options:0 110 | error:nil]; 111 | [regexLinkedMapLines enumerateMatchesInString:linkedMapFileContents options:0 range:NSMakeRange(0, linkedMapFileContents.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { 112 | 113 | if ([result numberOfRanges] != 4) return; 114 | 115 | // 匹配的 symbol __Text 一行行的字符串 116 | NSString *linkedTextSymbolLine = [linkedMapFileContents substringWithRange:result.range]; 117 | 118 | // symble 地址 119 | NSString *linkedTextSymbolStartAddressHexStr = [linkedMapFileContents substringWithRange:[result rangeAtIndex:1]]; 120 | unsigned long long linkedTextSymbolStartAddressDecimalValue = [Util decimalFromHexStr:linkedTextSymbolStartAddressHexStr]; 121 | 122 | // symbol 大小 123 | NSString *linkedTextSymbolSizeHexStr = [linkedMapFileContents substringWithRange:[result rangeAtIndex:2]]; 124 | unsigned long long linkedTextSymbolSizeDecimalValue = [Util decimalFromHexStr:linkedTextSymbolSizeHexStr]; 125 | // 具体的 symbol 126 | NSString *symbol = [linkedMapFileContents substringWithRange:[result rangeAtIndex:3]]; 127 | 128 | // 地址在 section __Text 之间 129 | if (linkedTextSymbolStartAddressDecimalValue >= sectionTextStartAddressDecimalValue && linkedTextSymbolStartAddressDecimalValue < sectionTextEndAddressDecimalValue) 130 | { 131 | NSLog(@"----linked_symbol:%@", linkedTextSymbolLine); 132 | 133 | // 计算 linked __Text 符号所占用虚拟内存页大小 用来和 1 做反向验证 134 | linkedTextSumAllSymbolSize += linkedTextSymbolSizeDecimalValue; 135 | // 计算 linked __Text 符号个数 用来和 1 做反向验证 136 | linkedTextSymbolLineCount++; 137 | 138 | // 地址跳跃检测和记录 139 | if (linkedTextNextSymbolPreComputeStartDecialValue != 0 && linkedTextNextSymbolPreComputeStartDecialValue != linkedTextSymbolStartAddressDecimalValue) 140 | { 141 | [linkedTextSymbolStartAddressJumpedArrM addObject:linkedTextSymbolLine ?: @""]; 142 | } 143 | linkedTextNextSymbolPreComputeStartDecialValue = linkedTextSymbolStartAddressDecimalValue + linkedTextSymbolSizeDecimalValue; 144 | 145 | // 符号重复 忽略 146 | if (linkedTextDictM[symbol] != nil) 147 | { 148 | [linkedTextSymbolDuplicatedArrM addObject:symbol]; 149 | return; 150 | } 151 | 152 | // 存入字典 153 | [linkedTextDictM setValue:@[@(linkedTextSymbolStartAddressDecimalValue), @(linkedTextSymbolSizeDecimalValue)] forKey:symbol]; 154 | } 155 | }]; 156 | 157 | // 3. 根据字典中起始地址和所有symbol大小总和,校验是否和1描述相符 158 | // 个数相符,但发现有symbol重复和地址跳跃的现象 159 | 160 | // 4. 分析 二进制重排文件order所包含的symbol,和 2 中字典做映射,得到symbol对应的地址和内存占用大小 161 | // 5. 分析 order文件中所包含的symbol,得到占用原始虚拟内存页个数 162 | orderTextDictM = [NSMutableDictionary dictionary]; 163 | // order 中symbol的个数 164 | __block unsigned long long orderSymbolLineCount = 0; 165 | // order 中symbol所占用内存页分布情况 166 | __block NSMutableSet *orderVMPageSetM = [NSMutableSet set]; 167 | // order 中 所有 symbol 占用大小总和,除以 VM_PAGE_SIZE_LINK 可以得到排序后占用的连续虚拟内页个数 168 | __block unsigned long long orderSumAllSymbolSize = 0; 169 | // order 中 未命中 section __Text 的symbol集合 170 | __block NSMutableArray *orderUntargetSymbolsArrM = [NSMutableArray array]; 171 | 172 | // order 文件内容 173 | NSError *orderContentError = nil; 174 | NSString* orderpFileContents = [NSString stringWithContentsOfFile:order_path encoding:NSASCIIStringEncoding error:&orderContentError]; 175 | if (orderContentError) 176 | { 177 | abort(); 178 | return; 179 | }; 180 | 181 | NSRegularExpression *regexOrder = [NSRegularExpression 182 | regularExpressionWithPattern:@"(.+)" 183 | options:0 184 | error:nil]; 185 | [regexOrder enumerateMatchesInString:orderpFileContents options:0 range:NSMakeRange(0, orderpFileContents.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { 186 | if ([result numberOfRanges] != 2) return; 187 | 188 | // 得到 order 一行行 symbol 符号 189 | NSString *orderSymbolLine = [orderpFileContents substringWithRange:[result rangeAtIndex:1]]; 190 | 191 | NSArray *arr = linkedTextDictM[orderSymbolLine]; 192 | if (arr == nil) 193 | { 194 | // order 对应 linked 文件,未命中的情况 195 | [orderUntargetSymbolsArrM addObject:orderSymbolLine ?: @""]; 196 | return; 197 | } 198 | 199 | // 计算 order symbol 行数 200 | orderSymbolLineCount++; 201 | 202 | // 获得 symbole 十进制 的地址 203 | unsigned long long orderSymbolStartAddressDecimalValue = [arr.firstObject unsignedLongLongValue]; 204 | if (orderSymbolStartAddressDecimalValue < sectionTextStartAddressDecimalValue || sectionTextStartAddressDecimalValue > sectionTextEndAddressDecimalValue) 205 | { 206 | // 发现非法地址,不在 __Text 起始地址和终止地址之间 207 | abort(); 208 | return; 209 | } 210 | unsigned long long orderSymbolRelativeStartAddressDecimalValue = orderSymbolStartAddressDecimalValue - sectionTextStartAddressDecimalValue; 211 | // 得到当前 symbole 所在的内存页序号index 212 | unsigned long long orderSymbolVMPageIndex = orderSymbolRelativeStartAddressDecimalValue / VM_PAGE_SIZE_LINK; 213 | [orderVMPageSetM addObject:@(orderSymbolVMPageIndex)]; // 得到被分配到了哪些虚拟内存页 214 | 215 | // 获得 symbole 十进制 的占用内存大小 216 | unsigned long long orderSymbolSizeDecimalValue = [arr.lastObject unsignedLongLongValue]; 217 | orderSumAllSymbolSize += orderSymbolSizeDecimalValue; 218 | }]; 219 | 220 | // 6. 将 order文件中所包含的symbol内存占用相加,分析得到 重排后所占虚拟内存页个数 221 | unsigned long long orderSymbolAllVMPageCount = orderSumAllSymbolSize / VM_PAGE_SIZE_LINK + 1; 222 | if (orderSymbolAllVMPageCount > orderVMPageSetM.count) orderSymbolAllVMPageCount = orderVMPageSetM.count; 223 | 224 | printf("\n---> 分析结果:"); 225 | printf("\n linked map __Text(链接文件):"); 226 | printf("\n 起始地址:%s", [[Util hexFromDecimal:sectionTextStartAddressDecimalValue] UTF8String]); 227 | printf("\n 结束地址:%s", [[Util hexFromDecimal:sectionTextEndAddressDecimalValue] UTF8String]); 228 | printf("\n 分配的虚拟内存页个数:%llu", sectionTextLinkedVMPageCount); 229 | printf("\n order symbol(重排文件):"); 230 | printf("\n 需要重排的符号个数:%llu", orderSymbolLineCount); 231 | printf("\n 分布的虚拟内存页个数:%lu", (unsigned long)orderVMPageSetM.count); 232 | printf("\n 二进制重排后分布的虚拟内存页个数:%llu", orderSymbolAllVMPageCount); 233 | // 7. 原始虚拟内存页个数 - 重排后所占虚拟内存页个数 = 节省的虚拟内存页个数 234 | printf("\n 内存缺页中断减少的个数:%llu", orderVMPageSetM.count - orderSymbolAllVMPageCount); 235 | // 8. 节省的虚拟内存页个数 * 每一个内存缺页中断大致的处理时间 = 节省的内存缺页中断处理总时间 236 | printf("\n 预估节省的时间:%.0fms\n", (orderVMPageSetM.count - orderSymbolAllVMPageCount) * PAGE_FAULT_CONST_ESTIMATE_TIME); 237 | } 238 | 239 | @end 240 | -------------------------------------------------------------------------------- /Linked_Order_Analyze.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B6313D6F25B4341C0035448B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B6313D6E25B4341C0035448B /* main.m */; }; 11 | B6313D7825B434280035448B /* Util.m in Sources */ = {isa = PBXBuildFile; fileRef = B6313D7725B434280035448B /* Util.m */; }; 12 | B6313D7C25B4348C0035448B /* Analyze.m in Sources */ = {isa = PBXBuildFile; fileRef = B6313D7B25B4348C0035448B /* Analyze.m */; }; 13 | B6856E142696E8E800ADAB16 /* MMTracePCGuard.m in Sources */ = {isa = PBXBuildFile; fileRef = B6856E132696E8E800ADAB16 /* MMTracePCGuard.m */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | B6313D6925B4341C0035448B /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = /usr/share/man/man1/; 21 | dstSubfolderSpec = 0; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 1; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | B6313D6B25B4341C0035448B /* Linked_Order_Analyze */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Linked_Order_Analyze; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | B6313D6E25B4341C0035448B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 31 | B6313D7625B434280035448B /* Util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Util.h; sourceTree = ""; }; 32 | B6313D7725B434280035448B /* Util.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Util.m; sourceTree = ""; }; 33 | B6313D7A25B4348C0035448B /* Analyze.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Analyze.h; sourceTree = ""; }; 34 | B6313D7B25B4348C0035448B /* Analyze.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Analyze.m; sourceTree = ""; }; 35 | B6856E122696E8E800ADAB16 /* MMTracePCGuard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMTracePCGuard.h; sourceTree = ""; }; 36 | B6856E132696E8E800ADAB16 /* MMTracePCGuard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMTracePCGuard.m; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | B6313D6825B4341C0035448B /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | B6313D6225B4341C0035448B = { 51 | isa = PBXGroup; 52 | children = ( 53 | B6313D6D25B4341C0035448B /* Linked_Order_Analyze */, 54 | B6313D6C25B4341C0035448B /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | B6313D6C25B4341C0035448B /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | B6313D6B25B4341C0035448B /* Linked_Order_Analyze */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | B6313D6D25B4341C0035448B /* Linked_Order_Analyze */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | B6856E122696E8E800ADAB16 /* MMTracePCGuard.h */, 70 | B6856E132696E8E800ADAB16 /* MMTracePCGuard.m */, 71 | B6313D6E25B4341C0035448B /* main.m */, 72 | B6313D7A25B4348C0035448B /* Analyze.h */, 73 | B6313D7B25B4348C0035448B /* Analyze.m */, 74 | B6313D7625B434280035448B /* Util.h */, 75 | B6313D7725B434280035448B /* Util.m */, 76 | ); 77 | path = Linked_Order_Analyze; 78 | sourceTree = ""; 79 | }; 80 | /* End PBXGroup section */ 81 | 82 | /* Begin PBXNativeTarget section */ 83 | B6313D6A25B4341C0035448B /* Linked_Order_Analyze */ = { 84 | isa = PBXNativeTarget; 85 | buildConfigurationList = B6313D7225B4341C0035448B /* Build configuration list for PBXNativeTarget "Linked_Order_Analyze" */; 86 | buildPhases = ( 87 | B6313D6725B4341C0035448B /* Sources */, 88 | B6313D6825B4341C0035448B /* Frameworks */, 89 | B6313D6925B4341C0035448B /* CopyFiles */, 90 | ); 91 | buildRules = ( 92 | ); 93 | dependencies = ( 94 | ); 95 | name = Linked_Order_Analyze; 96 | productName = Linked_Order_Analyze; 97 | productReference = B6313D6B25B4341C0035448B /* Linked_Order_Analyze */; 98 | productType = "com.apple.product-type.tool"; 99 | }; 100 | /* End PBXNativeTarget section */ 101 | 102 | /* Begin PBXProject section */ 103 | B6313D6325B4341C0035448B /* Project object */ = { 104 | isa = PBXProject; 105 | attributes = { 106 | LastUpgradeCheck = 1230; 107 | TargetAttributes = { 108 | B6313D6A25B4341C0035448B = { 109 | CreatedOnToolsVersion = 12.3; 110 | }; 111 | }; 112 | }; 113 | buildConfigurationList = B6313D6625B4341C0035448B /* Build configuration list for PBXProject "Linked_Order_Analyze" */; 114 | compatibilityVersion = "Xcode 9.3"; 115 | developmentRegion = en; 116 | hasScannedForEncodings = 0; 117 | knownRegions = ( 118 | en, 119 | Base, 120 | ); 121 | mainGroup = B6313D6225B4341C0035448B; 122 | productRefGroup = B6313D6C25B4341C0035448B /* Products */; 123 | projectDirPath = ""; 124 | projectRoot = ""; 125 | targets = ( 126 | B6313D6A25B4341C0035448B /* Linked_Order_Analyze */, 127 | ); 128 | }; 129 | /* End PBXProject section */ 130 | 131 | /* Begin PBXSourcesBuildPhase section */ 132 | B6313D6725B4341C0035448B /* Sources */ = { 133 | isa = PBXSourcesBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | B6313D7C25B4348C0035448B /* Analyze.m in Sources */, 137 | B6313D6F25B4341C0035448B /* main.m in Sources */, 138 | B6856E142696E8E800ADAB16 /* MMTracePCGuard.m in Sources */, 139 | B6313D7825B434280035448B /* Util.m in Sources */, 140 | ); 141 | runOnlyForDeploymentPostprocessing = 0; 142 | }; 143 | /* End PBXSourcesBuildPhase section */ 144 | 145 | /* Begin XCBuildConfiguration section */ 146 | B6313D7025B4341C0035448B /* Debug */ = { 147 | isa = XCBuildConfiguration; 148 | buildSettings = { 149 | ALWAYS_SEARCH_USER_PATHS = NO; 150 | CLANG_ANALYZER_NONNULL = YES; 151 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 152 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 153 | CLANG_CXX_LIBRARY = "libc++"; 154 | CLANG_ENABLE_MODULES = YES; 155 | CLANG_ENABLE_OBJC_ARC = YES; 156 | CLANG_ENABLE_OBJC_WEAK = YES; 157 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 158 | CLANG_WARN_BOOL_CONVERSION = YES; 159 | CLANG_WARN_COMMA = YES; 160 | CLANG_WARN_CONSTANT_CONVERSION = YES; 161 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 162 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 163 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 164 | CLANG_WARN_EMPTY_BODY = YES; 165 | CLANG_WARN_ENUM_CONVERSION = YES; 166 | CLANG_WARN_INFINITE_RECURSION = YES; 167 | CLANG_WARN_INT_CONVERSION = YES; 168 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 169 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 170 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 171 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 172 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 173 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 174 | CLANG_WARN_STRICT_PROTOTYPES = YES; 175 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 176 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 177 | CLANG_WARN_UNREACHABLE_CODE = YES; 178 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 179 | COPY_PHASE_STRIP = NO; 180 | DEBUG_INFORMATION_FORMAT = dwarf; 181 | ENABLE_STRICT_OBJC_MSGSEND = YES; 182 | ENABLE_TESTABILITY = YES; 183 | GCC_C_LANGUAGE_STANDARD = gnu11; 184 | GCC_DYNAMIC_NO_PIC = NO; 185 | GCC_NO_COMMON_BLOCKS = YES; 186 | GCC_OPTIMIZATION_LEVEL = 0; 187 | GCC_PREPROCESSOR_DEFINITIONS = ( 188 | "DEBUG=1", 189 | "$(inherited)", 190 | ); 191 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 192 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 193 | GCC_WARN_UNDECLARED_SELECTOR = YES; 194 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 195 | GCC_WARN_UNUSED_FUNCTION = YES; 196 | GCC_WARN_UNUSED_VARIABLE = YES; 197 | MACOSX_DEPLOYMENT_TARGET = 11.1; 198 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 199 | MTL_FAST_MATH = YES; 200 | ONLY_ACTIVE_ARCH = YES; 201 | SDKROOT = macosx; 202 | }; 203 | name = Debug; 204 | }; 205 | B6313D7125B4341C0035448B /* Release */ = { 206 | isa = XCBuildConfiguration; 207 | buildSettings = { 208 | ALWAYS_SEARCH_USER_PATHS = NO; 209 | CLANG_ANALYZER_NONNULL = YES; 210 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 211 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 212 | CLANG_CXX_LIBRARY = "libc++"; 213 | CLANG_ENABLE_MODULES = YES; 214 | CLANG_ENABLE_OBJC_ARC = YES; 215 | CLANG_ENABLE_OBJC_WEAK = YES; 216 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 217 | CLANG_WARN_BOOL_CONVERSION = YES; 218 | CLANG_WARN_COMMA = YES; 219 | CLANG_WARN_CONSTANT_CONVERSION = YES; 220 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 221 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 222 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 223 | CLANG_WARN_EMPTY_BODY = YES; 224 | CLANG_WARN_ENUM_CONVERSION = YES; 225 | CLANG_WARN_INFINITE_RECURSION = YES; 226 | CLANG_WARN_INT_CONVERSION = YES; 227 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 228 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 229 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 230 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 231 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 232 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 233 | CLANG_WARN_STRICT_PROTOTYPES = YES; 234 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 235 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 236 | CLANG_WARN_UNREACHABLE_CODE = YES; 237 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 238 | COPY_PHASE_STRIP = NO; 239 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 240 | ENABLE_NS_ASSERTIONS = NO; 241 | ENABLE_STRICT_OBJC_MSGSEND = YES; 242 | GCC_C_LANGUAGE_STANDARD = gnu11; 243 | GCC_NO_COMMON_BLOCKS = YES; 244 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 245 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 246 | GCC_WARN_UNDECLARED_SELECTOR = YES; 247 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 248 | GCC_WARN_UNUSED_FUNCTION = YES; 249 | GCC_WARN_UNUSED_VARIABLE = YES; 250 | MACOSX_DEPLOYMENT_TARGET = 11.1; 251 | MTL_ENABLE_DEBUG_INFO = NO; 252 | MTL_FAST_MATH = YES; 253 | SDKROOT = macosx; 254 | }; 255 | name = Release; 256 | }; 257 | B6313D7325B4341C0035448B /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | CODE_SIGN_STYLE = Manual; 261 | DEVELOPMENT_TEAM = ""; 262 | PRODUCT_BUNDLE_IDENTIFIER = "com.andy.linked-order-analyze"; 263 | PRODUCT_NAME = "$(TARGET_NAME)"; 264 | PROVISIONING_PROFILE_SPECIFIER = ""; 265 | }; 266 | name = Debug; 267 | }; 268 | B6313D7425B4341C0035448B /* Release */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | CODE_SIGN_STYLE = Manual; 272 | DEVELOPMENT_TEAM = ""; 273 | PRODUCT_BUNDLE_IDENTIFIER = "com.andy.linked-order-analyze"; 274 | PRODUCT_NAME = "$(TARGET_NAME)"; 275 | PROVISIONING_PROFILE_SPECIFIER = ""; 276 | }; 277 | name = Release; 278 | }; 279 | /* End XCBuildConfiguration section */ 280 | 281 | /* Begin XCConfigurationList section */ 282 | B6313D6625B4341C0035448B /* Build configuration list for PBXProject "Linked_Order_Analyze" */ = { 283 | isa = XCConfigurationList; 284 | buildConfigurations = ( 285 | B6313D7025B4341C0035448B /* Debug */, 286 | B6313D7125B4341C0035448B /* Release */, 287 | ); 288 | defaultConfigurationIsVisible = 0; 289 | defaultConfigurationName = Release; 290 | }; 291 | B6313D7225B4341C0035448B /* Build configuration list for PBXNativeTarget "Linked_Order_Analyze" */ = { 292 | isa = XCConfigurationList; 293 | buildConfigurations = ( 294 | B6313D7325B4341C0035448B /* Debug */, 295 | B6313D7425B4341C0035448B /* Release */, 296 | ); 297 | defaultConfigurationIsVisible = 0; 298 | defaultConfigurationName = Release; 299 | }; 300 | /* End XCConfigurationList section */ 301 | }; 302 | rootObject = B6313D6325B4341C0035448B /* Project object */; 303 | } 304 | --------------------------------------------------------------------------------