xref: /openbmc/linux/kernel/gcov/clang.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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, &current_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(&current_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