YCSymbolTracker打个小补丁

YCSymbolTracker是一个通过clang插桩记录应用启动过程中发起的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//
//  YCSymbolTracker.m
//  YCSymbolTracker
//
//  Created by ycpeng on 2020/6/10.
//

#import "YCSymbolTracker.h"

#import <stdint.h>
#import <stdio.h>
#import <sanitizer/coverage_interface.h>
#import <libkern/OSAtomic.h>
#import <dlfcn.h>
#import <os/lock.h>

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 = (uint32_t)++N;  // Guards should start from 1.
}

void printInfo(void *PC) {
    Dl_info info;
    dladdr(PC, &info);
    printf("fnam:%s \n fbase:%p \n sname:%s \n saddr:%p \n",
           info.dli_fname,
           info.dli_fbase,
           info.dli_sname,
           info.dli_saddr);
}

static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
static BOOL finished;
static NSMutableDictionary *mappings;
static os_unfair_lock lock;

typedef struct {
    void *pc;
    void *next;
} SymbolNode;

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
//    if (!*guard) return;  // Duplicate the guard check.
    if (finished) {
        return;
    }
   
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mappings = [NSMutableDictionary dictionaryWithCapacity:1000];
        lock = OS_UNFAIR_LOCK_INIT;
    });
   
    void *PC = __builtin_return_address(0);
    int64_t pcw = (int64_t)PC;
    int32_t pch = (pcw & 0xFFFFFFFF00000000) >> 32;
   
    os_unfair_lock_lock(&lock);
    NSMutableDictionary *sub = [mappings objectForKey:@(pch)];
    if (sub && sub[@(pcw)]) {
        os_unfair_lock_unlock(&lock);
        return;
    }
    if (!sub) {
        sub = [NSMutableDictionary dictionaryWithCapacity:10000];
        mappings[@(pch)] = sub;
    }
    sub[@(pcw)] = @1;
    os_unfair_lock_unlock(&lock);
   
    SymbolNode * node = malloc(sizeof(SymbolNode));
    *node = (SymbolNode){PC, NULL};
   
    OSAtomicEnqueue(&symbolList, node, offsetof(SymbolNode, next));

//    printInfo(PC);
}

@implementation YCSymbolTracker

+ (BOOL)exportSymbolsWithFilePath:(nonnull NSString *)filePath
{
    finished = YES;
   
    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);
        BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["]; // Objective-C method do nothing
        NSString * symbolName = isObjc? name : [@"_" stringByAppendingString:name]; // c function with "_"
        [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];
        }
    }
    // remove current method symbol (not necessary when launch)
    [funcs removeObject:[NSString stringWithFormat:@"%s", __FUNCTION__]];
   
    NSString *funcStr = [funcs componentsJoinedByString:@"\n"];
    NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
    if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
    }
    return [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
}

@end

添加了加锁逻辑以及重复项目过滤。

Comments