1 /* LLVM-to-GCOV converter by D.C. van Moolenbroek */
3 * Originally, we had a GCOV code coverage implementation for GCC only. We
4 * have now largely switched to LLVM, and LLVM uses a different internal
5 * implementation of the coverage data generation. For regular userland
6 * programs, the implementation is part of LLVM compiler-rt's libprofile_rt.
7 * That implementation is unsuitable for our system services. Instead, this
8 * file converts the calls used by LLVM into _gcov_f*() calls expected by our
9 * GCOV-for-GCC implementation, thus adding support for LLVM coverage by
10 * leveraging our previous GCC support.
17 #include <minix/syslib.h>
18 #include <minix/sysutil.h>
19 #include <minix/gcov.h>
23 * What is the maximum number of source modules for one single system service?
24 * This number is currently way higher than needed, but if we ever add support
25 * for coverage of system service libraries (e.g., libsys and libminc), this
26 * number may not even be high enough. A warning is printed on overflow.
27 * Note that we need this to be a static array, because we cannot use malloc()
28 * in particular in the initialization stage of the VM service.
30 #define NR_MODULES 256
33 * The code in this file is a MINIX3 service specific replacement of the
34 * GCDAProfiling.c code in LLVM's compiler-rt. Their code cannot be used
35 * directly because they assume a userland environment, using all sorts of
36 * POSIX calls as well as malloc(3), none of which we can offer for system
37 * services in this case. So, we provide our own implementation instead.
38 * However, while compiler-rt is always kept in sync with the LLVM profiling
39 * data emitter, we do not have that luxury. The current version of this
40 * implementation has been written for LLVM 3.4 and 3.6, between which the LLVM
41 * GCOV ABI changed. Our current implementation supports both versions, but
42 * may break with newer LLVM versions, even though we should be good up to and
43 * possibly including LLVM 4.0 at least. Hopefully, at this point the LLVM
44 * GCOV ABI should have stabilized a bit.
46 * Note that since we do not have access to internal LLVM headers here, an ABI
47 * mismatch would not be noticable until llvm-cov fails to load the resulting
48 * files. This whole mess is worth it only because we can really, really use
49 * the coverage information for our test sets..
51 #if __clang_major__ == 3 && __clang_minor__ == 4
52 #define LLVM_35 0 /* version 3.4 only */
53 #elif __clang_major__ == 3 && __clang_minor__ >= 5
54 #define LLVM_35 1 /* version 3.5 and later */
56 #error "unknown LLVM/clang version, manual inspection required"
59 typedef void (*write_cb_t
)(void);
60 typedef void (*flush_cb_t
)(void);
63 * Except for llvm_gcda_emit_function(), these functions are already declared
64 * in the 3.5+ ABI style. With the 3.4 ABI, some parameters may have garbage.
66 void llvm_gcda_start_file(const char *, const char *, uint32_t);
67 void llvm_gcda_emit_function(uint32_t, const char *,
69 uint32_t, uint8_t, uint32_t);
73 void llvm_gcda_emit_arcs(uint32_t, uint64_t *);
74 void llvm_gcda_summary_info(void);
75 void llvm_gcda_end_file(void);
76 void __gcov_flush(void);
77 void llvm_gcov_init(write_cb_t
, flush_cb_t
);
79 static flush_cb_t flush_array
[NR_MODULES
];
80 static unsigned int flush_count
= 0;
82 static FILE *gcov_file
= NULL
;
85 * LLVM hook for opening the .gcda file for a specific source module.
88 llvm_gcda_start_file(const char * file_name
, const char version
[4],
93 assert(gcov_file
== NULL
);
95 gcov_file
= _gcov_fopen(file_name
, "w+b");
96 assert(gcov_file
!= NULL
);
99 * Each _gcov_fwrite() invocation translates into a kernel call, so we
100 * want to aggregate writes as much as possible.
102 word
[0] = 0x67636461; /* magic: "gcda" */
103 memcpy(&word
[1], version
, sizeof(word
[1])); /* version */
105 word
[2] = stamp
; /* stamp */
107 word
[2] = 0x4C4C564D; /* stamp: "LLVM" */
110 _gcov_fwrite(word
, sizeof(word
[0]), __arraycount(word
), gcov_file
);
114 * LLVM hook for writing a function announcement to the currently opened .gcda
118 llvm_gcda_emit_function(uint32_t ident
, const char * func_name
,
120 uint32_t func_cksum
, uint8_t extra_cksum
, uint32_t cfg_cksum
)
126 size_t words
, len
, wlen
;
128 word
[0] = 0x01000000; /* tag: function */
130 word
[2] = ident
; /* ident */
132 word
[3] = func_cksum
; /* function checksum */
134 word
[3] = 0; /* function checksum */
138 word
[4] = cfg_cksum
; /* configuration checksum */
140 word
[4] = 0; /* configuration checksum */
144 word
[1] = words
; /* length */
146 if (func_name
!= NULL
) {
147 len
= strlen(func_name
) + 1;
148 wlen
= len
/ sizeof(word
[0]) + 1;
150 word
[2 + words
] = wlen
;
154 _gcov_fwrite(word
, sizeof(word
[0]), 2 + words
, gcov_file
);
156 if (func_name
!= NULL
) {
157 _gcov_fwrite(func_name
, 1, len
, gcov_file
);
158 _gcov_fwrite("\0\0\0\0", 1, wlen
* sizeof(uint32_t) - len
,
164 * LLVM hook for writing function arc counters to the currently opened .gcda
168 llvm_gcda_emit_arcs(uint32_t ncounters
, uint64_t * counters
)
172 assert(gcov_file
!= NULL
);
174 word
[0] = 0x01a10000; /* tag: arc counters */
175 word
[1] = ncounters
* 2; /* length */
177 _gcov_fwrite(word
, sizeof(word
[0]), __arraycount(word
), gcov_file
);
178 _gcov_fwrite(counters
, sizeof(*counters
), ncounters
, gcov_file
);
182 * LLVM hook for writing summary information to the currently opened .gcda
186 llvm_gcda_summary_info(void)
190 memset(word
, 0, sizeof(word
));
191 word
[0] = 0xa1000000; /* tag: object summary */
192 word
[1] = 9; /* length */
193 word
[2] = 0; /* checksum */
194 word
[3] = 0; /* counter number */
195 word
[4] = 1; /* runs */
196 word
[11] = 0xa3000000; /* tag: program summary */
197 word
[12] = 0; /* length */
199 _gcov_fwrite(word
, sizeof(word
[0]), __arraycount(word
), gcov_file
);
203 * LLVM hook for closing the currently opened .gcda file.
206 llvm_gcda_end_file(void)
210 assert(gcov_file
!= NULL
);
212 word
[0] = 0; /* tag: end of file */
213 word
[1] = 0; /* length zero */
214 _gcov_fwrite(word
, sizeof(word
[0]), __arraycount(word
), gcov_file
);
216 _gcov_fclose(gcov_file
);
221 * Our implementation for LLVM of the GCC function to flush the coverage data.
222 * The function is called by our libsys's GCOV code.
229 /* Call the flush function for each registered module. */
230 for (i
= 0; i
< flush_count
; i
++)
235 * LLVM hook for registration of write and flush callbacks. The former is to
236 * be used on exit, the latter on a pre-exit flush. We use the latter only.
237 * This function is basically called once for each compiled source module.
240 llvm_gcov_init(write_cb_t write_cb __unused
, flush_cb_t flush_cb
)
243 if (flush_cb
== NULL
)
246 /* If the array is full, drop this module. */
247 if (flush_count
== __arraycount(flush_array
))
248 return; /* array full, so we are going to miss information */
250 /* Add the flush function to the array. */
251 flush_array
[flush_count
++] = flush_cb
;
254 * We print this warning here so that we print it only once. What are
255 * the odds that there are *exactly* NR_MODULES modules anyway?
257 if (flush_count
== __arraycount(flush_array
))
258 printf("llvm_gcov: process %d has too many modules, "
259 "profiling data lost\n", sef_self());