// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2019 Google, Inc. * modified from kernel/gcov/gcc_4_7.c * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * LLVM uses profiling data that's deliberately similar to GCC, but has a * very different way of exporting that data. LLVM calls llvm_gcov_init() once * per module, and provides a couple of callbacks that we can use to ask for * more data. * * We care about the "writeout" callback, which in turn calls back into * compiler-rt/this module to dump all the gathered coverage data to disk: * * llvm_gcda_start_file() * llvm_gcda_emit_function() * llvm_gcda_emit_arcs() * llvm_gcda_emit_function() * llvm_gcda_emit_arcs() * [... repeats for each function ...] * llvm_gcda_summary_info() * llvm_gcda_end_file() * * This design is much more stateless and unstructured than gcc's, and is * intended to run at process exit. This forces us to keep some local state * about which module we're dealing with at the moment. On the other hand, it * also means we don't depend as much on how LLVM represents profiling data * internally. * * See LLVM's lib/Transforms/Instrumentation/GCOVProfiling.cpp for more * details on how this works, particularly GCOVProfiler::emitProfileArcs(), * GCOVProfiler::insertCounterWriteout(), and * GCOVProfiler::insertFlush().
*/
/** * gcov_info_filename - return info filename * @info: profiling data set
*/ constchar *gcov_info_filename(struct gcov_info *info)
{ return info->filename;
}
/** * gcov_info_version - return info version * @info: profiling data set
*/ unsignedint gcov_info_version(struct gcov_info *info)
{ return info->version;
}
/** * gcov_info_next - return next profiling data set * @info: profiling data set * * Returns next gcov_info following @info or first gcov_info in the chain if * @info is %NULL.
*/ struct gcov_info *gcov_info_next(struct gcov_info *info)
{ if (!info) return list_first_entry_or_null(&clang_gcov_list, struct gcov_info, head); if (list_is_last(&info->head, &clang_gcov_list)) return NULL; return list_next_entry(info, head);
}
/** * gcov_info_link - link/add profiling data set to the list * @info: profiling data set
*/ void gcov_info_link(struct gcov_info *info)
{
list_add_tail(&info->head, &clang_gcov_list);
}
/** * gcov_info_unlink - unlink/remove profiling data set from the list * @prev: previous profiling data set * @info: profiling data set
*/ void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
{ /* Generic code unlinks while iterating. */
__list_del_entry(&info->head);
}
/** * gcov_info_within_module - check if a profiling data set belongs to a module * @info: profiling data set * @mod: module * * Returns true if profiling data belongs module, false otherwise.
*/ bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
{ return within_module((unsignedlong)info->filename, mod);
}
/* Symbolic links to be created for each profiling data file. */ conststruct gcov_link gcov_link[] = {
{ OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
{ 0, NULL},
};
/** * gcov_info_reset - reset profiling data to zero * @info: profiling data set
*/ void gcov_info_reset(struct gcov_info *info)
{ struct gcov_fn_info *fn;
/** * gcov_info_is_compatible - check if profiling data can be added * @info1: first profiling data set * @info2: second profiling data set * * Returns non-zero if profiling data can be added, zero otherwise.
*/ int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
{ struct gcov_fn_info *fn_ptr1 = list_first_entry_or_null(
&info1->functions, struct gcov_fn_info, head); struct gcov_fn_info *fn_ptr2 = list_first_entry_or_null(
&info2->functions, struct gcov_fn_info, head);
if (info1->checksum != info2->checksum) returnfalse; if (!fn_ptr1) return fn_ptr1 == fn_ptr2; while (!list_is_last(&fn_ptr1->head, &info1->functions) &&
!list_is_last(&fn_ptr2->head, &info2->functions)) { if (fn_ptr1->checksum != fn_ptr2->checksum) returnfalse; if (fn_ptr1->cfg_checksum != fn_ptr2->cfg_checksum) returnfalse;
fn_ptr1 = list_next_entry(fn_ptr1, head);
fn_ptr2 = list_next_entry(fn_ptr2, head);
} return list_is_last(&fn_ptr1->head, &info1->functions) &&
list_is_last(&fn_ptr2->head, &info2->functions);
}
/** * gcov_info_add - add up profiling data * @dst: profiling data set to which data is added * @src: profiling data set which is added * * Adds profiling counts of @src to @dst.
*/ void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
{ struct gcov_fn_info *dfn_ptr; struct gcov_fn_info *sfn_ptr = list_first_entry_or_null(&src->functions, struct gcov_fn_info, head);
if (!fn_dup) goto err;
list_add_tail(&fn_dup->head, &dup->functions);
}
return dup;
err:
gcov_info_free(dup); return NULL;
}
/** * gcov_info_free - release memory for profiling data set duplicate * @info: profiling data set duplicate to free
*/ void gcov_info_free(struct gcov_info *info)
{ struct gcov_fn_info *fn, *tmp;
/** * convert_to_gcda - convert profiling data set to gcda file format * @buffer: the buffer to store file data or %NULL if no data should be stored * @info: profiling data set to be converted * * Returns the number of bytes that were/would have been stored into the buffer.
*/
size_t convert_to_gcda(char *buffer, struct gcov_info *info)
{ struct gcov_fn_info *fi_ptr;
size_t pos = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.