1e178a5beSGreg Hackmann // SPDX-License-Identifier: GPL-2.0
2e178a5beSGreg Hackmann /*
3e178a5beSGreg Hackmann * Copyright (C) 2019 Google, Inc.
4e178a5beSGreg Hackmann * modified from kernel/gcov/gcc_4_7.c
5e178a5beSGreg Hackmann *
6e178a5beSGreg Hackmann * This software is licensed under the terms of the GNU General Public
7e178a5beSGreg Hackmann * License version 2, as published by the Free Software Foundation, and
8e178a5beSGreg Hackmann * may be copied, distributed, and modified under those terms.
9e178a5beSGreg Hackmann *
10e178a5beSGreg Hackmann * This program is distributed in the hope that it will be useful,
11e178a5beSGreg Hackmann * but WITHOUT ANY WARRANTY; without even the implied warranty of
12e178a5beSGreg Hackmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13e178a5beSGreg Hackmann * GNU General Public License for more details.
14e178a5beSGreg Hackmann *
15e178a5beSGreg Hackmann *
16e178a5beSGreg Hackmann * LLVM uses profiling data that's deliberately similar to GCC, but has a
17e178a5beSGreg Hackmann * very different way of exporting that data. LLVM calls llvm_gcov_init() once
18e178a5beSGreg Hackmann * per module, and provides a couple of callbacks that we can use to ask for
19e178a5beSGreg Hackmann * more data.
20e178a5beSGreg Hackmann *
21e178a5beSGreg Hackmann * We care about the "writeout" callback, which in turn calls back into
22e178a5beSGreg Hackmann * compiler-rt/this module to dump all the gathered coverage data to disk:
23e178a5beSGreg Hackmann *
24e178a5beSGreg Hackmann * llvm_gcda_start_file()
25e178a5beSGreg Hackmann * llvm_gcda_emit_function()
26e178a5beSGreg Hackmann * llvm_gcda_emit_arcs()
27e178a5beSGreg Hackmann * llvm_gcda_emit_function()
28e178a5beSGreg Hackmann * llvm_gcda_emit_arcs()
29e178a5beSGreg Hackmann * [... repeats for each function ...]
30e178a5beSGreg Hackmann * llvm_gcda_summary_info()
31e178a5beSGreg Hackmann * llvm_gcda_end_file()
32e178a5beSGreg Hackmann *
33e178a5beSGreg Hackmann * This design is much more stateless and unstructured than gcc's, and is
34e178a5beSGreg Hackmann * intended to run at process exit. This forces us to keep some local state
35e178a5beSGreg Hackmann * about which module we're dealing with at the moment. On the other hand, it
36e178a5beSGreg Hackmann * also means we don't depend as much on how LLVM represents profiling data
37e178a5beSGreg Hackmann * internally.
38e178a5beSGreg Hackmann *
39e178a5beSGreg Hackmann * See LLVM's lib/Transforms/Instrumentation/GCOVProfiling.cpp for more
40e178a5beSGreg Hackmann * details on how this works, particularly GCOVProfiler::emitProfileArcs(),
41e178a5beSGreg Hackmann * GCOVProfiler::insertCounterWriteout(), and
42e178a5beSGreg Hackmann * GCOVProfiler::insertFlush().
43e178a5beSGreg Hackmann */
44e178a5beSGreg Hackmann
45e178a5beSGreg Hackmann #define pr_fmt(fmt) "gcov: " fmt
46e178a5beSGreg Hackmann
47e178a5beSGreg Hackmann #include <linux/kernel.h>
48e178a5beSGreg Hackmann #include <linux/list.h>
49e178a5beSGreg Hackmann #include <linux/printk.h>
50e178a5beSGreg Hackmann #include <linux/ratelimit.h>
51e178a5beSGreg Hackmann #include <linux/slab.h>
521391efa9SJohannes Berg #include <linux/mm.h>
53e178a5beSGreg Hackmann #include "gcov.h"
54e178a5beSGreg Hackmann
55e178a5beSGreg Hackmann typedef void (*llvm_gcov_callback)(void);
56e178a5beSGreg Hackmann
57e178a5beSGreg Hackmann struct gcov_info {
58e178a5beSGreg Hackmann struct list_head head;
59e178a5beSGreg Hackmann
60e178a5beSGreg Hackmann const char *filename;
61e178a5beSGreg Hackmann unsigned int version;
62e178a5beSGreg Hackmann u32 checksum;
63e178a5beSGreg Hackmann
64e178a5beSGreg Hackmann struct list_head functions;
65e178a5beSGreg Hackmann };
66e178a5beSGreg Hackmann
67e178a5beSGreg Hackmann struct gcov_fn_info {
68e178a5beSGreg Hackmann struct list_head head;
69e178a5beSGreg Hackmann
70e178a5beSGreg Hackmann u32 ident;
71e178a5beSGreg Hackmann u32 checksum;
72e178a5beSGreg Hackmann u32 cfg_checksum;
73e178a5beSGreg Hackmann
74e178a5beSGreg Hackmann u32 num_counters;
75e178a5beSGreg Hackmann u64 *counters;
76e178a5beSGreg Hackmann };
77e178a5beSGreg Hackmann
78e178a5beSGreg Hackmann static struct gcov_info *current_info;
79e178a5beSGreg Hackmann
80e178a5beSGreg Hackmann static LIST_HEAD(clang_gcov_list);
81e178a5beSGreg Hackmann
llvm_gcov_init(llvm_gcov_callback writeout,llvm_gcov_callback flush)82e178a5beSGreg Hackmann void llvm_gcov_init(llvm_gcov_callback writeout, llvm_gcov_callback flush)
83e178a5beSGreg Hackmann {
84e178a5beSGreg Hackmann struct gcov_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
85e178a5beSGreg Hackmann
86e178a5beSGreg Hackmann if (!info)
87e178a5beSGreg Hackmann return;
88e178a5beSGreg Hackmann
89e178a5beSGreg Hackmann INIT_LIST_HEAD(&info->head);
90e178a5beSGreg Hackmann INIT_LIST_HEAD(&info->functions);
91e178a5beSGreg Hackmann
92e178a5beSGreg Hackmann mutex_lock(&gcov_lock);
93e178a5beSGreg Hackmann
94e178a5beSGreg Hackmann list_add_tail(&info->head, &clang_gcov_list);
95e178a5beSGreg Hackmann current_info = info;
96e178a5beSGreg Hackmann writeout();
97e178a5beSGreg Hackmann current_info = NULL;
98e178a5beSGreg Hackmann if (gcov_events_enabled)
99e178a5beSGreg Hackmann gcov_event(GCOV_ADD, info);
100e178a5beSGreg Hackmann
101e178a5beSGreg Hackmann mutex_unlock(&gcov_lock);
102e178a5beSGreg Hackmann }
103e178a5beSGreg Hackmann EXPORT_SYMBOL(llvm_gcov_init);
104e178a5beSGreg Hackmann
llvm_gcda_start_file(const char * orig_filename,u32 version,u32 checksum)10560bcf728SNick Desaulniers void llvm_gcda_start_file(const char *orig_filename, u32 version, u32 checksum)
10660bcf728SNick Desaulniers {
10760bcf728SNick Desaulniers current_info->filename = orig_filename;
10860bcf728SNick Desaulniers current_info->version = version;
10960bcf728SNick Desaulniers current_info->checksum = checksum;
11060bcf728SNick Desaulniers }
11160bcf728SNick Desaulniers EXPORT_SYMBOL(llvm_gcda_start_file);
112e178a5beSGreg Hackmann
llvm_gcda_emit_function(u32 ident,u32 func_checksum,u32 cfg_checksum)1139562fd13SNick Desaulniers void llvm_gcda_emit_function(u32 ident, u32 func_checksum, u32 cfg_checksum)
11460bcf728SNick Desaulniers {
11560bcf728SNick Desaulniers struct gcov_fn_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
11660bcf728SNick Desaulniers
11760bcf728SNick Desaulniers if (!info)
11860bcf728SNick Desaulniers return;
11960bcf728SNick Desaulniers
12060bcf728SNick Desaulniers INIT_LIST_HEAD(&info->head);
12160bcf728SNick Desaulniers info->ident = ident;
12260bcf728SNick Desaulniers info->checksum = func_checksum;
12360bcf728SNick Desaulniers info->cfg_checksum = cfg_checksum;
12460bcf728SNick Desaulniers list_add_tail(&info->head, ¤t_info->functions);
12560bcf728SNick Desaulniers }
1269562fd13SNick Desaulniers EXPORT_SYMBOL(llvm_gcda_emit_function);
127e178a5beSGreg Hackmann
llvm_gcda_emit_arcs(u32 num_counters,u64 * counters)128e178a5beSGreg Hackmann void llvm_gcda_emit_arcs(u32 num_counters, u64 *counters)
129e178a5beSGreg Hackmann {
130e178a5beSGreg Hackmann struct gcov_fn_info *info = list_last_entry(¤t_info->functions,
131e178a5beSGreg Hackmann struct gcov_fn_info, head);
132e178a5beSGreg Hackmann
133e178a5beSGreg Hackmann info->num_counters = num_counters;
134e178a5beSGreg Hackmann info->counters = counters;
135e178a5beSGreg Hackmann }
136e178a5beSGreg Hackmann EXPORT_SYMBOL(llvm_gcda_emit_arcs);
137e178a5beSGreg Hackmann
llvm_gcda_summary_info(void)138e178a5beSGreg Hackmann void llvm_gcda_summary_info(void)
139e178a5beSGreg Hackmann {
140e178a5beSGreg Hackmann }
141e178a5beSGreg Hackmann EXPORT_SYMBOL(llvm_gcda_summary_info);
142e178a5beSGreg Hackmann
llvm_gcda_end_file(void)143e178a5beSGreg Hackmann void llvm_gcda_end_file(void)
144e178a5beSGreg Hackmann {
145e178a5beSGreg Hackmann }
146e178a5beSGreg Hackmann EXPORT_SYMBOL(llvm_gcda_end_file);
147e178a5beSGreg Hackmann
148e178a5beSGreg Hackmann /**
149e178a5beSGreg Hackmann * gcov_info_filename - return info filename
150e178a5beSGreg Hackmann * @info: profiling data set
151e178a5beSGreg Hackmann */
gcov_info_filename(struct gcov_info * info)152e178a5beSGreg Hackmann const char *gcov_info_filename(struct gcov_info *info)
153e178a5beSGreg Hackmann {
154e178a5beSGreg Hackmann return info->filename;
155e178a5beSGreg Hackmann }
156e178a5beSGreg Hackmann
157e178a5beSGreg Hackmann /**
158e178a5beSGreg Hackmann * gcov_info_version - return info version
159e178a5beSGreg Hackmann * @info: profiling data set
160e178a5beSGreg Hackmann */
gcov_info_version(struct gcov_info * info)161e178a5beSGreg Hackmann unsigned int gcov_info_version(struct gcov_info *info)
162e178a5beSGreg Hackmann {
163e178a5beSGreg Hackmann return info->version;
164e178a5beSGreg Hackmann }
165e178a5beSGreg Hackmann
166e178a5beSGreg Hackmann /**
167e178a5beSGreg Hackmann * gcov_info_next - return next profiling data set
168e178a5beSGreg Hackmann * @info: profiling data set
169e178a5beSGreg Hackmann *
170e178a5beSGreg Hackmann * Returns next gcov_info following @info or first gcov_info in the chain if
171e178a5beSGreg Hackmann * @info is %NULL.
172e178a5beSGreg Hackmann */
gcov_info_next(struct gcov_info * info)173e178a5beSGreg Hackmann struct gcov_info *gcov_info_next(struct gcov_info *info)
174e178a5beSGreg Hackmann {
175e178a5beSGreg Hackmann if (!info)
176e178a5beSGreg Hackmann return list_first_entry_or_null(&clang_gcov_list,
177e178a5beSGreg Hackmann struct gcov_info, head);
178e178a5beSGreg Hackmann if (list_is_last(&info->head, &clang_gcov_list))
179e178a5beSGreg Hackmann return NULL;
180e178a5beSGreg Hackmann return list_next_entry(info, head);
181e178a5beSGreg Hackmann }
182e178a5beSGreg Hackmann
183e178a5beSGreg Hackmann /**
184e178a5beSGreg Hackmann * gcov_info_link - link/add profiling data set to the list
185e178a5beSGreg Hackmann * @info: profiling data set
186e178a5beSGreg Hackmann */
gcov_info_link(struct gcov_info * info)187e178a5beSGreg Hackmann void gcov_info_link(struct gcov_info *info)
188e178a5beSGreg Hackmann {
189e178a5beSGreg Hackmann list_add_tail(&info->head, &clang_gcov_list);
190e178a5beSGreg Hackmann }
191e178a5beSGreg Hackmann
192e178a5beSGreg Hackmann /**
193e178a5beSGreg Hackmann * gcov_info_unlink - unlink/remove profiling data set from the list
194e178a5beSGreg Hackmann * @prev: previous profiling data set
195e178a5beSGreg Hackmann * @info: profiling data set
196e178a5beSGreg Hackmann */
gcov_info_unlink(struct gcov_info * prev,struct gcov_info * info)197e178a5beSGreg Hackmann void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
198e178a5beSGreg Hackmann {
199e178a5beSGreg Hackmann /* Generic code unlinks while iterating. */
200e178a5beSGreg Hackmann __list_del_entry(&info->head);
201e178a5beSGreg Hackmann }
202e178a5beSGreg Hackmann
203e178a5beSGreg Hackmann /**
204e178a5beSGreg Hackmann * gcov_info_within_module - check if a profiling data set belongs to a module
205e178a5beSGreg Hackmann * @info: profiling data set
206e178a5beSGreg Hackmann * @mod: module
207e178a5beSGreg Hackmann *
208e178a5beSGreg Hackmann * Returns true if profiling data belongs module, false otherwise.
209e178a5beSGreg Hackmann */
gcov_info_within_module(struct gcov_info * info,struct module * mod)210e178a5beSGreg Hackmann bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
211e178a5beSGreg Hackmann {
212e178a5beSGreg Hackmann return within_module((unsigned long)info->filename, mod);
213e178a5beSGreg Hackmann }
214e178a5beSGreg Hackmann
215e178a5beSGreg Hackmann /* Symbolic links to be created for each profiling data file. */
216e178a5beSGreg Hackmann const struct gcov_link gcov_link[] = {
217e178a5beSGreg Hackmann { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
218e178a5beSGreg Hackmann { 0, NULL},
219e178a5beSGreg Hackmann };
220e178a5beSGreg Hackmann
221e178a5beSGreg Hackmann /**
222e178a5beSGreg Hackmann * gcov_info_reset - reset profiling data to zero
223e178a5beSGreg Hackmann * @info: profiling data set
224e178a5beSGreg Hackmann */
gcov_info_reset(struct gcov_info * info)225e178a5beSGreg Hackmann void gcov_info_reset(struct gcov_info *info)
226e178a5beSGreg Hackmann {
227e178a5beSGreg Hackmann struct gcov_fn_info *fn;
228e178a5beSGreg Hackmann
229e178a5beSGreg Hackmann list_for_each_entry(fn, &info->functions, head)
230e178a5beSGreg Hackmann memset(fn->counters, 0,
231e178a5beSGreg Hackmann sizeof(fn->counters[0]) * fn->num_counters);
232e178a5beSGreg Hackmann }
233e178a5beSGreg Hackmann
234e178a5beSGreg Hackmann /**
235e178a5beSGreg Hackmann * gcov_info_is_compatible - check if profiling data can be added
236e178a5beSGreg Hackmann * @info1: first profiling data set
237e178a5beSGreg Hackmann * @info2: second profiling data set
238e178a5beSGreg Hackmann *
239e178a5beSGreg Hackmann * Returns non-zero if profiling data can be added, zero otherwise.
240e178a5beSGreg Hackmann */
gcov_info_is_compatible(struct gcov_info * info1,struct gcov_info * info2)241e178a5beSGreg Hackmann int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
242e178a5beSGreg Hackmann {
243e178a5beSGreg Hackmann struct gcov_fn_info *fn_ptr1 = list_first_entry_or_null(
244e178a5beSGreg Hackmann &info1->functions, struct gcov_fn_info, head);
245e178a5beSGreg Hackmann struct gcov_fn_info *fn_ptr2 = list_first_entry_or_null(
246e178a5beSGreg Hackmann &info2->functions, struct gcov_fn_info, head);
247e178a5beSGreg Hackmann
248e178a5beSGreg Hackmann if (info1->checksum != info2->checksum)
249e178a5beSGreg Hackmann return false;
250e178a5beSGreg Hackmann if (!fn_ptr1)
251e178a5beSGreg Hackmann return fn_ptr1 == fn_ptr2;
252e178a5beSGreg Hackmann while (!list_is_last(&fn_ptr1->head, &info1->functions) &&
253e178a5beSGreg Hackmann !list_is_last(&fn_ptr2->head, &info2->functions)) {
254e178a5beSGreg Hackmann if (fn_ptr1->checksum != fn_ptr2->checksum)
255e178a5beSGreg Hackmann return false;
2569562fd13SNick Desaulniers if (fn_ptr1->cfg_checksum != fn_ptr2->cfg_checksum)
2579562fd13SNick Desaulniers return false;
258e178a5beSGreg Hackmann fn_ptr1 = list_next_entry(fn_ptr1, head);
259e178a5beSGreg Hackmann fn_ptr2 = list_next_entry(fn_ptr2, head);
260e178a5beSGreg Hackmann }
261e178a5beSGreg Hackmann return list_is_last(&fn_ptr1->head, &info1->functions) &&
262e178a5beSGreg Hackmann list_is_last(&fn_ptr2->head, &info2->functions);
263e178a5beSGreg Hackmann }
264e178a5beSGreg Hackmann
265e178a5beSGreg Hackmann /**
266e178a5beSGreg Hackmann * gcov_info_add - add up profiling data
267e178a5beSGreg Hackmann * @dest: profiling data set to which data is added
268e178a5beSGreg Hackmann * @source: profiling data set which is added
269e178a5beSGreg Hackmann *
270e178a5beSGreg Hackmann * Adds profiling counts of @source to @dest.
271e178a5beSGreg Hackmann */
gcov_info_add(struct gcov_info * dst,struct gcov_info * src)272e178a5beSGreg Hackmann void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
273e178a5beSGreg Hackmann {
274e178a5beSGreg Hackmann struct gcov_fn_info *dfn_ptr;
275e178a5beSGreg Hackmann struct gcov_fn_info *sfn_ptr = list_first_entry_or_null(&src->functions,
276e178a5beSGreg Hackmann struct gcov_fn_info, head);
277e178a5beSGreg Hackmann
278e178a5beSGreg Hackmann list_for_each_entry(dfn_ptr, &dst->functions, head) {
279e178a5beSGreg Hackmann u32 i;
280e178a5beSGreg Hackmann
281e178a5beSGreg Hackmann for (i = 0; i < sfn_ptr->num_counters; i++)
282e178a5beSGreg Hackmann dfn_ptr->counters[i] += sfn_ptr->counters[i];
283*a6f810efSMukesh Ojha
284*a6f810efSMukesh Ojha sfn_ptr = list_next_entry(sfn_ptr, head);
285e178a5beSGreg Hackmann }
286e178a5beSGreg Hackmann }
287e178a5beSGreg Hackmann
gcov_fn_info_dup(struct gcov_fn_info * fn)28860bcf728SNick Desaulniers static struct gcov_fn_info *gcov_fn_info_dup(struct gcov_fn_info *fn)
28960bcf728SNick Desaulniers {
29060bcf728SNick Desaulniers size_t cv_size; /* counter values size */
29160bcf728SNick Desaulniers struct gcov_fn_info *fn_dup = kmemdup(fn, sizeof(*fn),
29260bcf728SNick Desaulniers GFP_KERNEL);
29360bcf728SNick Desaulniers if (!fn_dup)
29460bcf728SNick Desaulniers return NULL;
29560bcf728SNick Desaulniers INIT_LIST_HEAD(&fn_dup->head);
29660bcf728SNick Desaulniers
29760bcf728SNick Desaulniers cv_size = fn->num_counters * sizeof(fn->counters[0]);
2981391efa9SJohannes Berg fn_dup->counters = kvmalloc(cv_size, GFP_KERNEL);
29960bcf728SNick Desaulniers if (!fn_dup->counters) {
30060bcf728SNick Desaulniers kfree(fn_dup);
30160bcf728SNick Desaulniers return NULL;
30260bcf728SNick Desaulniers }
30360bcf728SNick Desaulniers
30460bcf728SNick Desaulniers memcpy(fn_dup->counters, fn->counters, cv_size);
30560bcf728SNick Desaulniers
30660bcf728SNick Desaulniers return fn_dup;
30760bcf728SNick Desaulniers }
308e178a5beSGreg Hackmann
309e178a5beSGreg Hackmann /**
310e178a5beSGreg Hackmann * gcov_info_dup - duplicate profiling data set
311e178a5beSGreg Hackmann * @info: profiling data set to duplicate
312e178a5beSGreg Hackmann *
313e178a5beSGreg Hackmann * Return newly allocated duplicate on success, %NULL on error.
314e178a5beSGreg Hackmann */
gcov_info_dup(struct gcov_info * info)315e178a5beSGreg Hackmann struct gcov_info *gcov_info_dup(struct gcov_info *info)
316e178a5beSGreg Hackmann {
317e178a5beSGreg Hackmann struct gcov_info *dup;
318e178a5beSGreg Hackmann struct gcov_fn_info *fn;
319e178a5beSGreg Hackmann
320e178a5beSGreg Hackmann dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
321e178a5beSGreg Hackmann if (!dup)
322e178a5beSGreg Hackmann return NULL;
323e178a5beSGreg Hackmann INIT_LIST_HEAD(&dup->head);
324e178a5beSGreg Hackmann INIT_LIST_HEAD(&dup->functions);
325e178a5beSGreg Hackmann dup->filename = kstrdup(info->filename, GFP_KERNEL);
326e178a5beSGreg Hackmann if (!dup->filename)
327e178a5beSGreg Hackmann goto err;
328e178a5beSGreg Hackmann
329e178a5beSGreg Hackmann list_for_each_entry(fn, &info->functions, head) {
330e178a5beSGreg Hackmann struct gcov_fn_info *fn_dup = gcov_fn_info_dup(fn);
331e178a5beSGreg Hackmann
332e178a5beSGreg Hackmann if (!fn_dup)
333e178a5beSGreg Hackmann goto err;
334e178a5beSGreg Hackmann list_add_tail(&fn_dup->head, &dup->functions);
335e178a5beSGreg Hackmann }
336e178a5beSGreg Hackmann
337e178a5beSGreg Hackmann return dup;
338e178a5beSGreg Hackmann
339e178a5beSGreg Hackmann err:
340e178a5beSGreg Hackmann gcov_info_free(dup);
341e178a5beSGreg Hackmann return NULL;
342e178a5beSGreg Hackmann }
343e178a5beSGreg Hackmann
344e178a5beSGreg Hackmann /**
345e178a5beSGreg Hackmann * gcov_info_free - release memory for profiling data set duplicate
346e178a5beSGreg Hackmann * @info: profiling data set duplicate to free
347e178a5beSGreg Hackmann */
gcov_info_free(struct gcov_info * info)34860bcf728SNick Desaulniers void gcov_info_free(struct gcov_info *info)
34960bcf728SNick Desaulniers {
35060bcf728SNick Desaulniers struct gcov_fn_info *fn, *tmp;
35160bcf728SNick Desaulniers
35260bcf728SNick Desaulniers list_for_each_entry_safe(fn, tmp, &info->functions, head) {
3531391efa9SJohannes Berg kvfree(fn->counters);
35460bcf728SNick Desaulniers list_del(&fn->head);
35560bcf728SNick Desaulniers kfree(fn);
35660bcf728SNick Desaulniers }
35760bcf728SNick Desaulniers kfree(info->filename);
35860bcf728SNick Desaulniers kfree(info);
35960bcf728SNick Desaulniers }
360e178a5beSGreg Hackmann
361e178a5beSGreg Hackmann /**
362e178a5beSGreg Hackmann * convert_to_gcda - convert profiling data set to gcda file format
363e178a5beSGreg Hackmann * @buffer: the buffer to store file data or %NULL if no data should be stored
364e178a5beSGreg Hackmann * @info: profiling data set to be converted
365e178a5beSGreg Hackmann *
366e178a5beSGreg Hackmann * Returns the number of bytes that were/would have been stored into the buffer.
367e178a5beSGreg Hackmann */
convert_to_gcda(char * buffer,struct gcov_info * info)3687a1d55b9SJohannes Berg size_t convert_to_gcda(char *buffer, struct gcov_info *info)
369e178a5beSGreg Hackmann {
370e178a5beSGreg Hackmann struct gcov_fn_info *fi_ptr;
371e178a5beSGreg Hackmann size_t pos = 0;
372e178a5beSGreg Hackmann
373e178a5beSGreg Hackmann /* File header. */
374e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
375e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, info->version);
376e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, info->checksum);
377e178a5beSGreg Hackmann
378e178a5beSGreg Hackmann list_for_each_entry(fi_ptr, &info->functions, head) {
379e178a5beSGreg Hackmann u32 i;
380e178a5beSGreg Hackmann
381e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
3829562fd13SNick Desaulniers pos += store_gcov_u32(buffer, pos, 3);
383e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
384e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, fi_ptr->checksum);
385e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
386e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, GCOV_TAG_COUNTER_BASE);
387e178a5beSGreg Hackmann pos += store_gcov_u32(buffer, pos, fi_ptr->num_counters * 2);
388e178a5beSGreg Hackmann for (i = 0; i < fi_ptr->num_counters; i++)
389e178a5beSGreg Hackmann pos += store_gcov_u64(buffer, pos, fi_ptr->counters[i]);
390e178a5beSGreg Hackmann }
391e178a5beSGreg Hackmann
392e178a5beSGreg Hackmann return pos;
393e178a5beSGreg Hackmann }
394