GCDAProfiling.c revision c533304a95fbed444ea02f5f9e7c6e37debd1c43
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 35typedef unsigned int uint32_t; 36typedef 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 */ 48static FILE *output_file = NULL; 49 50/* 51 * A list of functions to write out the data. 52 */ 53typedef void (*writeout_fn)(); 54 55struct writeout_fn_node { 56 writeout_fn fn; 57 struct writeout_fn_node *next; 58}; 59 60struct writeout_fn_node *writeout_fn_head = NULL; 61struct writeout_fn_node *writeout_fn_tail = NULL; 62 63/* 64 * A list of flush functions that our __gcov_flush() function should call. 65 */ 66typedef void (*flush_fn)(); 67 68struct flush_fn_node { 69 flush_fn fn; 70 struct flush_fn_node *next; 71}; 72 73struct flush_fn_node *flush_fn_head = NULL; 74struct flush_fn_node *flush_fn_tail = NULL; 75 76static void write_int32(uint32_t i) { 77 fwrite(&i, 4, 1, output_file); 78} 79 80static void write_int64(uint64_t i) { 81 uint32_t lo = i >> 0; 82 uint32_t hi = i >> 32; 83 write_int32(lo); 84 write_int32(hi); 85} 86 87static uint32_t length_of_string(const char *s) { 88 return (strlen(s) / 4) + 1; 89} 90 91static 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 98static 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 107static uint64_t read_int64() { 108 uint64_t tmp; 109 110 if (fread(&tmp, 1, 8, output_file) != 8) 111 return (uint64_t)-1; 112 113 return tmp; 114} 115 116static char *mangle_filename(const char *orig_filename) { 117 char *filename = 0; 118 int prefix_len = 0; 119 int prefix_strip = 0; 120 int level = 0; 121 const char *fname = orig_filename, *ptr = NULL; 122 const char *prefix = getenv("GCOV_PREFIX"); 123 const char *tmp = getenv("GCOV_PREFIX_STRIP"); 124 125 if (!prefix) 126 return strdup(orig_filename); 127 128 if (tmp) { 129 prefix_strip = atoi(tmp); 130 131 /* Negative GCOV_PREFIX_STRIP values are ignored */ 132 if (prefix_strip < 0) 133 prefix_strip = 0; 134 } 135 136 prefix_len = strlen(prefix); 137 filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1); 138 strcpy(filename, prefix); 139 140 if (prefix[prefix_len - 1] != '/') 141 strcat(filename, "/"); 142 143 for (ptr = fname + 1; *ptr != '\0' && level < prefix_strip; ++ptr) { 144 if (*ptr != '/') continue; 145 fname = ptr; 146 ++level; 147 } 148 149 strcat(filename, fname); 150 151 return filename; 152} 153 154static void recursive_mkdir(char *filename) { 155 int i; 156 157 for (i = 1; filename[i] != '\0'; ++i) { 158 if (filename[i] != '/') continue; 159 filename[i] = '\0'; 160#ifdef _WIN32 161 _mkdir(filename); 162#else 163 mkdir(filename, 0755); /* Some of these will fail, ignore it. */ 164#endif 165 filename[i] = '/'; 166 } 167} 168 169/* 170 * --- LLVM line counter API --- 171 */ 172 173/* A file in this case is a translation unit. Each .o file built with line 174 * profiling enabled will emit to a different file. Only one file may be 175 * started at a time. 176 */ 177void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { 178 char *filename = mangle_filename(orig_filename); 179 180 /* Try just opening the file. */ 181 output_file = fopen(filename, "r+b"); 182 183 if (!output_file) { 184 /* Try opening the file, creating it if necessary. */ 185 output_file = fopen(filename, "w+b"); 186 if (!output_file) { 187 /* Try creating the directories first then opening the file. */ 188 recursive_mkdir(filename); 189 output_file = fopen(filename, "w+b"); 190 if (!output_file) { 191 /* Bah! It's hopeless. */ 192 fprintf(stderr, "profiling:%s: cannot open\n", filename); 193 free(filename); 194 return; 195 } 196 } 197 } 198 199 /* gcda file, version, stamp LLVM. */ 200 fwrite("adcg", 4, 1, output_file); 201 fwrite(version, 4, 1, output_file); 202 fwrite("MVLL", 4, 1, output_file); 203 free(filename); 204 205#ifdef DEBUG_GCDAPROFILING 206 fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); 207#endif 208} 209 210/* Given an array of pointers to counters (counters), increment the n-th one, 211 * where we're also given a pointer to n (predecessor). 212 */ 213void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, 214 uint64_t **counters) { 215 uint64_t *counter; 216 uint32_t pred; 217 218 pred = *predecessor; 219 if (pred == 0xffffffff) 220 return; 221 counter = counters[pred]; 222 223 /* Don't crash if the pred# is out of sync. This can happen due to threads, 224 or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ 225 if (counter) 226 ++*counter; 227#ifdef DEBUG_GCDAPROFILING 228 else 229 fprintf(stderr, 230 "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n", 231 *counter, *predecessor); 232#endif 233} 234 235void llvm_gcda_emit_function(uint32_t ident, const char *function_name, 236 uint8_t use_extra_checksum) { 237 uint32_t len = 2; 238 if (use_extra_checksum) 239 len++; 240#ifdef DEBUG_GCDAPROFILING 241 fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident, 242 function_name ? function_name : "NULL"); 243#endif 244 if (!output_file) return; 245 246 /* function tag */ 247 fwrite("\0\0\0\1", 4, 1, output_file); 248 if (function_name) 249 len += 1 + length_of_string(function_name); 250 write_int32(len); 251 write_int32(ident); 252 write_int32(0); 253 if (use_extra_checksum) 254 write_int32(0); 255 if (function_name) 256 write_string(function_name); 257} 258 259void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { 260 uint32_t i; 261 uint64_t *old_ctrs = NULL; 262 uint32_t val = 0; 263 long pos = 0; 264 265 if (!output_file) return; 266 267 pos = ftell(output_file); 268 val = read_int32(); 269 270 if (val != (uint32_t)-1) { 271 /* There are counters present in the file. Merge them. */ 272 uint32_t j; 273 274 if (val != 0x01a10000) { 275 fprintf(stderr, "profiling: invalid magic number (0x%08x)\n", val); 276 return; 277 } 278 279 val = read_int32(); 280 if (val == (uint32_t)-1 || val / 2 != num_counters) { 281 fprintf(stderr, "profiling: invalid number of counters (%d)\n", val); 282 return; 283 } 284 285 old_ctrs = malloc(sizeof(uint64_t) * num_counters); 286 287 for (j = 0; j < num_counters; ++j) 288 old_ctrs[j] = read_int64(); 289 } 290 291 /* Reset for writing. */ 292 fseek(output_file, pos, SEEK_SET); 293 294 /* Counter #1 (arcs) tag */ 295 fwrite("\0\0\xa1\1", 4, 1, output_file); 296 write_int32(num_counters * 2); 297 for (i = 0; i < num_counters; ++i) 298 write_int64(counters[i] + (old_ctrs ? old_ctrs[i] : 0)); 299 300 free(old_ctrs); 301 302#ifdef DEBUG_GCDAPROFILING 303 fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); 304 for (i = 0; i < num_counters; ++i) 305 fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); 306#endif 307} 308 309void llvm_gcda_end_file() { 310 /* Write out EOF record. */ 311 if (!output_file) return; 312 fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file); 313 fclose(output_file); 314 output_file = NULL; 315 316#ifdef DEBUG_GCDAPROFILING 317 fprintf(stderr, "llvmgcda: -----\n"); 318#endif 319} 320 321void llvm_register_writeout_function(writeout_fn fn) { 322 struct writeout_fn_node *new_node = malloc(sizeof(struct writeout_fn_node)); 323 new_node->fn = fn; 324 new_node->next = NULL; 325 326 if (!writeout_fn_head) { 327 writeout_fn_head = writeout_fn_tail = new_node; 328 } else { 329 writeout_fn_tail->next = new_node; 330 writeout_fn_tail = new_node; 331 } 332} 333 334void __llvm_writeout_files() { 335 struct writeout_fn_node *curr = writeout_fn_head; 336 337 while (curr) { 338 curr->fn(); 339 curr = curr->next; 340 } 341} 342 343void llvm_delete_writeout_function_list() { 344 while (writeout_fn_head) { 345 struct writeout_fn_node *node = writeout_fn_head; 346 writeout_fn_head = writeout_fn_head->next; 347 free(node); 348 } 349 350 writeout_fn_head = writeout_fn_tail = NULL; 351} 352 353void llvm_register_flush_function(flush_fn fn) { 354 struct flush_fn_node *new_node = malloc(sizeof(struct flush_fn_node)); 355 new_node->fn = fn; 356 new_node->next = NULL; 357 358 if (!flush_fn_head) { 359 flush_fn_head = flush_fn_tail = new_node; 360 } else { 361 flush_fn_tail->next = new_node; 362 flush_fn_tail = new_node; 363 } 364} 365 366void __gcov_flush() { 367 struct flush_fn_node *curr = flush_fn_head; 368 369 while (curr) { 370 curr->fn(); 371 curr = curr->next; 372 } 373} 374 375void llvm_delete_flush_function_list() { 376 while (flush_fn_head) { 377 struct flush_fn_node *node = flush_fn_head; 378 flush_fn_head = flush_fn_head->next; 379 free(node); 380 } 381 382 flush_fn_head = flush_fn_tail = NULL; 383} 384