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