We talked earlier about the principle of binary reordering startup optimization. Before reordering, the code in each data page may or may not be the code at startup time, which leads to waste. The binary rearrangement puts all code at the top of the list at startup time, which reduces the number of page misses and saves startup time.

Clang piling is configured

  • Step 1

First, we open the Clang document, where the Traching PC is used to track the commands executed by the current CPU.

  • Step 2

-fsanitize-coverage=trace- PC-guard = fsanitize-coverage=trace- PC-guard __sanitizer_cov_trace_pc_guard_init and __sanitizer_cov_trace_pc_guard cannot be found. So let’s implement these two functions.

  • Step 3
#import "ViewController.h"
#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.h>

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

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) {
  
}

@end
Copy the code

After implementing these two functions in your project, compile them.

Debug the __sanitizer_cov_trace_pc_guard_init function

Here, star and stop are unsigned integers, representing the start and end positions, and x ending position -4 represents the number of symbols. The number of symbols printed the first time is 3. The second time we added the oc method, the C function, and the number of outputs after the block was 6. This shows that __sanitizer_cov_trace_pc_guard_init can collect the number of symbols and call back.

Debug the __sanitizer_cov_trace_pc_guard_init function

As you can see by breaking the __sanitizer_cov_trace_pc_guard_init function, When we execute oc, c, and block, __sanitizer_cov_trace_pc_guard_init, The __sanitizer_cov_trace_pc_guard_init function can intercept the execution of all methods, functions, and blocks. It is important to note that all symbols intercepted are in the current project. Symbols not in the binary of the current project will not be intercepted in the system library or in the third-party library.

Now that we know __sanitizer_cov_trace_pc_guard_init can intercept all symbols, how does it intercept? Let’s move on.

Clang piling principle

The assembly code bl 0x100732684 can be seen when executing a method, function, or block. This means that once the clang staked flag is added, the compiler adds a __sanitizer_cov_trace_pc_guard code to the edge of all methods, functions, and blocks. You’re changing the binary. This is something only compilers can do, and compilers do it when they translate high-level languages into assembly code. There is no official explanation as to whether to do it in the front end or in the back end, we can only analyze it, either in the IR phase in the front end or in the back end. But pile checking is definitely a performance loss, so we generally do code review when used.

Gets the symbol name

//HOOK all callback functions!! void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { void *PC = __builtin_return_address(0); Dl_info info; dladdr(PC, &info); NSLog(@"fname:%s\n fbase:%p\n sname:%s\n saddr:%p\n", info.dli_fname, info.dli_fbase, info.dli_sname, info.dli_saddr); }Copy the code
typedef struct dl_info { const char *dli_fname; /* The mach-o file name */ void *dli_fbase; /* file address */ const char *dli_sname; /* function name */ void *dli_saddr; /* function address */} Dl_info;Copy the code

Here the __builtin_return_address function means to get the internal return address of the current function, namely the address of the previous function, the caller of the current function. So *PC is the address of the 0th sentence of the previous function. Here we have a chance to get the symbolic name of the last function. Dladdr (node-> PC, &info); / / add to the info structure the address data that PC points to. Finally, the corresponding information is also output through printing. This is where you get the names of all the symbols.

Order files are generated by saving symbols in atomic queues

#import "ViewController.h" #include <stdint.h> #include <stdio.h> #include <sanitizer/coverage_interface.h> #import < DLFCN. H > #import <libkern/ osatomy. h> static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT; Typedef struct {void * PC; void * next; } SYNode; @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; test(); } void(^block)(void) = ^(void){NSLog(@"block function execute! ); }; Void test(){NSLog(@"test function executed!" ); block(); } 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) { void *PC = __builtin_return_address(0); SYNode * node = malloc(sizeof(SYNode)); *node = (SYNode){PC,NULL}; // Write the node structure to the symbolList, offsetof(SYNode, next) sets the next node, the next node, OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next)); } // Generate order file!! -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {// NSMutableArray<NSString *> * symbleNames = [NSMutableArray array]; While (YES) {// Circulatory body! Intercept!! SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode,next)); SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode,next)); if (node == NULL) { break; } Dl_info info; dladdr(node->pc, &info); NSString * name = @(info.dli_sname); // add _ to the function name. Is a function to add _ BOOL isObjc = [name hasPrefix: @ "+ ["] | | [name hasPrefix: @" - ["]; nsstrings * symbolName = isObjc? Name: [@ "_" stringByAppendingString: name], [symbleNames addObject: symbolName];} / / reverse time array, because the stack sequence is the function call, So the inverse NSEnumerator * em = [symbleNames reverseObjectEnumerator]; NSMutableArray * funcs = [NSMutableArray ArrayWithCapacity: symbleNames. Count]; nsstrings * name; / / to the array to heavy here, While (name = [em nextObject]) {if (![funcs containsObject:name]) {// Array has no name [funcs addObject:name];}} [funcs removeObject:[NSString stringWithFormat:@"%s",__func__]]; // Write a file //1 [funcs componentsJoinedByString:@"\n"]; NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"chenxi.order"]; NSData * file = [funcStr dataUsingEncoding:NSUTF8StringEncoding]; / / written to the sandbox path [[NSFileManager defaultManager] createFileAtPath: filePath contents: the file attributes: nil); / / print an array of strings NSLog(@"%@",funcStr); }Copy the code

We have already obtained the names of all symbols through interception, so we can save the intercepted symbols and generate order through code, as shown in the code above. But there are a few things to note here.

  • The __sanitizer_cov_trace_pc_guard function is also executed in the child thread if it is a method in the child thread, so be aware of the multithreading effect when writing here, which is stored through the atomic queue.

  • While loop, since the __sanitizer_cov_trace_pc_guard function also intercepts the while loop, making recursive calls when writing, / / fsanitize-coverage=func,trace-pc-guard / / other C flags / / fsanitize-coverage=func,trace-pc-guard / / other C flags / / fsanitize-coverage=func,trace-pc-guard

  • When generating an order file, negate the symbolic method because it is written in reverse order, and de-duplicate the symbol name.

  • The function name is preceded by an _.

View -(void)touchesBegan:(NSSet

*)touches withEvent:(UIEvent *)event At this point we can configure the order to the project. At this point the binary rearrangement is complete.

Swift symbol coverage

When we add to giveswiftClass, and calledswiftClass method, found not collectedswiftThis is becauseswiftSeparate configuration is required.

Need to be inother swift flagsHere to add-sanitize-coverage=func-sanitize-undefinedThese two parameters.

You can see it when you print itswiftMethods are collected, and the compiler pairsswiftThere is confusion in the symbol names, as you can seeswiftRelative to theOCIt’s safer.