1 /*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\ 2 |* 3 |* The LLVM Compiler Infrastructure 4 |* 5 |* This file is distributed under the University of Illinois Open Source 6 |* License. See LICENSE.TXT for details. 7 |* 8 |*===----------------------------------------------------------------------===*| 9 |* 10 |* This file implements the call back routines for the gcov profiling 11 |* instrumentation pass. Link against this library when running code through 12 |* the -insert-gcov-profiling LLVM pass. 13 |* 14 |* We emit files in a corrupt version of GCOV's "gcda" file format. These files 15 |* are only close enough that LCOV will happily parse them. Anything that lcov 16 |* ignores is missing. 17 |* 18 |* TODO: gcov is multi-process safe by having each exit open the existing file 19 |* and append to it. We'd like to achieve that and be thread-safe too. 20 |* 21 \*===----------------------------------------------------------------------===*/ 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <sys/stat.h> 27 #include <sys/types.h> 28 #ifdef _WIN32 29 #include <direct.h> 30 #endif 31 32 #ifndef _MSC_VER 33 #include <stdint.h> 34 #else 35 typedef unsigned int uint32_t; 36 typedef unsigned int uint64_t; 37 #endif 38 39 /* #define DEBUG_GCDAPROFILING */ 40 41 /* 42 * --- GCOV file format I/O primitives --- 43 */ 44 45 /* 46 * The current file we're outputting. 47 */ 48 static FILE *output_file = NULL; 49 50 /* 51 * A list of functions to write out the data. 52 */ 53 typedef void (*writeout_fn)(); 54 55 struct writeout_fn_node { 56 writeout_fn fn; 57 struct writeout_fn_node *next; 58 }; 59 60 struct writeout_fn_node *writeout_fn_head = NULL; 61 struct writeout_fn_node *writeout_fn_tail = NULL; 62 63 /* 64 * A list of flush functions that our __gcov_flush() function should call. 65 */ 66 typedef void (*flush_fn)(); 67 68 struct flush_fn_node { 69 flush_fn fn; 70 struct flush_fn_node *next; 71 }; 72 73 struct flush_fn_node *flush_fn_head = NULL; 74 struct flush_fn_node *flush_fn_tail = NULL; 75 76 static void write_int32(uint32_t i) { 77 fwrite(&i, 4, 1, output_file); 78 } 79 80 static uint64_t write_from_buffer(uint64_t *buffer, size_t size) { 81 if (fwrite(buffer, 8, size, output_file) != size) 82 return (uint64_t)-1; 83 84 return 0; 85 } 86 87 static uint32_t length_of_string(const char *s) { 88 return (strlen(s) / 4) + 1; 89 } 90 91 static void write_string(const char *s) { 92 uint32_t len = length_of_string(s); 93 write_int32(len); 94 fwrite(s, strlen(s), 1, output_file); 95 fwrite("\0\0\0\0", 4 - (strlen(s) % 4), 1, output_file); 96 } 97 98 static uint32_t read_int32() { 99 uint32_t tmp; 100 101 if (fread(&tmp, 1, 4, output_file) != 4) 102 return (uint32_t)-1; 103 104 return tmp; 105 } 106 107 static uint64_t read_into_buffer(uint64_t *buffer, size_t size) { 108 if (fread(buffer, 8, size, output_file) != size) 109 return (uint64_t)-1; 110 111 return 0; 112 } 113 114 static char *mangle_filename(const char *orig_filename) { 115 char *filename = 0; 116 int prefix_len = 0; 117 int prefix_strip = 0; 118 int level = 0; 119 const char *fname = orig_filename, *ptr = NULL; 120 const char *prefix = getenv("GCOV_PREFIX"); 121 const char *tmp = getenv("GCOV_PREFIX_STRIP"); 122 123 if (!prefix) 124 return strdup(orig_filename); 125 126 if (tmp) { 127 prefix_strip = atoi(tmp); 128 129 /* Negative GCOV_PREFIX_STRIP values are ignored */ 130 if (prefix_strip < 0) 131 prefix_strip = 0; 132 } 133 134 prefix_len = strlen(prefix); 135 filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1); 136 strcpy(filename, prefix); 137 138 if (prefix[prefix_len - 1] != '/') 139 strcat(filename, "/"); 140 141 for (ptr = fname + 1; *ptr != '\0' && level < prefix_strip; ++ptr) { 142 if (*ptr != '/') continue; 143 fname = ptr; 144 ++level; 145 } 146 147 strcat(filename, fname); 148 149 return filename; 150 } 151 152 static void recursive_mkdir(char *filename) { 153 int i; 154 155 for (i = 1; filename[i] != '\0'; ++i) { 156 if (filename[i] != '/') continue; 157 filename[i] = '\0'; 158 #ifdef _WIN32 159 _mkdir(filename); 160 #else 161 mkdir(filename, 0755); /* Some of these will fail, ignore it. */ 162 #endif 163 filename[i] = '/'; 164 } 165 } 166 167 /* 168 * --- LLVM line counter API --- 169 */ 170 171 /* A file in this case is a translation unit. Each .o file built with line 172 * profiling enabled will emit to a different file. Only one file may be 173 * started at a time. 174 */ 175 void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { 176 char *filename = mangle_filename(orig_filename); 177 178 /* Try just opening the file. */ 179 output_file = fopen(filename, "r+b"); 180 181 if (!output_file) { 182 /* Try opening the file, creating it if necessary. */ 183 output_file = fopen(filename, "w+b"); 184 if (!output_file) { 185 /* Try creating the directories first then opening the file. */ 186 recursive_mkdir(filename); 187 output_file = fopen(filename, "w+b"); 188 if (!output_file) { 189 /* Bah! It's hopeless. */ 190 fprintf(stderr, "profiling:%s: cannot open\n", filename); 191 free(filename); 192 return; 193 } 194 } 195 } 196 197 /* gcda file, version, stamp LLVM. */ 198 fwrite("adcg", 4, 1, output_file); 199 fwrite(version, 4, 1, output_file); 200 fwrite("MVLL", 4, 1, output_file); 201 free(filename); 202 203 #ifdef DEBUG_GCDAPROFILING 204 fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); 205 #endif 206 } 207 208 /* Given an array of pointers to counters (counters), increment the n-th one, 209 * where we're also given a pointer to n (predecessor). 210 */ 211 void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, 212 uint64_t **counters) { 213 uint64_t *counter; 214 uint32_t pred; 215 216 pred = *predecessor; 217 if (pred == 0xffffffff) 218 return; 219 counter = counters[pred]; 220 221 /* Don't crash if the pred# is out of sync. This can happen due to threads, 222 or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ 223 if (counter) 224 ++*counter; 225 #ifdef DEBUG_GCDAPROFILING 226 else 227 fprintf(stderr, 228 "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n", 229 *counter, *predecessor); 230 #endif 231 } 232 233 void llvm_gcda_emit_function(uint32_t ident, const char *function_name, 234 uint8_t use_extra_checksum) { 235 uint32_t len = 2; 236 if (use_extra_checksum) 237 len++; 238 #ifdef DEBUG_GCDAPROFILING 239 fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident, 240 function_name ? function_name : "NULL"); 241 #endif 242 if (!output_file) return; 243 244 /* function tag */ 245 fwrite("\0\0\0\1", 4, 1, output_file); 246 if (function_name) 247 len += 1 + length_of_string(function_name); 248 write_int32(len); 249 write_int32(ident); 250 write_int32(0); 251 if (use_extra_checksum) 252 write_int32(0); 253 if (function_name) 254 write_string(function_name); 255 } 256 257 void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { 258 uint32_t i; 259 uint64_t *old_ctrs = NULL; 260 uint32_t val = 0; 261 long pos = 0; 262 263 if (!output_file) return; 264 265 pos = ftell(output_file); 266 val = read_int32(); 267 268 if (val != (uint32_t)-1) { 269 /* There are counters present in the file. Merge them. */ 270 if (val != 0x01a10000) { 271 fprintf(stderr, "profiling:invalid magic number (0x%08x)\n", val); 272 return; 273 } 274 275 val = read_int32(); 276 if (val == (uint32_t)-1 || val / 2 != num_counters) { 277 fprintf(stderr, "profiling:invalid number of counters (%d)\n", val); 278 return; 279 } 280 281 old_ctrs = malloc(sizeof(uint64_t) * num_counters); 282 283 if (read_into_buffer(old_ctrs, num_counters) == (uint64_t)-1) { 284 fprintf(stderr, "profiling:invalid number of counters (%d)\n", 285 num_counters); 286 return; 287 } 288 } 289 290 /* Reset for writing. */ 291 fseek(output_file, pos, SEEK_SET); 292 293 /* Counter #1 (arcs) tag */ 294 fwrite("\0\0\xa1\1", 4, 1, output_file); 295 write_int32(num_counters * 2); 296 for (i = 0; i < num_counters; ++i) 297 counters[i] += (old_ctrs ? old_ctrs[i] : 0); 298 299 if (write_from_buffer(counters, num_counters) == (uint64_t)-1) { 300 fprintf(stderr, "profiling:cannot write to output file\n"); 301 return; 302 } 303 304 free(old_ctrs); 305 306 #ifdef DEBUG_GCDAPROFILING 307 fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); 308 for (i = 0; i < num_counters; ++i) 309 fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); 310 #endif 311 } 312 313 void llvm_gcda_end_file() { 314 /* Write out EOF record. */ 315 if (!output_file) return; 316 fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file); 317 fclose(output_file); 318 output_file = NULL; 319 320 #ifdef DEBUG_GCDAPROFILING 321 fprintf(stderr, "llvmgcda: -----\n"); 322 #endif 323 } 324 325 void llvm_register_writeout_function(writeout_fn fn) { 326 struct writeout_fn_node *new_node = malloc(sizeof(struct writeout_fn_node)); 327 new_node->fn = fn; 328 new_node->next = NULL; 329 330 if (!writeout_fn_head) { 331 writeout_fn_head = writeout_fn_tail = new_node; 332 } else { 333 writeout_fn_tail->next = new_node; 334 writeout_fn_tail = new_node; 335 } 336 } 337 338 void llvm_writeout_files() { 339 struct writeout_fn_node *curr = writeout_fn_head; 340 341 while (curr) { 342 curr->fn(); 343 curr = curr->next; 344 } 345 } 346 347 void llvm_delete_writeout_function_list() { 348 while (writeout_fn_head) { 349 struct writeout_fn_node *node = writeout_fn_head; 350 writeout_fn_head = writeout_fn_head->next; 351 free(node); 352 } 353 354 writeout_fn_head = writeout_fn_tail = NULL; 355 } 356 357 void llvm_register_flush_function(flush_fn fn) { 358 struct flush_fn_node *new_node = malloc(sizeof(struct flush_fn_node)); 359 new_node->fn = fn; 360 new_node->next = NULL; 361 362 if (!flush_fn_head) { 363 flush_fn_head = flush_fn_tail = new_node; 364 } else { 365 flush_fn_tail->next = new_node; 366 flush_fn_tail = new_node; 367 } 368 } 369 370 void __gcov_flush() { 371 struct flush_fn_node *curr = flush_fn_head; 372 373 while (curr) { 374 curr->fn(); 375 curr = curr->next; 376 } 377 } 378 379 void llvm_delete_flush_function_list() { 380 while (flush_fn_head) { 381 struct flush_fn_node *node = flush_fn_head; 382 flush_fn_head = flush_fn_head->next; 383 free(node); 384 } 385 386 flush_fn_head = flush_fn_tail = NULL; 387 } 388 389 void llvm_gcov_init(writeout_fn wfn, flush_fn ffn) { 390 static int atexit_ran = 0; 391 392 if (wfn) 393 llvm_register_writeout_function(wfn); 394 395 if (ffn) 396 llvm_register_flush_function(ffn); 397 398 if (atexit_ran == 0) { 399 atexit_ran = 1; 400 401 /* Make sure we write out the data and delete the data structures. */ 402 atexit(llvm_delete_flush_function_list); 403 atexit(llvm_delete_writeout_function_list); 404 atexit(llvm_writeout_files); 405 } 406 } 407