Commit 7a1d55b9 authored by Johannes Berg's avatar Johannes Berg Committed by Linus Torvalds

gcov: combine common code

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@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Acked-by: default avatarPeter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b2075dbb
...@@ -49,6 +49,55 @@ void gcov_enable_events(void) ...@@ -49,6 +49,55 @@ void gcov_enable_events(void)
mutex_unlock(&gcov_lock); 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 #ifdef CONFIG_MODULES
/* Update list and generate events when modules are unloaded. */ /* Update list and generate events when modules are unloaded. */
static int gcov_module_notifier(struct notifier_block *nb, unsigned long event, static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
......
...@@ -48,7 +48,6 @@ ...@@ -48,7 +48,6 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include "gcov.h" #include "gcov.h"
...@@ -449,71 +448,6 @@ void gcov_info_free(struct gcov_info *info) ...@@ -449,71 +448,6 @@ void gcov_info_free(struct gcov_info *info)
} }
#endif #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 * 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 * @buffer: the buffer to store file data or %NULL if no data should be stored
...@@ -521,7 +455,7 @@ static size_t store_gcov_u64(void *buffer, size_t off, u64 v) ...@@ -521,7 +455,7 @@ static size_t store_gcov_u64(void *buffer, size_t off, u64 v)
* *
* Returns the number of bytes that were/would have been stored into the buffer. * 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; struct gcov_fn_info *fi_ptr;
size_t pos = 0; size_t pos = 0;
...@@ -558,102 +492,3 @@ static size_t convert_to_gcda(char *buffer, struct gcov_info *info) ...@@ -558,102 +492,3 @@ static size_t convert_to_gcda(char *buffer, struct gcov_info *info)
return pos; 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;
}
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/vmalloc.h>
#include "gcov.h" #include "gcov.h"
/** /**
...@@ -85,6 +86,121 @@ static int __init gcov_persist_setup(char *str) ...@@ -85,6 +86,121 @@ static int __init gcov_persist_setup(char *str)
} }
__setup("gcov_persist=", gcov_persist_setup); __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 * seq_file.start() implementation for gcov data files. Note that the
* gcov_iterator interface is designed to be more restrictive than seq_file * gcov_iterator interface is designed to be more restrictive than seq_file
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include "gcov.h" #include "gcov.h"
...@@ -363,71 +362,6 @@ void gcov_info_free(struct gcov_info *info) ...@@ -363,71 +362,6 @@ void gcov_info_free(struct gcov_info *info)
kfree(info); kfree(info);
} }
#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 * 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 * @buffer: the buffer to store file data or %NULL if no data should be stored
...@@ -435,7 +369,7 @@ static size_t store_gcov_u64(void *buffer, size_t off, u64 v) ...@@ -435,7 +369,7 @@ static size_t store_gcov_u64(void *buffer, size_t off, u64 v)
* *
* Returns the number of bytes that were/would have been stored into the buffer. * 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; struct gcov_fn_info *fi_ptr;
struct gcov_ctr_info *ci_ptr; struct gcov_ctr_info *ci_ptr;
...@@ -481,102 +415,3 @@ static size_t convert_to_gcda(char *buffer, struct gcov_info *info) ...@@ -481,102 +415,3 @@ static size_t convert_to_gcda(char *buffer, struct gcov_info *info)
return pos; 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;
}
...@@ -48,6 +48,7 @@ struct gcov_info *gcov_info_next(struct gcov_info *info); ...@@ -48,6 +48,7 @@ struct gcov_info *gcov_info_next(struct gcov_info *info);
void gcov_info_link(struct gcov_info *info); void gcov_info_link(struct gcov_info *info);
void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info); void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info);
bool gcov_info_within_module(struct gcov_info *info, struct module *mod); bool gcov_info_within_module(struct gcov_info *info, struct module *mod);
size_t convert_to_gcda(char *buffer, struct gcov_info *info);
/* Base interface. */ /* Base interface. */
enum gcov_action { enum gcov_action {
...@@ -58,16 +59,9 @@ enum gcov_action { ...@@ -58,16 +59,9 @@ enum gcov_action {
void gcov_event(enum gcov_action action, struct gcov_info *info); void gcov_event(enum gcov_action action, struct gcov_info *info);
void gcov_enable_events(void); void gcov_enable_events(void);
/* Iterator control. */ /* writing helpers */
struct seq_file; size_t store_gcov_u32(void *buffer, size_t off, u32 v);
struct gcov_iterator; size_t store_gcov_u64(void *buffer, size_t off, u64 v);
struct gcov_iterator *gcov_iter_new(struct gcov_info *info);
void gcov_iter_free(struct gcov_iterator *iter);
void gcov_iter_start(struct gcov_iterator *iter);
int gcov_iter_next(struct gcov_iterator *iter);
int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq);
struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter);
/* gcov_info control. */ /* gcov_info control. */
void gcov_info_reset(struct gcov_info *info); void gcov_info_reset(struct gcov_info *info);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment