1 /* LLVM-to-GCOV converter by D.C. van Moolenbroek */ 2 /* 3 * Originally, we had a GCOV code coverage implementation for GCC only. We 4 * have now largely switched to LLVM, and LLVM uses a different internal 5 * implementation of the coverage data generation. For regular userland 6 * programs, the implementation is part of LLVM compiler-rt's libprofile_rt. 7 * That implementation is unsuitable for our system services. Instead, this 8 * file converts the calls used by LLVM into _gcov_f*() calls expected by our 9 * GCOV-for-GCC implementation, thus adding support for LLVM coverage by 10 * leveraging our previous GCC support. 11 */ 12 13 #if __clang__ 14 15 #include <stdlib.h> 16 #include <string.h> 17 #include <minix/syslib.h> 18 #include <minix/sysutil.h> 19 #include <minix/gcov.h> 20 #include <assert.h> 21 22 /* 23 * What is the maximum number of source modules for one single system service? 24 * This number is currently way higher than needed, but if we ever add support 25 * for coverage of system service libraries (e.g., libsys and libminc), this 26 * number may not even be high enough. A warning is printed on overflow. 27 * Note that we need this to be a static array, because we cannot use malloc() 28 * in particular in the initialization stage of the VM service. 29 */ 30 #define NR_MODULES 256 31 32 /* 33 * The code in this file is a MINIX3 service specific replacement of the 34 * GCDAProfiling.c code in LLVM's compiler-rt. Their code cannot be used 35 * directly because they assume a userland environment, using all sorts of 36 * POSIX calls as well as malloc(3), none of which we can offer for system 37 * services in this case. So, we provide our own implementation instead. 38 * However, while compiler-rt is always kept in sync with the LLVM profiling 39 * data emitter, we do not have that luxury. The current version of this 40 * implementation has been written for LLVM 3.4 and 3.6, between which the LLVM 41 * GCOV ABI changed. Our current implementation supports both versions, but 42 * may break with newer LLVM versions, even though we should be good up to and 43 * possibly including LLVM 4.0 at least. Hopefully, at this point the LLVM 44 * GCOV ABI should have stabilized a bit. 45 * 46 * Note that since we do not have access to internal LLVM headers here, an ABI 47 * mismatch would not be noticable until llvm-cov fails to load the resulting 48 * files. This whole mess is worth it only because we can really, really use 49 * the coverage information for our test sets.. 50 */ 51 #if __clang_major__ == 3 && __clang_minor__ == 4 52 #define LLVM_35 0 /* version 3.4 only */ 53 #elif __clang_major__ == 3 && __clang_minor__ >= 5 54 #define LLVM_35 1 /* version 3.5 and later */ 55 #else 56 #error "unknown LLVM/clang version, manual inspection required" 57 #endif 58 59 typedef void (*write_cb_t)(void); 60 typedef void (*flush_cb_t)(void); 61 62 /* 63 * Except for llvm_gcda_emit_function(), these functions are already declared 64 * in the 3.5+ ABI style. With the 3.4 ABI, some parameters may have garbage. 65 */ 66 void llvm_gcda_start_file(const char *, const char *, uint32_t); 67 void llvm_gcda_emit_function(uint32_t, const char *, 68 #if LLVM_35 69 uint32_t, uint8_t, uint32_t); 70 #else 71 uint8_t); 72 #endif 73 void llvm_gcda_emit_arcs(uint32_t, uint64_t *); 74 void llvm_gcda_summary_info(void); 75 void llvm_gcda_end_file(void); 76 void __gcov_flush(void); 77 void llvm_gcov_init(write_cb_t, flush_cb_t); 78 79 static flush_cb_t flush_array[NR_MODULES]; 80 static unsigned int flush_count = 0; 81 82 static FILE *gcov_file = NULL; 83 84 /* 85 * LLVM hook for opening the .gcda file for a specific source module. 86 */ 87 void 88 llvm_gcda_start_file(const char * file_name, const char version[4], 89 uint32_t stamp) 90 { 91 uint32_t word[3]; 92 93 assert(gcov_file == NULL); 94 95 gcov_file = _gcov_fopen(file_name, "w+b"); 96 assert(gcov_file != NULL); 97 98 /* 99 * Each _gcov_fwrite() invocation translates into a kernel call, so we 100 * want to aggregate writes as much as possible. 101 */ 102 word[0] = 0x67636461; /* magic: "gcda" */ 103 memcpy(&word[1], version, sizeof(word[1])); /* version */ 104 #if LLVM_35 105 word[2] = stamp; /* stamp */ 106 #else 107 word[2] = 0x4C4C564D; /* stamp: "LLVM" */ 108 #endif 109 110 _gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file); 111 } 112 113 /* 114 * LLVM hook for writing a function announcement to the currently opened .gcda 115 * file. 116 */ 117 void 118 llvm_gcda_emit_function(uint32_t ident, const char * func_name, 119 #if LLVM_35 120 uint32_t func_cksum, uint8_t extra_cksum, uint32_t cfg_cksum) 121 #else 122 uint8_t extra_cksum) 123 #endif 124 { 125 uint32_t word[6]; 126 size_t words, len, wlen; 127 128 word[0] = 0x01000000; /* tag: function */ 129 words = 2; 130 word[2] = ident; /* ident */ 131 #if LLVM_35 132 word[3] = func_cksum; /* function checksum */ 133 #else 134 word[3] = 0; /* function checksum */ 135 #endif 136 if (extra_cksum) { 137 #if LLVM_35 138 word[4] = cfg_cksum; /* configuration checksum */ 139 #else 140 word[4] = 0; /* configuration checksum */ 141 #endif 142 words++; 143 } 144 word[1] = words; /* length */ 145 146 if (func_name != NULL) { 147 len = strlen(func_name) + 1; 148 wlen = len / sizeof(word[0]) + 1; 149 word[1] += 1 + wlen; 150 word[2 + words] = wlen; 151 words++; 152 } 153 154 _gcov_fwrite(word, sizeof(word[0]), 2 + words, gcov_file); 155 156 if (func_name != NULL) { 157 _gcov_fwrite(func_name, 1, len, gcov_file); 158 _gcov_fwrite("\0\0\0\0", 1, wlen * sizeof(uint32_t) - len, 159 gcov_file); 160 } 161 } 162 163 /* 164 * LLVM hook for writing function arc counters to the currently opened .gcda 165 * file. 166 */ 167 void 168 llvm_gcda_emit_arcs(uint32_t ncounters, uint64_t * counters) 169 { 170 uint32_t word[2]; 171 172 assert(gcov_file != NULL); 173 174 word[0] = 0x01a10000; /* tag: arc counters */ 175 word[1] = ncounters * 2; /* length */ 176 177 _gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file); 178 _gcov_fwrite(counters, sizeof(*counters), ncounters, gcov_file); 179 } 180 181 /* 182 * LLVM hook for writing summary information to the currently opened .gcda 183 * file. 184 */ 185 void 186 llvm_gcda_summary_info(void) 187 { 188 uint32_t word[13]; 189 190 memset(word, 0, sizeof(word)); 191 word[0] = 0xa1000000; /* tag: object summary */ 192 word[1] = 9; /* length */ 193 word[2] = 0; /* checksum */ 194 word[3] = 0; /* counter number */ 195 word[4] = 1; /* runs */ 196 word[11] = 0xa3000000; /* tag: program summary */ 197 word[12] = 0; /* length */ 198 199 _gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file); 200 } 201 202 /* 203 * LLVM hook for closing the currently opened .gcda file. 204 */ 205 void 206 llvm_gcda_end_file(void) 207 { 208 uint32_t word[2]; 209 210 assert(gcov_file != NULL); 211 212 word[0] = 0; /* tag: end of file */ 213 word[1] = 0; /* length zero */ 214 _gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file); 215 216 _gcov_fclose(gcov_file); 217 gcov_file = NULL; 218 } 219 220 /* 221 * Our implementation for LLVM of the GCC function to flush the coverage data. 222 * The function is called by our libsys's GCOV code. 223 */ 224 void 225 __gcov_flush(void) 226 { 227 unsigned int i; 228 229 /* Call the flush function for each registered module. */ 230 for (i = 0; i < flush_count; i++) 231 flush_array[i](); 232 } 233 234 /* 235 * LLVM hook for registration of write and flush callbacks. The former is to 236 * be used on exit, the latter on a pre-exit flush. We use the latter only. 237 * This function is basically called once for each compiled source module. 238 */ 239 void 240 llvm_gcov_init(write_cb_t write_cb __unused, flush_cb_t flush_cb) 241 { 242 243 if (flush_cb == NULL) 244 return; 245 246 /* If the array is full, drop this module. */ 247 if (flush_count == __arraycount(flush_array)) 248 return; /* array full, so we are going to miss information */ 249 250 /* Add the flush function to the array. */ 251 flush_array[flush_count++] = flush_cb; 252 253 /* 254 * We print this warning here so that we print it only once. What are 255 * the odds that there are *exactly* NR_MODULES modules anyway? 256 */ 257 if (flush_count == __arraycount(flush_array)) 258 printf("llvm_gcov: process %d has too many modules, " 259 "profiling data lost\n", sef_self()); 260 } 261 262 #endif /*__clang__*/ 263