/root/src/xen/xen/common/gcov/llvm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Generic functionality for handling accesses to the PCI header from the |
3 | | * configuration space. |
4 | | * |
5 | | * Copyright (C) 2017 Citrix Systems R&D |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or |
8 | | * modify it under the terms and conditions of the GNU General Public |
9 | | * License, version 2, as published by the Free Software Foundation. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public |
17 | | * License along with this program; If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | //#include <errno.h> |
21 | | //#include <inttypes.h> |
22 | | |
23 | | //#include <string.h> |
24 | | #include <xen/errno.h> |
25 | | #include <xen/guest_access.h> |
26 | | #include <xen/types.h> |
27 | | |
28 | | #include <public/sysctl.h> |
29 | | |
30 | | //#include "InstrProfData.inc" |
31 | | |
32 | | enum llvm_profile_kind |
33 | | { |
34 | | indirect_call, |
35 | | memop_size, |
36 | | num_kinds, |
37 | | }; |
38 | | |
39 | | struct llvm_function_header { |
40 | | /* |
41 | | * Total size in bytes including this field. It must be a multiple |
42 | | * of sizeof(uint64_t). |
43 | | */ |
44 | | uint32_t size; |
45 | | /* |
46 | | *The number of value profile kinds that has value profile data. |
47 | | * In this implementation, a value profile kind is considered to |
48 | | * have profile data if the number of value profile sites for the |
49 | | * kind is not zero. More aggressively, the implementation can |
50 | | * choose to check the actual data value: if none of the value sites |
51 | | * has any profiled values, the kind can be skipped. |
52 | | */ |
53 | | uint32_t nr_value_kinds; |
54 | | }; |
55 | | |
56 | | struct llvm_profile_node |
57 | | { |
58 | | struct llvm_profile_node_val { |
59 | | uint64_t value; |
60 | | uint64_t count; |
61 | | } val; |
62 | | struct llvm_profile_node *next; |
63 | | }; |
64 | | |
65 | | struct llvm_profile_data |
66 | | { |
67 | | uint64_t name_ref; |
68 | | uint64_t function_hash; |
69 | | |
70 | | void *counter; |
71 | | void *function; |
72 | | struct llvm_profile_node **values; |
73 | | |
74 | | uint32_t nr_counters; |
75 | | uint16_t nr_value_sites[num_kinds]; |
76 | | }; |
77 | | |
78 | | struct llvm_profile_header { |
79 | | uint64_t magic; |
80 | | uint64_t version; |
81 | | uint64_t data_size; |
82 | | uint64_t counters_size; |
83 | | uint64_t names_size; |
84 | | uint64_t counters_delta; |
85 | | uint64_t names_delta; |
86 | | uint64_t value_kind_last; |
87 | | }; |
88 | | |
89 | | int __llvm_profile_runtime; |
90 | | |
91 | 1 | #define INSTR_PROF_RAW_MAGIC_64 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \ |
92 | 1 | (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | \ |
93 | 1 | (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129 |
94 | | #define INSTR_PROF_RAW_MAGIC_32 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \ |
95 | | (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | \ |
96 | | (uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129 |
97 | | |
98 | | extern struct llvm_profile_data __start___llvm_prf_data; |
99 | | extern struct llvm_profile_data __stop___llvm_prf_data; |
100 | | extern uint64_t __start___llvm_prf_cnts; |
101 | | extern uint64_t __stop___llvm_prf_cnts; |
102 | | extern char __start___llvm_prf_names; |
103 | | extern char __stop___llvm_prf_names; |
104 | | |
105 | | static uint64_t *__llvm_profile_begin_counters(void) |
106 | 2 | { |
107 | 2 | return &__start___llvm_prf_cnts; |
108 | 2 | } |
109 | | |
110 | | static uint64_t *__llvm_profile_end_counters(void) |
111 | 2 | { |
112 | 2 | return &__stop___llvm_prf_cnts; |
113 | 2 | } |
114 | | |
115 | | static const struct llvm_profile_data * __llvm_profile_begin_data(void) |
116 | 2 | { |
117 | 2 | return &__start___llvm_prf_data; |
118 | 2 | } |
119 | | |
120 | | static const struct llvm_profile_data * __llvm_profile_end_data(void) |
121 | 2 | { |
122 | 2 | return &__stop___llvm_prf_data; |
123 | 2 | } |
124 | | |
125 | | static const char *__llvm_profile_begin_names(void) |
126 | 2 | { |
127 | 2 | return &__start___llvm_prf_names; |
128 | 2 | } |
129 | | static const char *__llvm_profile_end_names(void) |
130 | 2 | { |
131 | 2 | return &__stop___llvm_prf_names; |
132 | 2 | } |
133 | | uint64_t __llvm_profile_get_data_size(const struct llvm_profile_data *Begin, |
134 | | const struct llvm_profile_data *End) |
135 | 2 | { |
136 | 2 | uint64_t BeginI = (uint64_t)Begin, EndI = (uint64_t)End; |
137 | 2 | |
138 | 2 | return ((EndI + sizeof(struct llvm_profile_data) - 1) - BeginI) / |
139 | 2 | sizeof(struct llvm_profile_data); |
140 | 2 | } |
141 | | |
142 | | uint8_t __llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes) |
143 | 2 | { |
144 | 2 | return 7 & (sizeof(uint64_t) - SizeInBytes % sizeof(uint64_t)); |
145 | 2 | } |
146 | | |
147 | | void llvm_profile_reset_counters(void) |
148 | 0 | { |
149 | 0 | const struct llvm_profile_data *data; |
150 | 0 |
|
151 | 0 | memset(__llvm_profile_begin_counters(), 0, |
152 | 0 | sizeof(uint64_t) * (__llvm_profile_end_counters() - |
153 | 0 | __llvm_profile_begin_counters())); |
154 | 0 |
|
155 | 0 | for ( data = __llvm_profile_begin_data(); |
156 | 0 | data < __llvm_profile_end_data(); |
157 | 0 | data++ ) |
158 | 0 | { |
159 | 0 | uint64_t site_count = 0; |
160 | 0 | struct llvm_profile_node **nodes; |
161 | 0 | uint32_t VKI, i; |
162 | 0 |
|
163 | 0 | if ( !data->values ) |
164 | 0 | continue; |
165 | 0 |
|
166 | 0 | nodes = data->values; |
167 | 0 |
|
168 | 0 | for (VKI = 0; VKI <= num_kinds; VKI++) |
169 | 0 | site_count += data->nr_value_sites[VKI]; |
170 | 0 |
|
171 | 0 | printk(XENLOG_INFO "site_count = %lu\n", site_count); |
172 | 0 | for (i = 0; i < site_count; i++) |
173 | 0 | { |
174 | 0 | struct llvm_profile_node *node = nodes[i]; |
175 | 0 |
|
176 | 0 | while (node) |
177 | 0 | { |
178 | 0 | node->val.count = 0; |
179 | 0 | node = node->next; |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | | void llvm_profile_write(XEN_GUEST_HANDLE_PARAM(char) buffer) |
186 | 1 | { |
187 | 1 | const struct llvm_profile_data *DataBegin = __llvm_profile_begin_data(); |
188 | 1 | const struct llvm_profile_data *DataEnd = __llvm_profile_end_data(); |
189 | 1 | const uint64_t *CountersBegin = __llvm_profile_begin_counters(); |
190 | 1 | const uint64_t *CountersEnd = __llvm_profile_end_counters(); |
191 | 1 | const char *NamesBegin = __llvm_profile_begin_names(); |
192 | 1 | const char *NamesEnd = __llvm_profile_end_names(); |
193 | 1 | const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); |
194 | 1 | const uint64_t CountersSize = CountersEnd - CountersBegin; |
195 | 1 | const uint64_t NamesSize = NamesEnd - NamesBegin; |
196 | 1 | const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); |
197 | 1 | struct llvm_profile_header header; |
198 | 1 | unsigned int off = 0; |
199 | 1 | |
200 | 1 | #if BITS_PER_LONG == 64 |
201 | 1 | header.magic = INSTR_PROF_RAW_MAGIC_64; |
202 | 1 | #else |
203 | | header.magic = INSTR_PROF_RAW_MAGIC_32; |
204 | | #endif |
205 | 1 | /* XXX: hardcoded version number... */ |
206 | 1 | header.version = 4; |
207 | 1 | header.data_size = DataSize; |
208 | 1 | header.counters_size = CountersSize; |
209 | 1 | header.names_size = NamesSize; |
210 | 1 | header.counters_delta = (uintptr_t)CountersBegin; |
211 | 1 | header.names_delta = (uintptr_t)NamesBegin; |
212 | 1 | header.value_kind_last = num_kinds - 1; |
213 | 1 | |
214 | 1 | /* Copy data into the buffer. */ |
215 | 1 | copy_to_guest_offset(buffer, off, (char *)&header, sizeof(struct llvm_profile_header)); |
216 | 1 | off += sizeof(struct llvm_profile_header); |
217 | 1 | copy_to_guest_offset(buffer, off, (char *)DataBegin, DataSize * sizeof(struct llvm_profile_data)); |
218 | 1 | off += DataSize * sizeof(struct llvm_profile_data); |
219 | 1 | copy_to_guest_offset(buffer, off, (char *)CountersBegin, CountersSize * sizeof(uint64_t)); |
220 | 1 | off += CountersSize * sizeof(uint64_t); |
221 | 1 | copy_to_guest_offset(buffer, off, NamesBegin, NamesSize * sizeof(uint8_t)); |
222 | 1 | off += NamesSize * sizeof(uint8_t); |
223 | 1 | clear_guest_offset(buffer, off, Padding * sizeof(uint8_t)); |
224 | 1 | off += Padding * sizeof(uint8_t); |
225 | 1 | } |
226 | | |
227 | | size_t llvm_profile_get_size(void) |
228 | 1 | { |
229 | 1 | //const struct llvm_profile_data *data; |
230 | 1 | const struct llvm_profile_data *DataBegin = __llvm_profile_begin_data(); |
231 | 1 | const struct llvm_profile_data *DataEnd = __llvm_profile_end_data(); |
232 | 1 | const uint64_t *CountersBegin = __llvm_profile_begin_counters(); |
233 | 1 | const uint64_t *CountersEnd = __llvm_profile_end_counters(); |
234 | 1 | const char *NamesBegin = __llvm_profile_begin_names(); |
235 | 1 | const char *NamesEnd = __llvm_profile_end_names(); |
236 | 1 | const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); |
237 | 1 | const uint64_t CountersSize = CountersEnd - CountersBegin; |
238 | 1 | const uint64_t NamesSize = NamesEnd - NamesBegin; |
239 | 1 | const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); |
240 | 1 | //size_t size = 0; |
241 | 1 | // |
242 | 1 | printk(XENLOG_INFO "Data size: %lu\n", DataSize); |
243 | 1 | printk(XENLOG_INFO "Name size: %lu\n", NamesSize); |
244 | 1 | printk(XENLOG_INFO "Counter size: %lu\n", CountersSize); |
245 | 1 | printk(XENLOG_INFO "Padding size: %u\n", Padding); |
246 | 1 | |
247 | 1 | return sizeof(struct llvm_profile_header) + |
248 | 1 | sizeof(struct llvm_profile_data) * DataSize + |
249 | 1 | sizeof(uint64_t) * CountersSize + sizeof(uint8_t) * NamesSize + |
250 | 1 | sizeof(uint8_t) * Padding; |
251 | 1 | |
252 | 1 | #if 0 |
253 | | for ( data = __llvm_profile_begin_data(); |
254 | | data < __llvm_profile_end_data(); |
255 | | data++ ) |
256 | | { |
257 | | enum llvm_profile_kind kind; |
258 | | uint64_t site_count = 0; |
259 | | struct llvm_profile_node **nodes; |
260 | | uint32_t VKI, i; |
261 | | |
262 | | for ( kind = 0; kind < num_kinds; kind++ ) |
263 | | { |
264 | | if ( !data->nr_value_sites[kind] ) |
265 | | continue; |
266 | | else |
267 | | printf("data not null!\n!"); |
268 | | #if 0 |
269 | | if ( !data->values ) |
270 | | continue; |
271 | | |
272 | | nodes = data->values; |
273 | | |
274 | | for (VKI = 0; VKI <= num_kinds; VKI++) |
275 | | site_count += data->nr_value_sites[VKI]; |
276 | | |
277 | | size += site_count * sizeof(struct llvm_function_header); |
278 | | for (i = 0; i < site_count; i++) |
279 | | { |
280 | | struct llvm_profile_node *node = nodes[i]; |
281 | | |
282 | | while (node) |
283 | | { |
284 | | size += sizeof(struct llvm_profile_node_val); |
285 | | node = node->next; |
286 | | } |
287 | | } |
288 | | #endif |
289 | | } |
290 | | } |
291 | | #endif |
292 | 1 | |
293 | 1 | //return size; |
294 | 1 | } |
295 | | |
296 | | int sysctl_gcov_op(struct xen_sysctl_gcov_op *op) |
297 | 2 | { |
298 | 2 | switch ( op->cmd ) |
299 | 2 | { |
300 | 1 | case XEN_SYSCTL_GCOV_get_size: |
301 | 1 | op->size = llvm_profile_get_size(); |
302 | 1 | break; |
303 | 0 | case XEN_SYSCTL_GCOV_reset: |
304 | 0 | llvm_profile_reset_counters(); |
305 | 0 | break; |
306 | 1 | case XEN_SYSCTL_GCOV_read: |
307 | 1 | { |
308 | 1 | XEN_GUEST_HANDLE_PARAM(char) buf; |
309 | 1 | |
310 | 1 | buf = guest_handle_cast(op->buffer, char); |
311 | 1 | llvm_profile_write(buf); |
312 | 1 | |
313 | 1 | break; |
314 | 1 | } |
315 | 0 | default: |
316 | 0 | return -ENOSYS; |
317 | 2 | } |
318 | 2 | |
319 | 1 | return 0; |
320 | 2 | } |