Skip to content

Commit

Permalink
gcov: combine common code
Browse files Browse the repository at this point in the history
There's a lot of duplicated code between gcc and clang implementations,
move it over to fs.c to simplify the code, there's no reason to believe
that for small data like this one would not just implement the simple
convert_to_gcda() function.

Link: https://lkml.kernel.org/r/20210315235453.e3fbb86e99a0.I08a3ee6dbe47ea3e8024956083f162884a958e40@changeid
Signed-off-by: Johannes Berg <[email protected]>
Acked-by: Peter Oberparleiter <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
jmberg-intel authored and torvalds committed May 7, 2021
1 parent b2075db commit 7a1d55b
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 342 deletions.
49 changes: 49 additions & 0 deletions kernel/gcov/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,55 @@ void gcov_enable_events(void)
mutex_unlock(&gcov_lock);
}

/**
* 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;
}

#ifdef CONFIG_MODULES
/* Update list and generate events when modules are unloaded. */
static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
Expand Down
167 changes: 1 addition & 166 deletions kernel/gcov/clang.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/ratelimit.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "gcov.h"
Expand Down Expand Up @@ -449,79 +448,14 @@ void gcov_info_free(struct gcov_info *info)
}
#endif

#define ITER_STRIDE PAGE_SIZE

/**
* struct gcov_iterator - specifies current file position in logical records
* @info: associated profiling data
* @buffer: buffer containing file data
* @size: size of buffer
* @pos: current position in file
*/
struct gcov_iterator {
struct gcov_info *info;
void *buffer;
size_t size;
loff_t pos;
};

/**
* 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.
*/
static 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.
*/
static 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;
}

/**
* 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.
*/
static size_t convert_to_gcda(char *buffer, struct gcov_info *info)
size_t convert_to_gcda(char *buffer, struct gcov_info *info)
{
struct gcov_fn_info *fi_ptr;
size_t pos = 0;
Expand Down Expand Up @@ -558,102 +492,3 @@ static size_t convert_to_gcda(char *buffer, struct gcov_info *info)

return pos;
}

/**
* gcov_iter_new - allocate and initialize profiling data iterator
* @info: profiling data set to be iterated
*
* Return file iterator on success, %NULL otherwise.
*/
struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
{
struct gcov_iterator *iter;

iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL);
if (!iter)
goto err_free;

iter->info = info;
/* Dry-run to get the actual buffer size. */
iter->size = convert_to_gcda(NULL, info);
iter->buffer = vmalloc(iter->size);
if (!iter->buffer)
goto err_free;

convert_to_gcda(iter->buffer, info);

return iter;

err_free:
kfree(iter);
return NULL;
}


/**
* gcov_iter_get_info - return profiling data set for given file iterator
* @iter: file iterator
*/
void gcov_iter_free(struct gcov_iterator *iter)
{
vfree(iter->buffer);
kfree(iter);
}

/**
* gcov_iter_get_info - return profiling data set for given file iterator
* @iter: file iterator
*/
struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
{
return iter->info;
}

/**
* gcov_iter_start - reset file iterator to starting position
* @iter: file iterator
*/
void gcov_iter_start(struct gcov_iterator *iter)
{
iter->pos = 0;
}

/**
* gcov_iter_next - advance file iterator to next logical record
* @iter: file iterator
*
* Return zero if new position is valid, non-zero if iterator has reached end.
*/
int gcov_iter_next(struct gcov_iterator *iter)
{
if (iter->pos < iter->size)
iter->pos += ITER_STRIDE;

if (iter->pos >= iter->size)
return -EINVAL;

return 0;
}

/**
* gcov_iter_write - write data for current pos to seq_file
* @iter: file iterator
* @seq: seq_file handle
*
* Return zero on success, non-zero otherwise.
*/
int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
{
size_t len;

if (iter->pos >= iter->size)
return -EINVAL;

len = ITER_STRIDE;
if (iter->pos + len > iter->size)
len = iter->size - iter->pos;

seq_write(seq, iter->buffer + iter->pos, len);

return 0;
}
116 changes: 116 additions & 0 deletions kernel/gcov/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/vmalloc.h>
#include "gcov.h"

/**
Expand Down Expand Up @@ -85,6 +86,121 @@ static int __init gcov_persist_setup(char *str)
}
__setup("gcov_persist=", gcov_persist_setup);

#define ITER_STRIDE PAGE_SIZE

/**
* struct gcov_iterator - specifies current file position in logical records
* @info: associated profiling data
* @buffer: buffer containing file data
* @size: size of buffer
* @pos: current position in file
*/
struct gcov_iterator {
struct gcov_info *info;
void *buffer;
size_t size;
loff_t pos;
};

/**
* gcov_iter_new - allocate and initialize profiling data iterator
* @info: profiling data set to be iterated
*
* Return file iterator on success, %NULL otherwise.
*/
static struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
{
struct gcov_iterator *iter;

iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL);
if (!iter)
goto err_free;

iter->info = info;
/* Dry-run to get the actual buffer size. */
iter->size = convert_to_gcda(NULL, info);
iter->buffer = vmalloc(iter->size);
if (!iter->buffer)
goto err_free;

convert_to_gcda(iter->buffer, info);

return iter;

err_free:
kfree(iter);
return NULL;
}


/**
* gcov_iter_free - free iterator data
* @iter: file iterator
*/
static void gcov_iter_free(struct gcov_iterator *iter)
{
vfree(iter->buffer);
kfree(iter);
}

/**
* gcov_iter_get_info - return profiling data set for given file iterator
* @iter: file iterator
*/
static struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
{
return iter->info;
}

/**
* gcov_iter_start - reset file iterator to starting position
* @iter: file iterator
*/
static void gcov_iter_start(struct gcov_iterator *iter)
{
iter->pos = 0;
}

/**
* gcov_iter_next - advance file iterator to next logical record
* @iter: file iterator
*
* Return zero if new position is valid, non-zero if iterator has reached end.
*/
static int gcov_iter_next(struct gcov_iterator *iter)
{
if (iter->pos < iter->size)
iter->pos += ITER_STRIDE;

if (iter->pos >= iter->size)
return -EINVAL;

return 0;
}

/**
* gcov_iter_write - write data for current pos to seq_file
* @iter: file iterator
* @seq: seq_file handle
*
* Return zero on success, non-zero otherwise.
*/
static int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
{
size_t len;

if (iter->pos >= iter->size)
return -EINVAL;

len = ITER_STRIDE;
if (iter->pos + len > iter->size)
len = iter->size - iter->pos;

seq_write(seq, iter->buffer + iter->pos, len);

return 0;
}

/*
* seq_file.start() implementation for gcov data files. Note that the
* gcov_iterator interface is designed to be more restrictive than seq_file
Expand Down
Loading

0 comments on commit 7a1d55b

Please sign in to comment.