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