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 "llvm/Support/DataTypes.h" 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <sys/stat.h> 28#include <sys/types.h> 29#ifdef _WIN32 30#include <direct.h> 31#endif 32 33/* #define DEBUG_GCDAPROFILING */ 34 35/* 36 * --- GCOV file format I/O primitives --- 37 */ 38 39static FILE *output_file = NULL; 40 41static void write_int32(uint32_t i) { 42 fwrite(&i, 4, 1, output_file); 43} 44 45static void write_int64(uint64_t i) { 46 uint32_t lo = i >> 0; 47 uint32_t hi = i >> 32; 48 write_int32(lo); 49 write_int32(hi); 50} 51 52static uint32_t length_of_string(const char *s) { 53 return (strlen(s) / 4) + 1; 54} 55 56static void write_string(const char *s) { 57 uint32_t len = length_of_string(s); 58 write_int32(len); 59 fwrite(s, strlen(s), 1, output_file); 60 fwrite("\0\0\0\0", 4 - (strlen(s) % 4), 1, output_file); 61} 62 63static char *mangle_filename(const char *orig_filename) { 64 /* TODO: handle GCOV_PREFIX_STRIP */ 65 const char *prefix; 66 char *filename = 0; 67 68 prefix = getenv("GCOV_PREFIX"); 69 70 if (!prefix) 71 return strdup(orig_filename); 72 73 filename = malloc(strlen(prefix) + 1 + strlen(orig_filename) + 1); 74 strcpy(filename, prefix); 75 strcat(filename, "/"); 76 strcat(filename, orig_filename); 77 return filename; 78} 79 80static void recursive_mkdir(const char *filename) { 81 char *pathname; 82 int i, e; 83 84 for (i = 1, e = strlen(filename); i != e; ++i) { 85 if (filename[i] != '/') continue; 86 pathname = malloc(i + 1); 87 strncpy(pathname, filename, i); 88 pathname[i] = '\0'; 89#ifdef _WIN32 90 _mkdir(pathname); 91#else 92 mkdir(pathname, 0750); /* some of these will fail, ignore it. */ 93#endif 94 free(pathname); 95 } 96} 97 98/* 99 * --- LLVM line counter API --- 100 */ 101 102/* A file in this case is a translation unit. Each .o file built with line 103 * profiling enabled will emit to a different file. Only one file may be 104 * started at a time. 105 */ 106void llvm_gcda_start_file(const char *orig_filename) { 107 char *filename; 108 filename = mangle_filename(orig_filename); 109 recursive_mkdir(filename); 110 output_file = fopen(filename, "w+b"); 111 112 if (!output_file) { 113 const char *cptr = strrchr(orig_filename, '/'); 114 output_file = fopen(cptr ? cptr + 1 : orig_filename, "w+b"); 115 116 if (!output_file) { 117 fprintf(stderr, "LLVM profiling runtime: cannot open '%s': ", 118 cptr ? cptr + 1 : orig_filename); 119 perror(""); 120 free(filename); 121 return; 122 } 123 } 124 125 /* gcda file, version 404*, stamp LLVM. */ 126#ifdef __APPLE__ 127 fwrite("adcg*204MVLL", 12, 1, output_file); 128#else 129 fwrite("adcg*404MVLL", 12, 1, output_file); 130#endif 131 132#ifdef DEBUG_GCDAPROFILING 133 printf("llvmgcda: [%s]\n", orig_filename); 134#endif 135 136 free(filename); 137} 138 139/* Given an array of pointers to counters (counters), increment the n-th one, 140 * where we're also given a pointer to n (predecessor). 141 */ 142void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, 143 uint64_t **counters) { 144 uint64_t *counter; 145 uint32_t pred; 146 147 pred = *predecessor; 148 if (pred == 0xffffffff) 149 return; 150 counter = counters[pred]; 151 152 /* Don't crash if the pred# is out of sync. This can happen due to threads, 153 or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ 154 if (counter) 155 ++*counter; 156#ifdef DEBUG_GCDAPROFILING 157 else 158 printf("llvmgcda: increment_indirect_counter counters=%x, pred=%u\n", 159 state_table_row, *predecessor); 160#endif 161} 162 163void llvm_gcda_emit_function(uint32_t ident, const char *function_name) { 164#ifdef DEBUG_GCDAPROFILING 165 printf("llvmgcda: function id=%x\n", ident); 166#endif 167 if (!output_file) return; 168 169 /* function tag */ 170 fwrite("\0\0\0\1", 4, 1, output_file); 171 write_int32(3 + 1 + length_of_string(function_name)); 172 write_int32(ident); 173 write_int32(0); 174 write_int32(0); 175 write_string(function_name); 176} 177 178void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { 179 uint32_t i; 180 181 /* Counter #1 (arcs) tag */ 182 if (!output_file) return; 183 fwrite("\0\0\xa1\1", 4, 1, output_file); 184 write_int32(num_counters * 2); 185 for (i = 0; i < num_counters; ++i) 186 write_int64(counters[i]); 187 188#ifdef DEBUG_GCDAPROFILING 189 printf("llvmgcda: %u arcs\n", num_counters); 190 for (i = 0; i < num_counters; ++i) 191 printf("llvmgcda: %llu\n", (unsigned long long)counters[i]); 192#endif 193} 194 195void llvm_gcda_end_file() { 196 /* Write out EOF record. */ 197 if (!output_file) return; 198 fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file); 199 fclose(output_file); 200 output_file = NULL; 201 202#ifdef DEBUG_GCDAPROFILING 203 printf("llvmgcda: -----\n"); 204#endif 205} 206