Only read *predecessor once so as to fix a theoretical issue where it changes
[oota-llvm.git] / runtime / libprofile / GCDAProfiling.c
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
28 /* #define DEBUG_GCDAPROFILING */
29
30 /*
31  * --- GCOV file format I/O primitives ---
32  */
33
34 static FILE *output_file = NULL;
35
36 static void write_int32(uint32_t i) {
37   fwrite(&i, 4, 1, output_file);
38 }
39
40 static void write_int64(uint64_t i) {
41   uint32_t lo, hi;
42   lo = i >>  0;
43   hi = i >> 32;
44
45   write_int32(lo);
46   write_int32(hi);
47 }
48
49 static char *mangle_filename(const char *orig_filename) {
50   /* TODO: handle GCOV_PREFIX_STRIP */
51   const char *prefix;
52   char *filename = 0;
53
54   prefix = getenv("GCOV_PREFIX");
55
56   if (!prefix)
57     return strdup(filename);
58
59   filename = malloc(strlen(prefix) + 1 + strlen(orig_filename) + 1);
60   strcpy(filename, prefix);
61   strcat(filename, "/");
62   strcat(filename, orig_filename);
63
64   return filename;
65 }
66
67 /*
68  * --- LLVM line counter API ---
69  */
70
71 /* A file in this case is a translation unit. Each .o file built with line
72  * profiling enabled will emit to a different file. Only one file may be
73  * started at a time.
74  */
75 void llvm_gcda_start_file(const char *orig_filename) {
76   char *filename;
77   filename = mangle_filename(orig_filename);
78   output_file = fopen(filename, "wb");
79
80   /* gcda file, version 404*, stamp LLVM. */
81   fwrite("adcg*404MVLL", 12, 1, output_file);
82
83 #ifdef DEBUG_GCDAPROFILING
84   printf("llvmgcda: [%s]\n", orig_filename);
85 #endif
86
87   free(filename);
88 }
89
90 /* Given an array of pointers to counters (counters), increment the n-th one,
91  * where we're also given a pointer to n (predecessor).
92  */
93 void llvm_gcda_increment_indirect_counter(uint32_t *predecessor,
94                                           uint64_t **counters) {
95   uint64_t *counter;
96   uint32_t pred;
97
98   pred = *predecessor;
99   if (pred == 0xffffffff)
100     return;
101   counter = counters[pred];
102
103   /* Don't crash if the pred# is out of sync. This can happen due to threads,
104      or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */
105   if (counter)
106     ++*counter;
107 #ifdef DEBUG_GCDAPROFILING
108   else
109     printf("llvmgcda: increment_indirect_counter counters=%x, pred=%u\n",
110            state_table_row, *predecessor);
111 #endif
112 }
113
114 void llvm_gcda_emit_function(uint32_t ident) {
115 #ifdef DEBUG_GCDAPROFILING
116   printf("llvmgcda: function id=%x\n", ident);
117 #endif
118
119   /* function tag */  
120   fwrite("\0\0\0\1", 4, 1, output_file);
121   write_int32(2);
122   write_int32(ident);
123   write_int32(0);
124 }
125
126 void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) {
127   uint32_t i;
128   /* counter #1 (arcs) tag */
129   fwrite("\0\0\xa1\1", 4, 1, output_file);
130   write_int32(num_counters * 2);
131   for (i = 0; i < num_counters; ++i) {
132     write_int64(counters[i]);
133   }
134
135 #ifdef DEBUG_GCDAPROFILING
136   printf("llvmgcda:   %u arcs\n", num_counters);
137   for (i = 0; i < num_counters; ++i) {
138     printf("llvmgcda:   %llu\n", (unsigned long long)counters[i]);
139   }
140 #endif
141 }
142
143 void llvm_gcda_end_file() {
144   /* Write out EOF record. */
145   fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file);
146   fclose(output_file);
147   output_file = NULL;
148
149 #ifdef DEBUG_GCDAPROFILING
150   printf("llvmgcda: -----\n");
151 #endif
152 }