xref: /minix3/minix/lib/libsys/llvm_gcov.c (revision 3ac58492b3d3709bad0ae9c60a137f63a90960b7)
1*3ac58492SDavid van Moolenbroek /* LLVM-to-GCOV converter by D.C. van Moolenbroek */
2*3ac58492SDavid van Moolenbroek /*
3*3ac58492SDavid van Moolenbroek  * Originally, we had a GCOV code coverage implementation for GCC only.  We
4*3ac58492SDavid van Moolenbroek  * have now largely switched to LLVM, and LLVM uses a different internal
5*3ac58492SDavid van Moolenbroek  * implementation of the coverage data generation.  For regular userland
6*3ac58492SDavid van Moolenbroek  * programs, the implementation is part of LLVM compiler-rt's libprofile_rt.
7*3ac58492SDavid van Moolenbroek  * That implementation is unsuitable for our system services.  Instead, this
8*3ac58492SDavid van Moolenbroek  * file converts the calls used by LLVM into _gcov_f*() calls expected by our
9*3ac58492SDavid van Moolenbroek  * GCOV-for-GCC implementation, thus adding support for LLVM coverage by
10*3ac58492SDavid van Moolenbroek  * leveraging our previous GCC support.
11*3ac58492SDavid van Moolenbroek  */
12*3ac58492SDavid van Moolenbroek 
13*3ac58492SDavid van Moolenbroek #if __clang__
14*3ac58492SDavid van Moolenbroek 
15*3ac58492SDavid van Moolenbroek #include <stdlib.h>
16*3ac58492SDavid van Moolenbroek #include <string.h>
17*3ac58492SDavid van Moolenbroek #include <minix/syslib.h>
18*3ac58492SDavid van Moolenbroek #include <minix/sysutil.h>
19*3ac58492SDavid van Moolenbroek #include <minix/gcov.h>
20*3ac58492SDavid van Moolenbroek #include <assert.h>
21*3ac58492SDavid van Moolenbroek 
22*3ac58492SDavid van Moolenbroek /*
23*3ac58492SDavid van Moolenbroek  * What is the maximum number of source modules for one single system service?
24*3ac58492SDavid van Moolenbroek  * This number is currently way higher than needed, but if we ever add support
25*3ac58492SDavid van Moolenbroek  * for coverage of system service libraries (e.g., libsys and libminc), this
26*3ac58492SDavid van Moolenbroek  * number may not even be high enough.  A warning is printed on overflow.
27*3ac58492SDavid van Moolenbroek  * Note that we need this to be a static array, because we cannot use malloc()
28*3ac58492SDavid van Moolenbroek  * in particular in the initialization stage of the VM service.
29*3ac58492SDavid van Moolenbroek  */
30*3ac58492SDavid van Moolenbroek #define NR_MODULES 256
31*3ac58492SDavid van Moolenbroek 
32*3ac58492SDavid van Moolenbroek /*
33*3ac58492SDavid van Moolenbroek  * The code in this file is a MINIX3 service specific replacement of the
34*3ac58492SDavid van Moolenbroek  * GCDAProfiling.c code in LLVM's compiler-rt.  Their code cannot be used
35*3ac58492SDavid van Moolenbroek  * directly because they assume a userland environment, using all sorts of
36*3ac58492SDavid van Moolenbroek  * POSIX calls as well as malloc(3), none of which we can offer for system
37*3ac58492SDavid van Moolenbroek  * services in this case.  So, we provide our own implementation instead.
38*3ac58492SDavid van Moolenbroek  * However, while compiler-rt is always kept in sync with the LLVM profiling
39*3ac58492SDavid van Moolenbroek  * data emitter, we do not have that luxury.  The current version of this
40*3ac58492SDavid van Moolenbroek  * implementation has been written for LLVM 3.4 and 3.6, between which the LLVM
41*3ac58492SDavid van Moolenbroek  * GCOV ABI changed.  Our current implementation supports both versions, but
42*3ac58492SDavid van Moolenbroek  * may break with newer LLVM versions, even though we should be good up to and
43*3ac58492SDavid van Moolenbroek  * possibly including LLVM 4.0 at least.  Hopefully, at this point the LLVM
44*3ac58492SDavid van Moolenbroek  * GCOV ABI should have stabilized a bit.
45*3ac58492SDavid van Moolenbroek  *
46*3ac58492SDavid van Moolenbroek  * Note that since we do not have access to internal LLVM headers here, an ABI
47*3ac58492SDavid van Moolenbroek  * mismatch would not be noticable until llvm-cov fails to load the resulting
48*3ac58492SDavid van Moolenbroek  * files.  This whole mess is worth it only because we can really, really use
49*3ac58492SDavid van Moolenbroek  * the coverage information for our test sets..
50*3ac58492SDavid van Moolenbroek  */
51*3ac58492SDavid van Moolenbroek #if __clang_major__ == 3 && __clang_minor__ == 4
52*3ac58492SDavid van Moolenbroek #define LLVM_35	0	/* version 3.4 only */
53*3ac58492SDavid van Moolenbroek #elif __clang_major__ == 3 && __clang_minor__ >= 5
54*3ac58492SDavid van Moolenbroek #define LLVM_35	1	/* version 3.5 and later */
55*3ac58492SDavid van Moolenbroek #else
56*3ac58492SDavid van Moolenbroek #error "unknown LLVM/clang version, manual inspection required"
57*3ac58492SDavid van Moolenbroek #endif
58*3ac58492SDavid van Moolenbroek 
59*3ac58492SDavid van Moolenbroek typedef void (*write_cb_t)(void);
60*3ac58492SDavid van Moolenbroek typedef void (*flush_cb_t)(void);
61*3ac58492SDavid van Moolenbroek 
62*3ac58492SDavid van Moolenbroek /*
63*3ac58492SDavid van Moolenbroek  * Except for llvm_gcda_emit_function(), these functions are already declared
64*3ac58492SDavid van Moolenbroek  * in the 3.5+ ABI style.  With the 3.4 ABI, some parameters may have garbage.
65*3ac58492SDavid van Moolenbroek  */
66*3ac58492SDavid van Moolenbroek void llvm_gcda_start_file(const char *, const char *, uint32_t);
67*3ac58492SDavid van Moolenbroek void llvm_gcda_emit_function(uint32_t, const char *,
68*3ac58492SDavid van Moolenbroek #if LLVM_35
69*3ac58492SDavid van Moolenbroek 	uint32_t, uint8_t, uint32_t);
70*3ac58492SDavid van Moolenbroek #else
71*3ac58492SDavid van Moolenbroek 	uint8_t);
72*3ac58492SDavid van Moolenbroek #endif
73*3ac58492SDavid van Moolenbroek void llvm_gcda_emit_arcs(uint32_t, uint64_t *);
74*3ac58492SDavid van Moolenbroek void llvm_gcda_summary_info(void);
75*3ac58492SDavid van Moolenbroek void llvm_gcda_end_file(void);
76*3ac58492SDavid van Moolenbroek void __gcov_flush(void);
77*3ac58492SDavid van Moolenbroek void llvm_gcov_init(write_cb_t, flush_cb_t);
78*3ac58492SDavid van Moolenbroek 
79*3ac58492SDavid van Moolenbroek static flush_cb_t flush_array[NR_MODULES];
80*3ac58492SDavid van Moolenbroek static unsigned int flush_count = 0;
81*3ac58492SDavid van Moolenbroek 
82*3ac58492SDavid van Moolenbroek static FILE *gcov_file = NULL;
83*3ac58492SDavid van Moolenbroek 
84*3ac58492SDavid van Moolenbroek /*
85*3ac58492SDavid van Moolenbroek  * LLVM hook for opening the .gcda file for a specific source module.
86*3ac58492SDavid van Moolenbroek  */
87*3ac58492SDavid van Moolenbroek void
llvm_gcda_start_file(const char * file_name,const char version[4],uint32_t stamp)88*3ac58492SDavid van Moolenbroek llvm_gcda_start_file(const char * file_name, const char version[4],
89*3ac58492SDavid van Moolenbroek 	uint32_t stamp)
90*3ac58492SDavid van Moolenbroek {
91*3ac58492SDavid van Moolenbroek 	uint32_t word[3];
92*3ac58492SDavid van Moolenbroek 
93*3ac58492SDavid van Moolenbroek 	assert(gcov_file == NULL);
94*3ac58492SDavid van Moolenbroek 
95*3ac58492SDavid van Moolenbroek 	gcov_file = _gcov_fopen(file_name, "w+b");
96*3ac58492SDavid van Moolenbroek 	assert(gcov_file != NULL);
97*3ac58492SDavid van Moolenbroek 
98*3ac58492SDavid van Moolenbroek 	/*
99*3ac58492SDavid van Moolenbroek 	 * Each _gcov_fwrite() invocation translates into a kernel call, so we
100*3ac58492SDavid van Moolenbroek 	 * want to aggregate writes as much as possible.
101*3ac58492SDavid van Moolenbroek 	 */
102*3ac58492SDavid van Moolenbroek 	word[0] = 0x67636461;				/* magic: "gcda" */
103*3ac58492SDavid van Moolenbroek 	memcpy(&word[1], version, sizeof(word[1]));	/* version */
104*3ac58492SDavid van Moolenbroek #if LLVM_35
105*3ac58492SDavid van Moolenbroek 	word[2] = stamp;				/* stamp */
106*3ac58492SDavid van Moolenbroek #else
107*3ac58492SDavid van Moolenbroek 	word[2] = 0x4C4C564D;				/* stamp: "LLVM" */
108*3ac58492SDavid van Moolenbroek #endif
109*3ac58492SDavid van Moolenbroek 
110*3ac58492SDavid van Moolenbroek 	_gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file);
111*3ac58492SDavid van Moolenbroek }
112*3ac58492SDavid van Moolenbroek 
113*3ac58492SDavid van Moolenbroek /*
114*3ac58492SDavid van Moolenbroek  * LLVM hook for writing a function announcement to the currently opened .gcda
115*3ac58492SDavid van Moolenbroek  * file.
116*3ac58492SDavid van Moolenbroek  */
117*3ac58492SDavid van Moolenbroek void
llvm_gcda_emit_function(uint32_t ident,const char * func_name,uint32_t func_cksum,uint8_t extra_cksum,uint32_t cfg_cksum)118*3ac58492SDavid van Moolenbroek llvm_gcda_emit_function(uint32_t ident, const char * func_name,
119*3ac58492SDavid van Moolenbroek #if LLVM_35
120*3ac58492SDavid van Moolenbroek 	uint32_t func_cksum, uint8_t extra_cksum, uint32_t cfg_cksum)
121*3ac58492SDavid van Moolenbroek #else
122*3ac58492SDavid van Moolenbroek 	uint8_t extra_cksum)
123*3ac58492SDavid van Moolenbroek #endif
124*3ac58492SDavid van Moolenbroek {
125*3ac58492SDavid van Moolenbroek 	uint32_t word[6];
126*3ac58492SDavid van Moolenbroek 	size_t words, len, wlen;
127*3ac58492SDavid van Moolenbroek 
128*3ac58492SDavid van Moolenbroek 	word[0] = 0x01000000;			/* tag: function */
129*3ac58492SDavid van Moolenbroek 	words = 2;
130*3ac58492SDavid van Moolenbroek 	word[2] = ident;			/* ident */
131*3ac58492SDavid van Moolenbroek #if LLVM_35
132*3ac58492SDavid van Moolenbroek 	word[3] = func_cksum;			/* function checksum */
133*3ac58492SDavid van Moolenbroek #else
134*3ac58492SDavid van Moolenbroek 	word[3] = 0;				/* function checksum */
135*3ac58492SDavid van Moolenbroek #endif
136*3ac58492SDavid van Moolenbroek 	if (extra_cksum) {
137*3ac58492SDavid van Moolenbroek #if LLVM_35
138*3ac58492SDavid van Moolenbroek 		word[4] = cfg_cksum;		/* configuration checksum */
139*3ac58492SDavid van Moolenbroek #else
140*3ac58492SDavid van Moolenbroek 		word[4] = 0;			/* configuration checksum */
141*3ac58492SDavid van Moolenbroek #endif
142*3ac58492SDavid van Moolenbroek 		words++;
143*3ac58492SDavid van Moolenbroek 	}
144*3ac58492SDavid van Moolenbroek 	word[1] = words;			/* length */
145*3ac58492SDavid van Moolenbroek 
146*3ac58492SDavid van Moolenbroek 	if (func_name != NULL) {
147*3ac58492SDavid van Moolenbroek 		len = strlen(func_name) + 1;
148*3ac58492SDavid van Moolenbroek 		wlen = len / sizeof(word[0]) + 1;
149*3ac58492SDavid van Moolenbroek 		word[1] += 1 + wlen;
150*3ac58492SDavid van Moolenbroek 		word[2 + words] = wlen;
151*3ac58492SDavid van Moolenbroek 		words++;
152*3ac58492SDavid van Moolenbroek 	}
153*3ac58492SDavid van Moolenbroek 
154*3ac58492SDavid van Moolenbroek 	_gcov_fwrite(word, sizeof(word[0]), 2 + words, gcov_file);
155*3ac58492SDavid van Moolenbroek 
156*3ac58492SDavid van Moolenbroek 	if (func_name != NULL) {
157*3ac58492SDavid van Moolenbroek 		_gcov_fwrite(func_name, 1, len, gcov_file);
158*3ac58492SDavid van Moolenbroek 		_gcov_fwrite("\0\0\0\0", 1, wlen * sizeof(uint32_t) - len,
159*3ac58492SDavid van Moolenbroek 		    gcov_file);
160*3ac58492SDavid van Moolenbroek 	}
161*3ac58492SDavid van Moolenbroek }
162*3ac58492SDavid van Moolenbroek 
163*3ac58492SDavid van Moolenbroek /*
164*3ac58492SDavid van Moolenbroek  * LLVM hook for writing function arc counters to the currently opened .gcda
165*3ac58492SDavid van Moolenbroek  * file.
166*3ac58492SDavid van Moolenbroek  */
167*3ac58492SDavid van Moolenbroek void
llvm_gcda_emit_arcs(uint32_t ncounters,uint64_t * counters)168*3ac58492SDavid van Moolenbroek llvm_gcda_emit_arcs(uint32_t ncounters, uint64_t * counters)
169*3ac58492SDavid van Moolenbroek {
170*3ac58492SDavid van Moolenbroek 	uint32_t word[2];
171*3ac58492SDavid van Moolenbroek 
172*3ac58492SDavid van Moolenbroek 	assert(gcov_file != NULL);
173*3ac58492SDavid van Moolenbroek 
174*3ac58492SDavid van Moolenbroek 	word[0] = 0x01a10000;			/* tag: arc counters */
175*3ac58492SDavid van Moolenbroek 	word[1] = ncounters * 2;		/* length */
176*3ac58492SDavid van Moolenbroek 
177*3ac58492SDavid van Moolenbroek 	_gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file);
178*3ac58492SDavid van Moolenbroek 	_gcov_fwrite(counters, sizeof(*counters), ncounters, gcov_file);
179*3ac58492SDavid van Moolenbroek }
180*3ac58492SDavid van Moolenbroek 
181*3ac58492SDavid van Moolenbroek /*
182*3ac58492SDavid van Moolenbroek  * LLVM hook for writing summary information to the currently opened .gcda
183*3ac58492SDavid van Moolenbroek  * file.
184*3ac58492SDavid van Moolenbroek  */
185*3ac58492SDavid van Moolenbroek void
llvm_gcda_summary_info(void)186*3ac58492SDavid van Moolenbroek llvm_gcda_summary_info(void)
187*3ac58492SDavid van Moolenbroek {
188*3ac58492SDavid van Moolenbroek 	uint32_t word[13];
189*3ac58492SDavid van Moolenbroek 
190*3ac58492SDavid van Moolenbroek 	memset(word, 0, sizeof(word));
191*3ac58492SDavid van Moolenbroek 	word[0] = 0xa1000000;			/* tag: object summary */
192*3ac58492SDavid van Moolenbroek 	word[1] = 9;				/* length */
193*3ac58492SDavid van Moolenbroek 	word[2] = 0;				/* checksum */
194*3ac58492SDavid van Moolenbroek 	word[3] = 0;				/* counter number */
195*3ac58492SDavid van Moolenbroek 	word[4] = 1;				/* runs */
196*3ac58492SDavid van Moolenbroek 	word[11] = 0xa3000000;			/* tag: program summary */
197*3ac58492SDavid van Moolenbroek 	word[12] = 0;				/* length */
198*3ac58492SDavid van Moolenbroek 
199*3ac58492SDavid van Moolenbroek 	_gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file);
200*3ac58492SDavid van Moolenbroek }
201*3ac58492SDavid van Moolenbroek 
202*3ac58492SDavid van Moolenbroek /*
203*3ac58492SDavid van Moolenbroek  * LLVM hook for closing the currently opened .gcda file.
204*3ac58492SDavid van Moolenbroek  */
205*3ac58492SDavid van Moolenbroek void
llvm_gcda_end_file(void)206*3ac58492SDavid van Moolenbroek llvm_gcda_end_file(void)
207*3ac58492SDavid van Moolenbroek {
208*3ac58492SDavid van Moolenbroek 	uint32_t word[2];
209*3ac58492SDavid van Moolenbroek 
210*3ac58492SDavid van Moolenbroek 	assert(gcov_file != NULL);
211*3ac58492SDavid van Moolenbroek 
212*3ac58492SDavid van Moolenbroek 	word[0] = 0;				/* tag: end of file */
213*3ac58492SDavid van Moolenbroek 	word[1] = 0;				/* length zero */
214*3ac58492SDavid van Moolenbroek 	_gcov_fwrite(word, sizeof(word[0]), __arraycount(word), gcov_file);
215*3ac58492SDavid van Moolenbroek 
216*3ac58492SDavid van Moolenbroek 	_gcov_fclose(gcov_file);
217*3ac58492SDavid van Moolenbroek 	gcov_file = NULL;
218*3ac58492SDavid van Moolenbroek }
219*3ac58492SDavid van Moolenbroek 
220*3ac58492SDavid van Moolenbroek /*
221*3ac58492SDavid van Moolenbroek  * Our implementation for LLVM of the GCC function to flush the coverage data.
222*3ac58492SDavid van Moolenbroek  * The function is called by our libsys's GCOV code.
223*3ac58492SDavid van Moolenbroek  */
224*3ac58492SDavid van Moolenbroek void
__gcov_flush(void)225*3ac58492SDavid van Moolenbroek __gcov_flush(void)
226*3ac58492SDavid van Moolenbroek {
227*3ac58492SDavid van Moolenbroek 	unsigned int i;
228*3ac58492SDavid van Moolenbroek 
229*3ac58492SDavid van Moolenbroek 	/* Call the flush function for each registered module. */
230*3ac58492SDavid van Moolenbroek 	for (i = 0; i < flush_count; i++)
231*3ac58492SDavid van Moolenbroek 		flush_array[i]();
232*3ac58492SDavid van Moolenbroek }
233*3ac58492SDavid van Moolenbroek 
234*3ac58492SDavid van Moolenbroek /*
235*3ac58492SDavid van Moolenbroek  * LLVM hook for registration of write and flush callbacks.  The former is to
236*3ac58492SDavid van Moolenbroek  * be used on exit, the latter on a pre-exit flush.  We use the latter only.
237*3ac58492SDavid van Moolenbroek  * This function is basically called once for each compiled source module.
238*3ac58492SDavid van Moolenbroek  */
239*3ac58492SDavid van Moolenbroek void
llvm_gcov_init(write_cb_t write_cb __unused,flush_cb_t flush_cb)240*3ac58492SDavid van Moolenbroek llvm_gcov_init(write_cb_t write_cb __unused, flush_cb_t flush_cb)
241*3ac58492SDavid van Moolenbroek {
242*3ac58492SDavid van Moolenbroek 
243*3ac58492SDavid van Moolenbroek 	if (flush_cb == NULL)
244*3ac58492SDavid van Moolenbroek 		return;
245*3ac58492SDavid van Moolenbroek 
246*3ac58492SDavid van Moolenbroek 	/* If the array is full, drop this module. */
247*3ac58492SDavid van Moolenbroek 	if (flush_count == __arraycount(flush_array))
248*3ac58492SDavid van Moolenbroek 		return; /* array full, so we are going to miss information */
249*3ac58492SDavid van Moolenbroek 
250*3ac58492SDavid van Moolenbroek 	/* Add the flush function to the array. */
251*3ac58492SDavid van Moolenbroek 	flush_array[flush_count++] = flush_cb;
252*3ac58492SDavid van Moolenbroek 
253*3ac58492SDavid van Moolenbroek 	/*
254*3ac58492SDavid van Moolenbroek 	 * We print this warning here so that we print it only once.  What are
255*3ac58492SDavid van Moolenbroek 	 * the odds that there are *exactly* NR_MODULES modules anyway?
256*3ac58492SDavid van Moolenbroek 	 */
257*3ac58492SDavid van Moolenbroek 	if (flush_count == __arraycount(flush_array))
258*3ac58492SDavid van Moolenbroek 		printf("llvm_gcov: process %d has too many modules, "
259*3ac58492SDavid van Moolenbroek 		    "profiling data lost\n", sef_self());
260*3ac58492SDavid van Moolenbroek }
261*3ac58492SDavid van Moolenbroek 
262*3ac58492SDavid van Moolenbroek #endif /*__clang__*/
263