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