diff options
author | Frantisek Hrbata <fhrbata@redhat.com> | 2013-11-12 18:11:26 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-12 22:09:34 -0500 |
commit | 5f41ea0386a53414d688cfcaa321a78310e5f7c1 (patch) | |
tree | 8f9d3b7020b5ee3b31445b993d16cad8f7736584 /kernel/gcov | |
parent | 8cbce376e3fdf4a21f59365aefbb52eac3c2e312 (diff) |
gcov: add support for gcc 4.7 gcov format
The gcov in-memory format changed in gcc 4.7. The biggest change, which
requires this special implementation, is that gcov_info no longer contains
array of counters for each counter type for all functions and gcov_fn_info
is not used for mapping of function's counters to these arrays(offset).
Now each gcov_fn_info contans it's counters, which makes things a little
bit easier.
This is heavily based on the previous gcc_3_4.c implementation and patches
provided by Peter Oberparleiter. Specially the buffer gcda implementation
for iterator.
[akpm@linux-foundation.org: use kmemdup() and kcalloc()]
[oberpar@linux.vnet.ibm.com: gcc_4_7.c needs vmalloc.h]
Signed-off-by: Frantisek Hrbata <fhrbata@redhat.com>
Cc: Jan Stancek <jstancek@redhat.com>
Cc: Kees Cook <keescook@chromium.org>
Reviewed-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Andy Gospodarek <agospoda@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/gcov')
-rw-r--r-- | kernel/gcov/base.c | 6 | ||||
-rw-r--r-- | kernel/gcov/gcc_4_7.c | 560 |
2 files changed, 566 insertions, 0 deletions
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c index 912576a671d8..f45b75b713c0 100644 --- a/kernel/gcov/base.c +++ b/kernel/gcov/base.c | |||
@@ -79,6 +79,12 @@ void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters) | |||
79 | } | 79 | } |
80 | EXPORT_SYMBOL(__gcov_merge_delta); | 80 | EXPORT_SYMBOL(__gcov_merge_delta); |
81 | 81 | ||
82 | void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters) | ||
83 | { | ||
84 | /* Unused. */ | ||
85 | } | ||
86 | EXPORT_SYMBOL(__gcov_merge_ior); | ||
87 | |||
82 | /** | 88 | /** |
83 | * gcov_enable_events - enable event reporting through gcov_event() | 89 | * gcov_enable_events - enable event reporting through gcov_event() |
84 | * | 90 | * |
diff --git a/kernel/gcov/gcc_4_7.c b/kernel/gcov/gcc_4_7.c new file mode 100644 index 000000000000..2c6e4631c814 --- /dev/null +++ b/kernel/gcov/gcc_4_7.c | |||
@@ -0,0 +1,560 @@ | |||
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 | |||
14 | #include <linux/errno.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/seq_file.h> | ||
18 | #include <linux/vmalloc.h> | ||
19 | #include "gcov.h" | ||
20 | |||
21 | #define GCOV_COUNTERS 8 | ||
22 | #define GCOV_TAG_FUNCTION_LENGTH 3 | ||
23 | |||
24 | static struct gcov_info *gcov_info_head; | ||
25 | |||
26 | /** | ||
27 | * struct gcov_ctr_info - information about counters for a single function | ||
28 | * @num: number of counter values for this type | ||
29 | * @values: array of counter values for this type | ||
30 | * | ||
31 | * This data is generated by gcc during compilation and doesn't change | ||
32 | * at run-time with the exception of the values array. | ||
33 | */ | ||
34 | struct gcov_ctr_info { | ||
35 | unsigned int num; | ||
36 | gcov_type *values; | ||
37 | }; | ||
38 | |||
39 | /** | ||
40 | * struct gcov_fn_info - profiling meta data per function | ||
41 | * @key: comdat key | ||
42 | * @ident: unique ident of function | ||
43 | * @lineno_checksum: function lineo_checksum | ||
44 | * @cfg_checksum: function cfg checksum | ||
45 | * @ctrs: instrumented counters | ||
46 | * | ||
47 | * This data is generated by gcc during compilation and doesn't change | ||
48 | * at run-time. | ||
49 | * | ||
50 | * Information about a single function. This uses the trailing array | ||
51 | * idiom. The number of counters is determined from the merge pointer | ||
52 | * array in gcov_info. The key is used to detect which of a set of | ||
53 | * comdat functions was selected -- it points to the gcov_info object | ||
54 | * of the object file containing the selected comdat function. | ||
55 | */ | ||
56 | struct gcov_fn_info { | ||
57 | const struct gcov_info *key; | ||
58 | unsigned int ident; | ||
59 | unsigned int lineno_checksum; | ||
60 | unsigned int cfg_checksum; | ||
61 | struct gcov_ctr_info ctrs[0]; | ||
62 | }; | ||
63 | |||
64 | /** | ||
65 | * struct gcov_info - profiling data per object file | ||
66 | * @version: gcov version magic indicating the gcc version used for compilation | ||
67 | * @next: list head for a singly-linked list | ||
68 | * @stamp: uniquifying time stamp | ||
69 | * @filename: name of the associated gcov data file | ||
70 | * @merge: merge functions (null for unused counter type) | ||
71 | * @n_functions: number of instrumented functions | ||
72 | * @functions: pointer to pointers to function information | ||
73 | * | ||
74 | * This data is generated by gcc during compilation and doesn't change | ||
75 | * at run-time with the exception of the next pointer. | ||
76 | */ | ||
77 | struct gcov_info { | ||
78 | unsigned int version; | ||
79 | struct gcov_info *next; | ||
80 | unsigned int stamp; | ||
81 | const char *filename; | ||
82 | void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int); | ||
83 | unsigned int n_functions; | ||
84 | struct gcov_fn_info **functions; | ||
85 | }; | ||
86 | |||
87 | /** | ||
88 | * gcov_info_filename - return info filename | ||
89 | * @info: profiling data set | ||
90 | */ | ||
91 | const char *gcov_info_filename(struct gcov_info *info) | ||
92 | { | ||
93 | return info->filename; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * gcov_info_version - return info version | ||
98 | * @info: profiling data set | ||
99 | */ | ||
100 | unsigned int gcov_info_version(struct gcov_info *info) | ||
101 | { | ||
102 | return info->version; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * gcov_info_next - return next profiling data set | ||
107 | * @info: profiling data set | ||
108 | * | ||
109 | * Returns next gcov_info following @info or first gcov_info in the chain if | ||
110 | * @info is %NULL. | ||
111 | */ | ||
112 | struct gcov_info *gcov_info_next(struct gcov_info *info) | ||
113 | { | ||
114 | if (!info) | ||
115 | return gcov_info_head; | ||
116 | |||
117 | return info->next; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * gcov_info_link - link/add profiling data set to the list | ||
122 | * @info: profiling data set | ||
123 | */ | ||
124 | void gcov_info_link(struct gcov_info *info) | ||
125 | { | ||
126 | info->next = gcov_info_head; | ||
127 | gcov_info_head = info; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * gcov_info_unlink - unlink/remove profiling data set from the list | ||
132 | * @prev: previous profiling data set | ||
133 | * @info: profiling data set | ||
134 | */ | ||
135 | void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info) | ||
136 | { | ||
137 | if (prev) | ||
138 | prev->next = info->next; | ||
139 | else | ||
140 | gcov_info_head = info->next; | ||
141 | } | ||
142 | |||
143 | /* Symbolic links to be created for each profiling data file. */ | ||
144 | const struct gcov_link gcov_link[] = { | ||
145 | { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */ | ||
146 | { 0, NULL}, | ||
147 | }; | ||
148 | |||
149 | /* | ||
150 | * Determine whether a counter is active. Doesn't change at run-time. | ||
151 | */ | ||
152 | static int counter_active(struct gcov_info *info, unsigned int type) | ||
153 | { | ||
154 | return info->merge[type] ? 1 : 0; | ||
155 | } | ||
156 | |||
157 | /* Determine number of active counters. Based on gcc magic. */ | ||
158 | static unsigned int num_counter_active(struct gcov_info *info) | ||
159 | { | ||
160 | unsigned int i; | ||
161 | unsigned int result = 0; | ||
162 | |||
163 | for (i = 0; i < GCOV_COUNTERS; i++) { | ||
164 | if (counter_active(info, i)) | ||
165 | result++; | ||
166 | } | ||
167 | return result; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * gcov_info_reset - reset profiling data to zero | ||
172 | * @info: profiling data set | ||
173 | */ | ||
174 | void gcov_info_reset(struct gcov_info *info) | ||
175 | { | ||
176 | struct gcov_ctr_info *ci_ptr; | ||
177 | unsigned int fi_idx; | ||
178 | unsigned int ct_idx; | ||
179 | |||
180 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { | ||
181 | ci_ptr = info->functions[fi_idx]->ctrs; | ||
182 | |||
183 | for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) { | ||
184 | if (!counter_active(info, ct_idx)) | ||
185 | continue; | ||
186 | |||
187 | memset(ci_ptr->values, 0, | ||
188 | sizeof(gcov_type) * ci_ptr->num); | ||
189 | ci_ptr++; | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * gcov_info_is_compatible - check if profiling data can be added | ||
196 | * @info1: first profiling data set | ||
197 | * @info2: second profiling data set | ||
198 | * | ||
199 | * Returns non-zero if profiling data can be added, zero otherwise. | ||
200 | */ | ||
201 | int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2) | ||
202 | { | ||
203 | return (info1->stamp == info2->stamp); | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * gcov_info_add - add up profiling data | ||
208 | * @dest: profiling data set to which data is added | ||
209 | * @source: profiling data set which is added | ||
210 | * | ||
211 | * Adds profiling counts of @source to @dest. | ||
212 | */ | ||
213 | void gcov_info_add(struct gcov_info *dst, struct gcov_info *src) | ||
214 | { | ||
215 | struct gcov_ctr_info *dci_ptr; | ||
216 | struct gcov_ctr_info *sci_ptr; | ||
217 | unsigned int fi_idx; | ||
218 | unsigned int ct_idx; | ||
219 | unsigned int val_idx; | ||
220 | |||
221 | for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) { | ||
222 | dci_ptr = dst->functions[fi_idx]->ctrs; | ||
223 | sci_ptr = src->functions[fi_idx]->ctrs; | ||
224 | |||
225 | for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) { | ||
226 | if (!counter_active(src, ct_idx)) | ||
227 | continue; | ||
228 | |||
229 | for (val_idx = 0; val_idx < sci_ptr->num; val_idx++) | ||
230 | dci_ptr->values[val_idx] += | ||
231 | sci_ptr->values[val_idx]; | ||
232 | |||
233 | dci_ptr++; | ||
234 | sci_ptr++; | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * gcov_info_dup - duplicate profiling data set | ||
241 | * @info: profiling data set to duplicate | ||
242 | * | ||
243 | * Return newly allocated duplicate on success, %NULL on error. | ||
244 | */ | ||
245 | struct gcov_info *gcov_info_dup(struct gcov_info *info) | ||
246 | { | ||
247 | struct gcov_info *dup; | ||
248 | struct gcov_ctr_info *dci_ptr; /* dst counter info */ | ||
249 | struct gcov_ctr_info *sci_ptr; /* src counter info */ | ||
250 | unsigned int active; | ||
251 | unsigned int fi_idx; /* function info idx */ | ||
252 | unsigned int ct_idx; /* counter type idx */ | ||
253 | size_t fi_size; /* function info size */ | ||
254 | size_t cv_size; /* counter values size */ | ||
255 | |||
256 | dup = kmemdup(info, sizeof(*dup), GFP_KERNEL); | ||
257 | if (!dup) | ||
258 | return NULL; | ||
259 | |||
260 | dup->next = NULL; | ||
261 | dup->filename = NULL; | ||
262 | dup->functions = NULL; | ||
263 | |||
264 | dup->filename = kstrdup(info->filename, GFP_KERNEL); | ||
265 | if (!dup->filename) | ||
266 | goto err_free; | ||
267 | |||
268 | dup->functions = kcalloc(info->n_functions, | ||
269 | sizeof(struct gcov_fn_info *), GFP_KERNEL); | ||
270 | if (!dup->functions) | ||
271 | goto err_free; | ||
272 | |||
273 | active = num_counter_active(info); | ||
274 | fi_size = sizeof(struct gcov_fn_info); | ||
275 | fi_size += sizeof(struct gcov_ctr_info) * active; | ||
276 | |||
277 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { | ||
278 | dup->functions[fi_idx] = kzalloc(fi_size, GFP_KERNEL); | ||
279 | if (!dup->functions[fi_idx]) | ||
280 | goto err_free; | ||
281 | |||
282 | *(dup->functions[fi_idx]) = *(info->functions[fi_idx]); | ||
283 | |||
284 | sci_ptr = info->functions[fi_idx]->ctrs; | ||
285 | dci_ptr = dup->functions[fi_idx]->ctrs; | ||
286 | |||
287 | for (ct_idx = 0; ct_idx < active; ct_idx++) { | ||
288 | |||
289 | cv_size = sizeof(gcov_type) * sci_ptr->num; | ||
290 | |||
291 | dci_ptr->values = vmalloc(cv_size); | ||
292 | |||
293 | if (!dci_ptr->values) | ||
294 | goto err_free; | ||
295 | |||
296 | dci_ptr->num = sci_ptr->num; | ||
297 | memcpy(dci_ptr->values, sci_ptr->values, cv_size); | ||
298 | |||
299 | sci_ptr++; | ||
300 | dci_ptr++; | ||
301 | } | ||
302 | } | ||
303 | |||
304 | return dup; | ||
305 | err_free: | ||
306 | gcov_info_free(dup); | ||
307 | return NULL; | ||
308 | } | ||
309 | |||
310 | /** | ||
311 | * gcov_info_free - release memory for profiling data set duplicate | ||
312 | * @info: profiling data set duplicate to free | ||
313 | */ | ||
314 | void gcov_info_free(struct gcov_info *info) | ||
315 | { | ||
316 | unsigned int active; | ||
317 | unsigned int fi_idx; | ||
318 | unsigned int ct_idx; | ||
319 | struct gcov_ctr_info *ci_ptr; | ||
320 | |||
321 | if (!info->functions) | ||
322 | goto free_info; | ||
323 | |||
324 | active = num_counter_active(info); | ||
325 | |||
326 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { | ||
327 | if (!info->functions[fi_idx]) | ||
328 | continue; | ||
329 | |||
330 | ci_ptr = info->functions[fi_idx]->ctrs; | ||
331 | |||
332 | for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++) | ||
333 | vfree(ci_ptr->values); | ||
334 | |||
335 | kfree(info->functions[fi_idx]); | ||
336 | } | ||
337 | |||
338 | free_info: | ||
339 | kfree(info->functions); | ||
340 | kfree(info->filename); | ||
341 | kfree(info); | ||
342 | } | ||
343 | |||
344 | #define ITER_STRIDE PAGE_SIZE | ||
345 | |||
346 | /** | ||
347 | * struct gcov_iterator - specifies current file position in logical records | ||
348 | * @info: associated profiling data | ||
349 | * @buffer: buffer containing file data | ||
350 | * @size: size of buffer | ||
351 | * @pos: current position in file | ||
352 | */ | ||
353 | struct gcov_iterator { | ||
354 | struct gcov_info *info; | ||
355 | void *buffer; | ||
356 | size_t size; | ||
357 | loff_t pos; | ||
358 | }; | ||
359 | |||
360 | /** | ||
361 | * store_gcov_u32 - store 32 bit number in gcov format to buffer | ||
362 | * @buffer: target buffer or NULL | ||
363 | * @off: offset into the buffer | ||
364 | * @v: value to be stored | ||
365 | * | ||
366 | * Number format defined by gcc: numbers are recorded in the 32 bit | ||
367 | * unsigned binary form of the endianness of the machine generating the | ||
368 | * file. Returns the number of bytes stored. If @buffer is %NULL, doesn't | ||
369 | * store anything. | ||
370 | */ | ||
371 | static size_t store_gcov_u32(void *buffer, size_t off, u32 v) | ||
372 | { | ||
373 | u32 *data; | ||
374 | |||
375 | if (buffer) { | ||
376 | data = buffer + off; | ||
377 | *data = v; | ||
378 | } | ||
379 | |||
380 | return sizeof(*data); | ||
381 | } | ||
382 | |||
383 | /** | ||
384 | * store_gcov_u64 - store 64 bit number in gcov format to buffer | ||
385 | * @buffer: target buffer or NULL | ||
386 | * @off: offset into the buffer | ||
387 | * @v: value to be stored | ||
388 | * | ||
389 | * Number format defined by gcc: numbers are recorded in the 32 bit | ||
390 | * unsigned binary form of the endianness of the machine generating the | ||
391 | * file. 64 bit numbers are stored as two 32 bit numbers, the low part | ||
392 | * first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store | ||
393 | * anything. | ||
394 | */ | ||
395 | static size_t store_gcov_u64(void *buffer, size_t off, u64 v) | ||
396 | { | ||
397 | u32 *data; | ||
398 | |||
399 | if (buffer) { | ||
400 | data = buffer + off; | ||
401 | |||
402 | data[0] = (v & 0xffffffffUL); | ||
403 | data[1] = (v >> 32); | ||
404 | } | ||
405 | |||
406 | return sizeof(*data) * 2; | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * convert_to_gcda - convert profiling data set to gcda file format | ||
411 | * @buffer: the buffer to store file data or %NULL if no data should be stored | ||
412 | * @info: profiling data set to be converted | ||
413 | * | ||
414 | * Returns the number of bytes that were/would have been stored into the buffer. | ||
415 | */ | ||
416 | static size_t convert_to_gcda(char *buffer, struct gcov_info *info) | ||
417 | { | ||
418 | struct gcov_fn_info *fi_ptr; | ||
419 | struct gcov_ctr_info *ci_ptr; | ||
420 | unsigned int fi_idx; | ||
421 | unsigned int ct_idx; | ||
422 | unsigned int cv_idx; | ||
423 | size_t pos = 0; | ||
424 | |||
425 | /* File header. */ | ||
426 | pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC); | ||
427 | pos += store_gcov_u32(buffer, pos, info->version); | ||
428 | pos += store_gcov_u32(buffer, pos, info->stamp); | ||
429 | |||
430 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { | ||
431 | fi_ptr = info->functions[fi_idx]; | ||
432 | |||
433 | /* Function record. */ | ||
434 | pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION); | ||
435 | pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH); | ||
436 | pos += store_gcov_u32(buffer, pos, fi_ptr->ident); | ||
437 | pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum); | ||
438 | pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum); | ||
439 | |||
440 | ci_ptr = fi_ptr->ctrs; | ||
441 | |||
442 | for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) { | ||
443 | if (!counter_active(info, ct_idx)) | ||
444 | continue; | ||
445 | |||
446 | /* Counter record. */ | ||
447 | pos += store_gcov_u32(buffer, pos, | ||
448 | GCOV_TAG_FOR_COUNTER(ct_idx)); | ||
449 | pos += store_gcov_u32(buffer, pos, ci_ptr->num * 2); | ||
450 | |||
451 | for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) { | ||
452 | pos += store_gcov_u64(buffer, pos, | ||
453 | ci_ptr->values[cv_idx]); | ||
454 | } | ||
455 | |||
456 | ci_ptr++; | ||
457 | } | ||
458 | } | ||
459 | |||
460 | return pos; | ||
461 | } | ||
462 | |||
463 | /** | ||
464 | * gcov_iter_new - allocate and initialize profiling data iterator | ||
465 | * @info: profiling data set to be iterated | ||
466 | * | ||
467 | * Return file iterator on success, %NULL otherwise. | ||
468 | */ | ||
469 | struct gcov_iterator *gcov_iter_new(struct gcov_info *info) | ||
470 | { | ||
471 | struct gcov_iterator *iter; | ||
472 | |||
473 | iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL); | ||
474 | if (!iter) | ||
475 | goto err_free; | ||
476 | |||
477 | iter->info = info; | ||
478 | /* Dry-run to get the actual buffer size. */ | ||
479 | iter->size = convert_to_gcda(NULL, info); | ||
480 | iter->buffer = vmalloc(iter->size); | ||
481 | if (!iter->buffer) | ||
482 | goto err_free; | ||
483 | |||
484 | convert_to_gcda(iter->buffer, info); | ||
485 | |||
486 | return iter; | ||
487 | |||
488 | err_free: | ||
489 | kfree(iter); | ||
490 | return NULL; | ||
491 | } | ||
492 | |||
493 | |||
494 | /** | ||
495 | * gcov_iter_get_info - return profiling data set for given file iterator | ||
496 | * @iter: file iterator | ||
497 | */ | ||
498 | void gcov_iter_free(struct gcov_iterator *iter) | ||
499 | { | ||
500 | vfree(iter->buffer); | ||
501 | kfree(iter); | ||
502 | } | ||
503 | |||
504 | /** | ||
505 | * gcov_iter_get_info - return profiling data set for given file iterator | ||
506 | * @iter: file iterator | ||
507 | */ | ||
508 | struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter) | ||
509 | { | ||
510 | return iter->info; | ||
511 | } | ||
512 | |||
513 | /** | ||
514 | * gcov_iter_start - reset file iterator to starting position | ||
515 | * @iter: file iterator | ||
516 | */ | ||
517 | void gcov_iter_start(struct gcov_iterator *iter) | ||
518 | { | ||
519 | iter->pos = 0; | ||
520 | } | ||
521 | |||
522 | /** | ||
523 | * gcov_iter_next - advance file iterator to next logical record | ||
524 | * @iter: file iterator | ||
525 | * | ||
526 | * Return zero if new position is valid, non-zero if iterator has reached end. | ||
527 | */ | ||
528 | int gcov_iter_next(struct gcov_iterator *iter) | ||
529 | { | ||
530 | if (iter->pos < iter->size) | ||
531 | iter->pos += ITER_STRIDE; | ||
532 | |||
533 | if (iter->pos >= iter->size) | ||
534 | return -EINVAL; | ||
535 | |||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | /** | ||
540 | * gcov_iter_write - write data for current pos to seq_file | ||
541 | * @iter: file iterator | ||
542 | * @seq: seq_file handle | ||
543 | * | ||
544 | * Return zero on success, non-zero otherwise. | ||
545 | */ | ||
546 | int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq) | ||
547 | { | ||
548 | size_t len; | ||
549 | |||
550 | if (iter->pos >= iter->size) | ||
551 | return -EINVAL; | ||
552 | |||
553 | len = ITER_STRIDE; | ||
554 | if (iter->pos + len > iter->size) | ||
555 | len = iter->size - iter->pos; | ||
556 | |||
557 | seq_write(seq, iter->buffer + iter->pos, len); | ||
558 | |||
559 | return 0; | ||
560 | } | ||