1 /*
2 * This code provides functions to handle gcc's profiling data format
3 * introduced with gcc 4.7.
4 *
5 * This file is based heavily on gcc_3_4.c file.
6 *
7 * For a better understanding, refer to gcc source:
8 * gcc/gcov-io.h
9 * libgcc/libgcov.c
10 *
11 * Uses gcc-internal data definitions.
12 *
13 * Imported from Linux and modified for Xen by
14 * Wei Liu <wei.liu2@citrix.com>
15 */
16
17 #include <xen/string.h>
18
19 #include "gcov.h"
20
21 #if GCC_VERSION < 40700
22 #error "Wrong version of GCC used to compile gcov"
23 #elif GCC_VERSION < 40900
24 #define GCOV_COUNTERS 8
25 #elif GCC_VERSION < 50000
26 #define GCOV_COUNTERS 9
27 #elif GCC_VERSION < 70000
28 #define GCOV_COUNTERS 10
29 #elif GCC_VERSION < 100000
30 #define GCOV_COUNTERS 9
31 #elif GCC_VERSION < 140000
32 #define GCOV_COUNTERS 8
33 #elif GCC_VERSION < 150000
34 #define GCOV_COUNTERS 9
35 #else
36 #define GCOV_COUNTERS 10
37 #endif
38
39 #define GCOV_TAG_FUNCTION_LENGTH 3
40
41 #if GCC_VERSION < 120000
42 #define GCOV_UNIT_SIZE 1
43 #else
44 /* Since GCC 12, sizes are in BYTES and not in WORDS (4B). */
45 #define GCOV_UNIT_SIZE 4
46 #endif
47
48 static struct gcov_info *gcov_info_head;
49
50 /**
51 * struct gcov_ctr_info - information about counters for a single function
52 * @num: number of counter values for this type
53 * @values: array of counter values for this type
54 *
55 * This data is generated by gcc during compilation and doesn't change
56 * at run-time with the exception of the values array.
57 */
58 struct gcov_ctr_info {
59 unsigned int num;
60 gcov_type *values;
61 };
62
63 /**
64 * struct gcov_fn_info - profiling meta data per function
65 * @key: comdat key
66 * @ident: unique ident of function
67 * @lineno_checksum: function lineo_checksum
68 * @cfg_checksum: function cfg checksum
69 * @ctrs: instrumented counters
70 *
71 * This data is generated by gcc during compilation and doesn't change
72 * at run-time.
73 *
74 * Information about a single function. This uses the trailing array
75 * idiom. The number of counters is determined from the merge pointer
76 * array in gcov_info. The key is used to detect which of a set of
77 * comdat functions was selected -- it points to the gcov_info object
78 * of the object file containing the selected comdat function.
79 */
80 struct gcov_fn_info {
81 const struct gcov_info *key;
82 unsigned int ident;
83 unsigned int lineno_checksum;
84 unsigned int cfg_checksum;
85 struct gcov_ctr_info ctrs[0];
86 };
87
88 /**
89 * struct gcov_info - profiling data per object file
90 * @version: gcov version magic indicating the gcc version used for compilation
91 * @next: list head for a singly-linked list
92 * @stamp: uniquifying time stamp
93 * @filename: name of the associated gcov data file
94 * @merge: merge functions (null for unused counter type)
95 * @n_functions: number of instrumented functions
96 * @functions: pointer to pointers to function information
97 *
98 * This data is generated by gcc during compilation and doesn't change
99 * at run-time with the exception of the next pointer.
100 */
101 struct gcov_info {
102 unsigned int version;
103 struct gcov_info *next;
104 unsigned int stamp;
105 #if GCC_VERSION >= 120000
106 /* GCC 12 introduced a checksum field */
107 unsigned int checksum;
108 #endif
109 const char *filename;
110 void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
111 unsigned int n_functions;
112 struct gcov_fn_info **functions;
113 };
114
counter_active(const struct gcov_info * info,unsigned int type)115 static int counter_active(const struct gcov_info *info, unsigned int type)
116 {
117 return info->merge[type] ? 1 : 0;
118 }
119
gcov_info_link(struct gcov_info * info)120 void gcov_info_link(struct gcov_info *info)
121 {
122 info->next = gcov_info_head;
123 gcov_info_head = info;
124 }
125
gcov_info_next(const struct gcov_info * info)126 struct gcov_info *gcov_info_next(const struct gcov_info *info)
127 {
128 if ( !info )
129 return gcov_info_head;
130 return info->next;
131 }
132
gcov_info_reset(struct gcov_info * info)133 void gcov_info_reset(struct gcov_info *info)
134 {
135 struct gcov_ctr_info *ci_ptr;
136 unsigned int fi_idx;
137 unsigned int ct_idx;
138
139 for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ )
140 {
141 ci_ptr = info->functions[fi_idx]->ctrs;
142
143 for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ )
144 {
145 if ( !counter_active(info, ct_idx) )
146 continue;
147
148 memset(ci_ptr->values, 0, sizeof(gcov_type) * ci_ptr->num);
149 ci_ptr++;
150 }
151 }
152 }
153
gcov_info_filename(const struct gcov_info * info)154 const char *gcov_info_filename(const struct gcov_info *info)
155 {
156 return info->filename;
157 }
158
159
160 /**
161 * gcov_info_to_gcda - convert profiling data set to gcda file format
162 * @buffer: the buffer to store file data or %NULL if no data should be stored
163 * @info: profiling data set to be converted
164 *
165 * Returns the number of bytes that were/would have been stored into the buffer.
166 */
gcov_info_to_gcda(char * buffer,const struct gcov_info * info)167 size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info)
168 {
169 struct gcov_fn_info *fi_ptr;
170 struct gcov_ctr_info *ci_ptr;
171 unsigned int fi_idx;
172 unsigned int ct_idx;
173 unsigned int cv_idx;
174 size_t pos = 0;
175
176 /* File header. */
177 pos += gcov_store_uint32(buffer, pos, GCOV_DATA_MAGIC);
178 pos += gcov_store_uint32(buffer, pos, info->version);
179 pos += gcov_store_uint32(buffer, pos, info->stamp);
180
181 #if GCC_VERSION >= 120000
182 /* Use zero as checksum of the compilation unit. */
183 pos += gcov_store_uint32(buffer, pos, 0);
184 #endif
185
186 for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ )
187 {
188 fi_ptr = info->functions[fi_idx];
189
190 /* Function record. */
191 pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION);
192 pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH * GCOV_UNIT_SIZE);
193 pos += gcov_store_uint32(buffer, pos, fi_ptr->ident);
194 pos += gcov_store_uint32(buffer, pos, fi_ptr->lineno_checksum);
195 pos += gcov_store_uint32(buffer, pos, fi_ptr->cfg_checksum);
196
197 ci_ptr = fi_ptr->ctrs;
198
199 for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ )
200 {
201 if (! counter_active(info, ct_idx) )
202 continue;
203
204 /* Counter record. */
205 pos += gcov_store_uint32(buffer, pos,
206 GCOV_TAG_FOR_COUNTER(ct_idx));
207 pos += gcov_store_uint32(buffer, pos, ci_ptr->num * 2 * GCOV_UNIT_SIZE);
208
209 for ( cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++ )
210 pos += gcov_store_uint64(buffer, pos, ci_ptr->values[cv_idx]);
211
212 ci_ptr++;
213 }
214 }
215
216 return pos;
217 }
218
219 /*
220 * Local variables:
221 * mode: C
222 * c-file-style: "BSD"
223 * c-basic-offset: 4
224 * tab-width: 4
225 * indent-tabs-mode: nil
226 * End:
227 */
228