| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * This code provides functions to handle gcc and llvm coverage info format
- * introduced with gcc 4.7 and llvm clang compiled with -coverage option.
- *
- * This file is based heavily on gcc_4_7.c and clang.c file.
- * - https://github.com/torvalds/linux/blob/master/kernel/gcov/gcc_4_7.c
- * - https://github.com/torvalds/linux/commits/master/kernel/gcov/clang.c
- */
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #if BITS_PER_LONG >= 64
- typedef long gcov_type;
- #else
- typedef long long gcov_type;
- #endif
- typedef uint64_t u64;
- typedef uint32_t u32;
- /*
- * Profiling data types used for gcc 3.4 and above - these are defined by
- * gcc and need to be kept as close to the original definition as possible to
- * remain compatible.
- */
- #define GCOV_DATA_MAGIC ((unsigned int) 0x67636461)
- #define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000)
- #define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000)
- #define GCOV_TAG_FOR_COUNTER(count) \
- (GCOV_TAG_COUNTER_BASE + ((unsigned int) (count) << 17))
- #ifndef __clang__
- /*
- * GCC -coverage support
- *
- * Data structures and runtime functions for handling coverage information
- * generated by GCC with -coverage option. Supports GCC 4.7+ format.
- * Based on Linux kernel's gcov/gcc_4_7.c implementation.
- */
- #if (__GNUC__ >= 15)
- #define GCOV_COUNTERS 10
- #elif (__GNUC__ >= 14)
- #define GCOV_COUNTERS 9
- #elif (__GNUC__ >= 10)
- #define GCOV_COUNTERS 8
- #elif (__GNUC__ >= 7)
- #define GCOV_COUNTERS 9
- #elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
- #define GCOV_COUNTERS 10
- #else
- #define GCOV_COUNTERS 9
- #endif
- #define GCOV_TAG_FUNCTION_LENGTH 3
- /* Since GCC 12.1 sizes are in BYTES and not in WORDS (4B). */
- #if (__GNUC__ >= 12)
- #define GCOV_UNIT_SIZE 4
- #else
- #define GCOV_UNIT_SIZE 1
- #endif
- /**
- * struct gcov_ctr_info - information about counters for a single function
- * @num: number of counter values for this type
- * @values: array of counter values for this type
- *
- * This data is generated by gcc during compilation and doesn't change
- * at run-time with the exception of the values array.
- */
- struct gcov_ctr_info {
- unsigned int num;
- gcov_type *values;
- };
- /**
- * struct gcov_fn_info - profiling metadata per function for GCC
- * @key: comdat key for detecting selected comdat function
- * @ident: unique identifier of the function
- * @lineno_checksum: function line number checksum
- * @cfg_checksum: control flow graph checksum
- * @ctrs: instrumented counters (flexible array member)
- *
- * This data is generated by gcc during compilation and doesn't change
- * at run-time. Uses trailing array idiom for counters.
- */
- struct gcov_fn_info {
- const struct gcov_info *key;
- unsigned int ident;
- unsigned int lineno_checksum;
- unsigned int cfg_checksum;
- struct gcov_ctr_info ctrs[];
- };
- /**
- * struct gcov_info - coverage info per object file
- * @version: gcov version magic indicating the gcc version used for compilation
- * @next: list head for a singly-linked list
- * @stamp: uniquifying time stamp
- * @checksum: unique object checksum
- * @filename: name of the associated gcov data file
- * @merge: merge functions (null for unused counter type)
- * @n_functions: number of instrumented functions
- * @functions: pointer to pointers to function information
- *
- * This data is generated by gcc during compilation and doesn't change
- * at run-time with the exception of the next pointer.
- */
- struct gcov_info {
- unsigned int version;
- struct gcov_info *next;
- unsigned int stamp;
- /* Since GCC 12.1 a checksum field is added. */
- #if (__GNUC__ >= 12)
- unsigned int checksum;
- #endif
- const char *filename;
- void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
- unsigned int n_functions;
- struct gcov_fn_info **functions;
- };
- #else
- /*
- * LLVM/Clang -coverage support
- *
- * Data structures and runtime callbacks for handling coverage information
- * generated by LLVM/Clang with -coverage option. Based on Linux kernel's
- * gcov/clang.c implementation.
- */
- /**
- * llvm_gcov_callback - LLVM coverage callback function type
- *
- * Callback function type used by LLVM for coverage data writeout and flush operations.
- */
- typedef void (*llvm_gcov_callback)(void);
- /**
- * struct gcov_fn_info - profiling metadata per function for LLVM/Clang
- * @next: pointer to next function info in linked list
- * @ident: unique identifier of the function
- * @checksum: function checksum for validation
- * @cfg_checksum: control flow graph checksum for validation
- * @num_counters: number of coverage counters for this function
- * @counters: array of 64-bit counter values
- *
- * This structure stores coverage information for a single function when
- * compiled with LLVM/Clang using the -coverage option. The data is generated
- * during compilation and updated at runtime by LLVM's coverage instrumentation.
- */
- struct gcov_fn_info {
- struct gcov_fn_info *next;
- u32 ident;
- u32 checksum;
- u32 cfg_checksum;
- u32 num_counters;
- u64 *counters;
- };
- /**
- * struct gcov_info - coverage info per object file for LLVM/Clang
- * @next: pointer to next gcov_info in linked list
- * @filename: name of the associated gcov data file
- * @version: gcov version magic indicating the compiler version used
- * @checksum: unique object checksum
- * @functions: linked list of function-level coverage information
- *
- * This structure stores coverage information for an entire object file when
- * compiled with LLVM/Clang. It serves as the container for all functions'
- * coverage data within that compilation unit. The data is generated during
- * compilation and the counters are updated at runtime.
- */
- struct gcov_info {
- struct gcov_info *next;
- const char *filename;
- unsigned int version;
- u32 checksum;
- struct gcov_fn_info *functions;
- };
- /* Pointer to currently active gcov_info during LLVM gcov information initialization */
- static struct gcov_info *current_info;
- /* Pointer to currently active gcov_fn_info during function processing (also serves as tail pointer) */
- static struct gcov_fn_info *current_function;
- #endif
- /**
- * struct gcov_data - analyzed coverage data
- * @next: list head for a singly-linked list
- * @filename: name of the associated gcov data file
- * @buffer: buffer pointer to save gcda data via convert_to_gcda
- * @size: buffer size in bytes
- *
- */
- struct gcov_data {
- struct gcov_data *next;
- const char *filename;
- char *buffer;
- size_t size;
- };
- /* Head of linked list containing all gcov_info from compiled object files */
- struct gcov_info *gcov_info_head = NULL;
- /* Head of linked list storing collected coverage data after gcov_collect(0) */
- struct gcov_data *gcov_data_head = NULL;
- /**
- * store_gcov_u32 - store 32 bit number in gcov format to buffer
- * @buffer: target buffer or NULL
- * @off: offset into the buffer
- * @v: value to be stored
- *
- * Number format defined by gcc: numbers are recorded in the 32 bit
- * unsigned binary form of the endianness of the machine generating the
- * file. Returns the number of bytes stored. If @buffer is %NULL, doesn't
- * store anything.
- */
- size_t store_gcov_u32(void *buffer, size_t off, u32 v)
- {
- u32 *data;
- if (buffer) {
- data = buffer + off;
- *data = v;
- }
- return sizeof(*data);
- }
- /**
- * store_gcov_u64 - store 64 bit number in gcov format to buffer
- * @buffer: target buffer or NULL
- * @off: offset into the buffer
- * @v: value to be stored
- *
- * Number format defined by gcc: numbers are recorded in the 32 bit
- * unsigned binary form of the endianness of the machine generating the
- * file. 64 bit numbers are stored as two 32 bit numbers, the low part
- * first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
- * anything.
- */
- size_t store_gcov_u64(void *buffer, size_t off, u64 v)
- {
- u32 *data;
- if (buffer) {
- data = buffer + off;
- data[0] = (v & 0xffffffffUL);
- data[1] = (v >> 32);
- }
- return sizeof(*data) * 2;
- }
- /**
- * gcov_data_link - link/add coverage data set to the global list
- * @data: pointer to gcov_data structure to be linked
- *
- * Adds the given coverage data structure to the head of the global
- * gcov_data_head linked list. This function is called after collecting
- * and converting coverage data from gcov_info structures.
- */
- void gcov_data_link(struct gcov_data *data)
- {
- data->next = gcov_data_head;
- gcov_data_head = data;
- }
- #ifndef __clang__
- /*
- * GCC-specific stub functions
- *
- * These functions are called by GCC-generated profiling code but are
- * not used in this bare-metal implementation. They are provided as
- * stubs to satisfy linker references.
- */
- /**
- * gcov_info_link - link/add coverage info set to the list
- * @info: coverage info set
- */
- void gcov_info_link(struct gcov_info *info)
- {
- info->next = gcov_info_head;
- gcov_info_head = info;
- }
- /**
- * gcov_info_unlink - unlink/remove coverage info set from the list
- * @prev: previous coverage info set
- * @info: coverage info set
- */
- void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
- {
- if (prev)
- prev->next = info->next;
- else
- gcov_info_head = info->next;
- }
- /*
- * Determine whether a counter is active. Doesn't change at run-time.
- */
- static int counter_active(struct gcov_info *info, unsigned int type)
- {
- return info->merge[type] ? 1 : 0;
- }
- /*
- * These functions may be referenced by gcc-generated profiling code but serve
- * no function for kernel profiling.
- */
- void __gcov_flush(void)
- {
- /* Unused. */
- }
- void __gcov_merge_add(gcov_type *counters, unsigned int n_counters)
- {
- /* Unused. */
- }
- void __gcov_merge_single(gcov_type *counters, unsigned int n_counters)
- {
- /* Unused. */
- }
- void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters)
- {
- /* Unused. */
- }
- void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters)
- {
- /* Unused. */
- }
- void __gcov_merge_time_profile(gcov_type *counters, unsigned int n_counters)
- {
- /* Unused. */
- }
- void __gcov_merge_icall_topn(gcov_type *counters, unsigned int n_counters)
- {
- /* Unused. */
- }
- void __gcov_exit(void)
- {
- /* Unused. */
- }
- /*
- * __gcov_init is called by gcc-generated constructor code for each object
- * file compiled with -fprofile-arcs.
- */
- void __gcov_init(struct gcov_info *info)
- {
- if (info) {
- gcov_info_link(info);
- }
- }
- /**
- * convert_to_gcda - convert coverage info set to gcda file format
- * @buffer: the buffer to store file data or %NULL if no data should be stored
- * @info: coverage info 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;
- struct gcov_ctr_info *ci_ptr;
- unsigned int fi_idx;
- unsigned int ct_idx;
- unsigned int cv_idx;
- size_t pos = 0;
- /* File header. */
- pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
- pos += store_gcov_u32(buffer, pos, info->version);
- pos += store_gcov_u32(buffer, pos, info->stamp);
- #if (__GNUC__ >= 12)
- /* Use zero as checksum of the compilation unit. */
- pos += store_gcov_u32(buffer, pos, 0);
- #endif
- for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
- fi_ptr = info->functions[fi_idx];
- /* Function record. */
- pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
- pos += store_gcov_u32(buffer, pos,
- GCOV_TAG_FUNCTION_LENGTH * GCOV_UNIT_SIZE);
- pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
- pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum);
- pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
- ci_ptr = fi_ptr->ctrs;
- for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
- if (!counter_active(info, ct_idx))
- continue;
- /* Counter record. */
- pos += store_gcov_u32(buffer, pos,
- GCOV_TAG_FOR_COUNTER(ct_idx));
- pos += store_gcov_u32(buffer, pos,
- ci_ptr->num * 2 * GCOV_UNIT_SIZE);
- for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
- pos += store_gcov_u64(buffer, pos,
- ci_ptr->values[cv_idx]);
- }
- ci_ptr++;
- }
- }
- return pos;
- }
- #else
- /*
- * LLVM/Clang -coverage runtime stub functions
- *
- * These functions are called by Clang-generated profiling code when
- * compiled with -coverage option. They are provided as stubs to
- * satisfy linker references in this bare-metal implementation.
- */
- /**
- * llvm_gcov_init - initialize LLVM coverage runtime
- * @writeout: callback for writing coverage data
- * @flush: callback for flushing coverage data
- *
- * Called by Clang runtime to initialize coverage data structures.
- */
- void llvm_gcov_init(llvm_gcov_callback writeout, llvm_gcov_callback flush)
- {
- struct gcov_info *info = (struct gcov_info *)malloc(sizeof(*info));
- if (!info) {
- return;
- }
- info->next = NULL;
- info->functions = NULL;
- current_info = info;
- current_function = NULL;
- if (gcov_info_head == NULL) {
- gcov_info_head = current_info;
- } else {
- gcov_info_head->next = current_info;
- }
- writeout();
- current_info = NULL;
- }
- /**
- * llvm_gcda_start_file - start coverage data for a source file
- * @orig_filename: original source file name
- * @version: gcov version magic
- * @checksum: compilation unit checksum
- *
- * Called by Clang runtime at the start of coverage data emission.
- */
- void llvm_gcda_start_file(const char *orig_filename, u32 version, u32 checksum)
- {
- if (!current_info) {
- return;
- }
- current_info->filename = orig_filename;
- current_info->version = version;
- current_info->checksum = checksum;
- }
- /**
- * llvm_gcda_emit_function - emit coverage data for a function
- * @ident: function unique identifier
- * @func_checksum: function checksum
- * @cfg_checksum: control flow graph checksum
- *
- * Called by Clang runtime to emit coverage data for each function.
- */
- void llvm_gcda_emit_function(u32 ident, u32 func_checksum, u32 cfg_checksum)
- {
- struct gcov_fn_info *info = (struct gcov_fn_info *)malloc(sizeof(*info));
- if (!info) {
- return;
- }
- info->ident = ident;
- info->checksum = func_checksum;
- info->cfg_checksum = cfg_checksum;
- info->next = NULL;
- if (current_info->functions == NULL) {
- current_info->functions = info;
- } else {
- current_function->next = info;
- }
- current_function = info;
- }
- /**
- * llvm_gcda_emit_arcs - emit coverage counters for function arcs
- * @num_counters: number of arc counters
- * @counters: array of 64-bit counter values
- *
- * Called by Clang runtime to emit arc coverage counters.
- */
- void llvm_gcda_emit_arcs(u32 num_counters, u64 *counters)
- {
- if (current_function) {
- current_function->num_counters = num_counters;
- current_function->counters = counters;
- }
- }
- /**
- * llvm_gcda_summary_info - emit summary information
- *
- * Stub function for coverage summary (not used in this implementation).
- */
- void llvm_gcda_summary_info(void)
- {
- }
- /**
- * llvm_gcda_end_file - end coverage data emission for a file
- *
- * Called by Clang runtime at the end of coverage data emission.
- */
- void llvm_gcda_end_file(void)
- {
- }
- /**
- * 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;
- /* File header. */
- pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
- pos += store_gcov_u32(buffer, pos, info->version);
- pos += store_gcov_u32(buffer, pos, info->checksum);
- for (fi_ptr = info->functions; fi_ptr != NULL; fi_ptr = fi_ptr->next) {
- u32 i;
- pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
- pos += store_gcov_u32(buffer, pos, 3);
- pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
- pos += store_gcov_u32(buffer, pos, fi_ptr->checksum);
- pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
- pos += store_gcov_u32(buffer, pos, GCOV_TAG_COUNTER_BASE);
- pos += store_gcov_u32(buffer, pos, fi_ptr->num_counters * 2);
- for (i = 0; i < fi_ptr->num_counters; i++) {
- pos += store_gcov_u64(buffer, pos, fi_ptr->counters[i]);
- }
- }
- return pos;
- }
- #endif
- /* Dump buffer in hex format, 20 bytes per line */
- #define NUM_OCTETS_PER_LINE 20
- #define FLUSH_OUTPUT() fflush(stdout)
- /**
- * hexdumpbuf - dump buffer content as hex to stdout
- * @buf: pointer to buffer to dump
- * @sz: size of buffer in bytes
- *
- * Dumps buffer content in hexadecimal format, 20 bytes per line.
- * Modified based on: https://github.com/astarasikov/lk/commit/2a4af09a894194dfaff3e05f6fd505241d54d074
- */
- static void hexdumpbuf(char *buf, unsigned long sz)
- {
- unsigned long rem, cur = 0, i = 0;
- FLUSH_OUTPUT();
- while (cur < sz) {
- rem = ((sz - cur) < NUM_OCTETS_PER_LINE) ? (sz - cur) : NUM_OCTETS_PER_LINE;
- for (i = 0; i < rem; i++) {
- printf("%02x", buf[cur + i]);
- }
- printf("\n");
- FLUSH_OUTPUT();
- cur += rem;
- }
- }
- /**
- * dump_gcov_info - convert and dump single gcov_info as hex
- * @info: pointer to gcov_info structure to dump
- *
- * Converts coverage data to gcda format and prints as hex to console
- * for debugging purposes.
- */
- static void dump_gcov_info(struct gcov_info *info)
- {
- size_t sz = 0;
- char *bufptr = NULL;
- if (!info) {
- return;
- }
- sz = convert_to_gcda(NULL, info);
- bufptr = (char *)malloc(sz);
- if (bufptr == NULL) {
- printf("ERROR: Can't allocate gcda buffer for %s\n", info->filename);
- return;
- }
- sz = convert_to_gcda(bufptr, info);
- hexdumpbuf(bufptr, sz);
- free(bufptr);
- printf("\nCREATE: %s\n", info->filename);
- }
- /**
- * gcov_dump - dump all coverage data to console
- *
- * Iterates through gcov_info_head linked list and converts each gcov_info
- * to gcda format, then prints as hexadecimal to stdout for debugging.
- */
- void gcov_dump(void)
- {
- struct gcov_info *info;
- if (!gcov_info_head) {
- return;
- }
- printf("\nDump coverage data start\n");
- FLUSH_OUTPUT();
- for (info = gcov_info_head; info != NULL; info = info->next) {
- dump_gcov_info(info);
- }
- printf("\nDump coverage data finish\n");
- FLUSH_OUTPUT();
- }
- /**
- * gcov_free - free all collected coverage data
- *
- * Frees all gcov_data structures and their associated buffers from
- * the gcov_data_head linked list. Should be called before collecting
- * new coverage data to avoid memory leaks.
- */
- void gcov_free(void)
- {
- struct gcov_data *data;
- if (gcov_data_head == NULL) {
- return;
- }
- for (data = gcov_data_head; data != NULL; data = data->next) {
- if (data->buffer) {
- free(data->buffer);
- }
- free(data);
- }
- gcov_data_head = NULL;
- }
- /**
- * gcov_collect - collect and convert coverage data from gcov_info_head
- * @interface: output interface selector
- * - 0: collect only, store in gcov_data_head
- * - 1: collect and save to .gcda files
- * - >1: collect and dump to console via gcov_dump()
- *
- * Collects coverage data from gcov_info_head, converts to gcda format,
- * and stores in gcov_data_head linked list. May fail if heap is insufficient.
- *
- * Returns: 0 on success, -1 on failure
- */
- int gcov_collect(unsigned long interface)
- {
- struct gcov_info *info;
- struct gcov_data *data;
- size_t sz = 0, count = 0;
- char *bufptr = NULL;
- /* Check if coverage info exists */
- if (!gcov_info_head) {
- return -1;
- }
- /* Dump to console if interface > 1 */
- if (interface > 1) {
- gcov_dump();
- return 0;
- }
- /* Free previous coverage data to collect latest */
- gcov_free();
- for (info = gcov_info_head; info != NULL; info = info->next) {
- sz = convert_to_gcda(NULL, info);
- bufptr = (char *)malloc(sz);
- data = (struct gcov_data *)malloc(sizeof(struct gcov_data));
- if ((bufptr == NULL) || (data == NULL)) {
- printf("Can't allocate gcda buffer for %s\n", info->filename);
- return -1;
- }
- data->filename = info->filename;
- data->buffer = bufptr;
- data->size = sz;
- convert_to_gcda(bufptr, info);
- gcov_data_link(data);
- if (interface == 1) {
- FILE *fp = fopen(data->filename, "wb");
- if (fp != NULL) {
- printf("Create and store coverage data in %s file\n", data->filename);
- fwrite(data->buffer, 1, (size_t)data->size, fp);
- fclose(fp);
- } else {
- printf("Unable to open %s file\n", data->filename);
- }
- }
- count += 1;
- }
- if (count) {
- printf("%lu files coverage data collected, see gcov_data_head=0x%lx\n", (unsigned long)count, (unsigned long)gcov_data_head);
- }
- return 0;
- }
|