1 /*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\
3 |* The LLVM Compiler Infrastructure
5 |* This file is distributed under the University of Illinois Open Source
6 |* License. See LICENSE.TXT for details.
8 |*===----------------------------------------------------------------------===*|
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.
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.
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.
21 \*===----------------------------------------------------------------------===*/
27 #include <sys/types.h>
35 typedef unsigned int uint32_t;
36 typedef unsigned int uint64_t;
39 /* #define DEBUG_GCDAPROFILING */
42 * --- GCOV file format I/O primitives ---
45 static FILE *output_file
= NULL
;
47 static void write_int32(uint32_t i
) {
48 fwrite(&i
, 4, 1, output_file
);
51 static void write_int64(uint64_t i
) {
53 uint32_t hi
= i
>> 32;
58 static uint32_t length_of_string(const char *s
) {
59 return (strlen(s
) / 4) + 1;
62 static void write_string(const char *s
) {
63 uint32_t len
= length_of_string(s
);
65 fwrite(s
, strlen(s
), 1, output_file
);
66 fwrite("\0\0\0\0", 4 - (strlen(s
) % 4), 1, output_file
);
69 static uint32_t read_int32() {
72 if (fread(&tmp
, 1, 4, output_file
) != 4)
78 static uint64_t read_int64() {
81 if (fread(&tmp
, 1, 8, output_file
) != 8)
87 static char *mangle_filename(const char *orig_filename
) {
92 const char *fname
= orig_filename
, *ptr
= NULL
;
93 const char *prefix
= getenv("GCOV_PREFIX");
94 const char *tmp
= getenv("GCOV_PREFIX_STRIP");
97 return strdup(orig_filename
);
100 prefix_strip
= atoi(tmp
);
102 /* Negative GCOV_PREFIX_STRIP values are ignored */
103 if (prefix_strip
< 0)
107 prefix_len
= strlen(prefix
);
108 filename
= malloc(prefix_len
+ 1 + strlen(orig_filename
) + 1);
109 strcpy(filename
, prefix
);
111 if (prefix
[prefix_len
- 1] != '/')
112 strcat(filename
, "/");
114 for (ptr
= fname
+ 1; *ptr
!= '\0' && level
< prefix_strip
; ++ptr
) {
115 if (*ptr
!= '/') continue;
120 strcat(filename
, fname
);
125 static void recursive_mkdir(char *filename
) {
128 for (i
= 1; filename
[i
] != '\0'; ++i
) {
129 if (filename
[i
] != '/') continue;
134 mkdir(filename
, 0755); /* Some of these will fail, ignore it. */
141 * --- LLVM line counter API ---
144 /* A file in this case is a translation unit. Each .o file built with line
145 * profiling enabled will emit to a different file. Only one file may be
148 void llvm_gcda_start_file(const char *orig_filename
) {
149 char *filename
= mangle_filename(orig_filename
);
151 /* Try just opening the file. */
152 output_file
= fopen(filename
, "r+b");
155 /* Try opening the file, creating it if necessary. */
156 output_file
= fopen(filename
, "w+b");
158 /* Try creating the directories first then opening the file. */
159 recursive_mkdir(filename
);
160 output_file
= fopen(filename
, "w+b");
162 /* Bah! It's hopeless. */
163 fprintf(stderr
, "profiling:%s: cannot open\n", filename
);
170 /* gcda file, version 404*, stamp LLVM. */
172 fwrite("adcg*204MVLL", 12, 1, output_file
);
174 fwrite("adcg*404MVLL", 12, 1, output_file
);
179 #ifdef DEBUG_GCDAPROFILING
180 fprintf(stderr
, "llvmgcda: [%s]\n", orig_filename
);
184 /* Given an array of pointers to counters (counters), increment the n-th one,
185 * where we're also given a pointer to n (predecessor).
187 void llvm_gcda_increment_indirect_counter(uint32_t *predecessor
,
188 uint64_t **counters
) {
193 if (pred
== 0xffffffff)
195 counter
= counters
[pred
];
197 /* Don't crash if the pred# is out of sync. This can happen due to threads,
198 or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */
201 #ifdef DEBUG_GCDAPROFILING
204 "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n",
205 *counter
, *predecessor
);
209 void llvm_gcda_emit_function(uint32_t ident
, const char *function_name
) {
210 #ifdef DEBUG_GCDAPROFILING
211 fprintf(stderr
, "llvmgcda: function id=0x%08x\n", ident
);
213 if (!output_file
) return;
216 fwrite("\0\0\0\1", 4, 1, output_file
);
217 write_int32(3 + 1 + length_of_string(function_name
));
221 write_string(function_name
);
224 void llvm_gcda_emit_arcs(uint32_t num_counters
, uint64_t *counters
) {
226 uint64_t *old_ctrs
= NULL
;
230 if (!output_file
) return;
232 pos
= ftell(output_file
);
235 if (val
!= (uint32_t)-1) {
236 /* There are counters present in the file. Merge them. */
239 if (val
!= 0x01a10000) {
240 fprintf(stderr
, "profiling: invalid magic number (0x%08x)\n", val
);
245 if (val
== (uint32_t)-1 || val
/ 2 != num_counters
) {
246 fprintf(stderr
, "profiling: invalid number of counters (%d)\n", val
);
250 old_ctrs
= malloc(sizeof(uint64_t) * num_counters
);
252 for (j
= 0; j
< num_counters
; ++j
)
253 old_ctrs
[j
] = read_int64();
256 /* Reset for writing. */
257 fseek(output_file
, pos
, SEEK_SET
);
259 /* Counter #1 (arcs) tag */
260 fwrite("\0\0\xa1\1", 4, 1, output_file
);
261 write_int32(num_counters
* 2);
262 for (i
= 0; i
< num_counters
; ++i
)
263 write_int64(counters
[i
] + (old_ctrs
? old_ctrs
[i
] : 0));
267 #ifdef DEBUG_GCDAPROFILING
268 fprintf(stderr
, "llvmgcda: %u arcs\n", num_counters
);
269 for (i
= 0; i
< num_counters
; ++i
)
270 fprintf(stderr
, "llvmgcda: %llu\n", (unsigned long long)counters
[i
]);
274 void llvm_gcda_end_file() {
275 /* Write out EOF record. */
276 if (!output_file
) return;
277 fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file
);
281 #ifdef DEBUG_GCDAPROFILING
282 fprintf(stderr
, "llvmgcda: -----\n");