aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/testing/dump_clang_coverage/write_profile_data.cpp
blob: 13d3f918c7b091e8d9d08af343c73d5846804813 (plain) (blame)
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
123
124
125
126
127
128
129
130
131
#include <dlfcn.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern "C" {
    void __llvm_profile_initialize_file(void);
    int __llvm_profile_write_file(void);
    void __llvm_profile_set_filename(const char* filename_pattern);

    // there might no llmv rt, for example for the targets from contrib
    __attribute__((weak)) int __llvm_profile_write_file(void) {
        return 0;
    }
    __attribute__((weak)) void __llvm_profile_initialize_file(void) {
    }
    __attribute__((weak)) void __llvm_profile_set_filename(const char*) {
    }
}

namespace {
    void dummy() {
    }
}

bool getSoName(char* buff, size_t size) {
    // returns so name for shared objects and exe_name for binaries
    Dl_info dl_info = {0, 0, 0, 0};
    if (dladdr((void*)(intptr_t)dummy, &dl_info) != 0) {
        if (dl_info.dli_fname) {
            const char* name = dl_info.dli_fname;
            char real_path[PATH_MAX];
            const char* resolved = realpath(name, real_path);
            if (resolved != NULL)
                name = real_path;
            const char* lastSlash = strrchr(name, '/');
            if (!!lastSlash) {
                name = lastSlash + 1;
            }
            strncpy(buff, name, size);
            return true;
        }
    }
    return false;
}

bool getExeName(char* buff, size_t size) {
    ssize_t len = readlink("/proc/self/exe", buff, size);
    if (len <= 0) {
        return false;
    }

    buff[len] = '\0';
    char* lastSlash = strrchr(buff, '/');
    if (!!lastSlash) {
        strncpy(buff, lastSlash + 1, size);
        buff[(buff + len) - lastSlash] = '\0';
    }
    return true;
}

bool getSelfName(char* buff, size_t size) {
#if defined(_musl_)
    return getExeName(buff, size);
#else
    return getSoName(buff, size);
#endif
}

void replaceFirst(char* data, size_t dsize, const char* pat, size_t psize, const char* repl, size_t rsize) {
    char* patPtr = strstr(data, pat);
    if (!patPtr) {
        return;
    }

    char tmp[PATH_MAX] = {0};
    char* tmpPtr = &tmp[0];

    strcpy(tmpPtr, patPtr + psize);
    strcpy(patPtr, repl);
    strcpy(patPtr + rsize, tmpPtr);
    data[dsize - psize + rsize] = '\0';
}

// Adds support of the specifier '%e' (executable filename (without path prefix)) to the LLVM_PROFILE_FILE
void parseAndSetFilename() {
    const char* profile_file = getenv("LLVM_PROFILE_FILE");
    if (!profile_file)
        return;

    // __llvm_profile_set_filename doesn't copy name, so it must remain valid
    static char pattern[PATH_MAX] = {0};
    char* patternPtr = &pattern[0];

    strncpy(patternPtr, profile_file, PATH_MAX - 1);

    if (!!strstr(patternPtr, "%e")) {
        char buff[PATH_MAX] = {0};
        char* buffPtr = &buff[0];

        if (getSelfName(buffPtr, PATH_MAX)) {
            size_t patternSize = strlen(patternPtr);
            size_t buffSize = strlen(buffPtr);

            if (patternSize + buffSize >= PATH_MAX) {
                abort();
            }
            replaceFirst(patternPtr, patternSize, "%e", 2, buffPtr, buffSize);
        }
    }

    __llvm_profile_set_filename(patternPtr);
}

void __attribute__((constructor)) premain() {
    parseAndSetFilename();
    if (getenv("YA_COVERAGE_DUMP_PROFILE_AND_EXIT")) {
        __llvm_profile_initialize_file();
        int rc = __llvm_profile_write_file();
        if (!rc && getenv("YA_COVERAGE_DUMP_PROFILE_EXIT_CODE")) {
            if (const char* token = getenv("YA_COVERAGE_DUMP_PROFILE_RELIABILITY_TOKEN")) {
                fprintf(stdout, "%s", token);
                fflush(stdout);
            }
            rc = atoi(getenv("YA_COVERAGE_DUMP_PROFILE_EXIT_CODE"));
        }
        exit(rc);
    }
}