GCDAProfiling.c revision 843f359862fb8370eacf8aed4e749c46a92b2e38
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 45static FILE *output_file = NULL; 46 47static void write_int32(uint32_t i) { 48 fwrite(&i, 4, 1, output_file); 49} 50 51static void write_int64(uint64_t i) { 52 uint32_t lo = i >> 0; 53 uint32_t hi = i >> 32; 54 write_int32(lo); 55 write_int32(hi); 56} 57 58static uint32_t length_of_string(const char *s) { 59 return (strlen(s) / 4) + 1; 60} 61 62static void write_string(const char *s) { 63 uint32_t len = length_of_string(s); 64 write_int32(len); 65 fwrite(s, strlen(s), 1, output_file); 66 fwrite("\0\0\0\0", 4 - (strlen(s) % 4), 1, output_file); 67} 68 69static uint32_t read_int32() { 70 uint32_t tmp; 71 72 if (fread(&tmp, 1, 4, output_file) != 4) 73 return (uint32_t)-1; 74 75 return tmp; 76} 77 78static uint64_t read_int64() { 79 uint64_t tmp; 80 81 if (fread(&tmp, 1, 8, output_file) != 8) 82 return (uint64_t)-1; 83 84 return tmp; 85} 86 87static char *mangle_filename(const char *orig_filename) { 88 char *filename = 0; 89 int prefix_len = 0; 90 int prefix_strip = 0; 91 int level = 0; 92 const char *fname = orig_filename, *ptr = NULL; 93 const char *prefix = getenv("GCOV_PREFIX"); 94 const char *tmp = getenv("GCOV_PREFIX_STRIP"); 95 96 if (!prefix) 97 return strdup(orig_filename); 98 99 if (tmp) { 100 prefix_strip = atoi(tmp); 101 102 /* Negative GCOV_PREFIX_STRIP values are ignored */ 103 if (prefix_strip < 0) 104 prefix_strip = 0; 105 } 106 107 prefix_len = strlen(prefix); 108 filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1); 109 strcpy(filename, prefix); 110 111 if (prefix[prefix_len - 1] != '/') 112 strcat(filename, "/"); 113 114 for (ptr = fname + 1; *ptr != '\0' && level < prefix_strip; ++ptr) { 115 if (*ptr != '/') continue; 116 fname = ptr; 117 ++level; 118 } 119 120 strcat(filename, fname); 121 122 return filename; 123} 124 125static void recursive_mkdir(char *filename) { 126 int i; 127 128 for (i = 1; filename[i] != '\0'; ++i) { 129 if (filename[i] != '/') continue; 130 filename[i] = '\0'; 131#ifdef _WIN32 132 _mkdir(filename); 133#else 134 mkdir(filename, 0755); /* Some of these will fail, ignore it. */ 135#endif 136 filename[i] = '/'; 137 } 138} 139 140/* 141 * --- LLVM line counter API --- 142 */ 143 144/* A file in this case is a translation unit. Each .o file built with line 145 * profiling enabled will emit to a different file. Only one file may be 146 * started at a time. 147 */ 148void llvm_gcda_start_file(const char *orig_filename) { 149 char *filename = mangle_filename(orig_filename); 150 char buffer[13]; 151 152 /* Try just opening the file. */ 153 output_file = fopen(filename, "r+b"); 154 155 if (!output_file) { 156 /* Try opening the file, creating it if necessary. */ 157 output_file = fopen(filename, "w+b"); 158 if (!output_file) { 159 /* Try creating the directories first then opening the file. */ 160 recursive_mkdir(filename); 161 output_file = fopen(filename, "w+b"); 162 if (!output_file) { 163 /* Bah! It's hopeless. */ 164 fprintf(stderr, "profiling:%s: cannot open\n", filename); 165 free(filename); 166 return; 167 } 168 } 169 } 170 171 /* gcda file, version 404*, stamp LLVM. */ 172#ifdef __APPLE__ 173 fwrite("adcg*204MVLL", 12, 1, output_file); 174#else 175 fwrite("adcg*404MVLL", 12, 1, output_file); 176#endif 177 178 free(filename); 179 180#ifdef DEBUG_GCDAPROFILING 181 fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); 182#endif 183} 184 185/* Given an array of pointers to counters (counters), increment the n-th one, 186 * where we're also given a pointer to n (predecessor). 187 */ 188void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, 189 uint64_t **counters) { 190 uint64_t *counter; 191 uint32_t pred; 192 193 pred = *predecessor; 194 if (pred == 0xffffffff) 195 return; 196 counter = counters[pred]; 197 198 /* Don't crash if the pred# is out of sync. This can happen due to threads, 199 or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ 200 if (counter) 201 ++*counter; 202#ifdef DEBUG_GCDAPROFILING 203 else 204 fprintf(stderr, 205 "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n", 206 *counter, *predecessor); 207#endif 208} 209 210void llvm_gcda_emit_function(uint32_t ident, const char *function_name) { 211#ifdef DEBUG_GCDAPROFILING 212 fprintf(stderr, "llvmgcda: function id=0x%08x\n", ident); 213#endif 214 if (!output_file) return; 215 216 /* function tag */ 217 fwrite("\0\0\0\1", 4, 1, output_file); 218 write_int32(3 + 1 + length_of_string(function_name)); 219 write_int32(ident); 220 write_int32(0); 221 write_int32(0); 222 write_string(function_name); 223} 224 225void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { 226 uint32_t i; 227 uint64_t *old_ctrs = NULL; 228 uint32_t val = 0; 229 long pos = 0; 230 231 if (!output_file) return; 232 233 pos = ftell(output_file); 234 val = read_int32(); 235 236 fprintf(stderr, "Read: 0x%08x\n", val); 237 238 if (val != (uint32_t)-1) { 239 /* There are counters present in the file. Merge them. */ 240 uint32_t j; 241 242 if (val != 0x01a10000) { 243 fprintf(stderr, "profiling: invalid magic number (0x%08x)\n", val); 244 return; 245 } 246 247 val = read_int32(); 248 if (val == (uint32_t)-1 || val / 2 != num_counters) { 249 fprintf(stderr, "profiling: invalid number of counters (%d)\n", val); 250 return; 251 } 252 253 old_ctrs = malloc(sizeof(uint64_t) * num_counters); 254 255 for (j = 0; j < num_counters; ++j) { 256 old_ctrs[j] = read_int64(); 257 fprintf(stderr, "old counter[%d]: %lld\n", j, old_ctrs[j]); 258 } 259 } 260 261 /* Reset for writing. */ 262 fseek(output_file, pos, SEEK_SET); 263 264 /* Counter #1 (arcs) tag */ 265 fwrite("\0\0\xa1\1", 4, 1, output_file); 266 write_int32(num_counters * 2); 267 for (i = 0; i < num_counters; ++i) 268 write_int64(counters[i] + (old_ctrs ? old_ctrs[i] : 0)); 269 270 free(old_ctrs); 271 272#ifdef DEBUG_GCDAPROFILING 273 fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); 274 for (i = 0; i < num_counters; ++i) 275 fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); 276#endif 277} 278 279void llvm_gcda_end_file() { 280 /* Write out EOF record. */ 281 if (!output_file) return; 282 fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file); 283 fclose(output_file); 284 output_file = NULL; 285 286#ifdef DEBUG_GCDAPROFILING 287 fprintf(stderr, "llvmgcda: -----\n"); 288#endif 289} 290