Commit 2521f2c2 authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Linus Torvalds

gcov: add gcov profiling infrastructure

Enable the use of GCC's coverage testing tool gcov [1] with the Linux
kernel.  gcov may be useful for:

 * debugging (has this code been reached at all?)
 * test improvement (how do I change my test to cover these lines?)
 * minimizing kernel configurations (do I need this option if the
   associated code is never run?)

The profiling patch incorporates the following changes:

 * change kbuild to include profiling flags
 * provide functions needed by profiling code
 * present profiling data as files in debugfs

Note that on some architectures, enabling gcc's profiling option
"-fprofile-arcs" for the entire kernel may trigger compile/link/
run-time problems, some of which are caused by toolchain bugs and
others which require adjustment of architecture code.

For this reason profiling the entire kernel is initially restricted
to those architectures for which it is known to work without changes.
This restriction can be lifted once an architecture has been tested
and found compatible with gcc's profiling. Profiling of single files
or directories is still available on all platforms (see config help
text).

[1] http://gcc.gnu.org/onlinedocs/gcc/Gcov.htmlSigned-off-by: default avatarPeter Oberparleiter <oberpar@linux.vnet.ibm.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Huang Ying <ying.huang@intel.com>
Cc: Li Wei <W.Li@Sun.COM>
Cc: Michael Ellerman <michaele@au1.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Heiko Carstens <heicars2@linux.vnet.ibm.com>
Cc: Martin Schwidefsky <mschwid2@linux.vnet.ibm.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: WANG Cong <xiyou.wangcong@gmail.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Jeff Dike <jdike@addtoit.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 0b923606
Using gcov with the Linux kernel
================================
1. Introduction
2. Preparation
3. Customization
4. Files
5. Modules
6. Separated build and test machines
7. Troubleshooting
Appendix A: sample script: gather_on_build.sh
Appendix B: sample script: gather_on_test.sh
1. Introduction
===============
gcov profiling kernel support enables the use of GCC's coverage testing
tool gcov [1] with the Linux kernel. Coverage data of a running kernel
is exported in gcov-compatible format via the "gcov" debugfs directory.
To get coverage data for a specific file, change to the kernel build
directory and use gcov with the -o option as follows (requires root):
# cd /tmp/linux-out
# gcov -o /sys/kernel/debug/gcov/tmp/linux-out/kernel spinlock.c
This will create source code files annotated with execution counts
in the current directory. In addition, graphical gcov front-ends such
as lcov [2] can be used to automate the process of collecting data
for the entire kernel and provide coverage overviews in HTML format.
Possible uses:
* debugging (has this line been reached at all?)
* test improvement (how do I change my test to cover these lines?)
* minimizing kernel configurations (do I need this option if the
associated code is never run?)
--
[1] http://gcc.gnu.org/onlinedocs/gcc/Gcov.html
[2] http://ltp.sourceforge.net/coverage/lcov.php
2. Preparation
==============
Configure the kernel with:
CONFIG_DEBUGFS=y
CONFIG_GCOV_KERNEL=y
and to get coverage data for the entire kernel:
CONFIG_GCOV_PROFILE_ALL=y
Note that kernels compiled with profiling flags will be significantly
larger and run slower. Also CONFIG_GCOV_PROFILE_ALL may not be supported
on all architectures.
Profiling data will only become accessible once debugfs has been
mounted:
mount -t debugfs none /sys/kernel/debug
3. Customization
================
To enable profiling for specific files or directories, add a line
similar to the following to the respective kernel Makefile:
For a single file (e.g. main.o):
GCOV_PROFILE_main.o := y
For all files in one directory:
GCOV_PROFILE := y
To exclude files from being profiled even when CONFIG_GCOV_PROFILE_ALL
is specified, use:
GCOV_PROFILE_main.o := n
and:
GCOV_PROFILE := n
Only files which are linked to the main kernel image or are compiled as
kernel modules are supported by this mechanism.
4. Files
========
The gcov kernel support creates the following files in debugfs:
/sys/kernel/debug/gcov
Parent directory for all gcov-related files.
/sys/kernel/debug/gcov/reset
Global reset file: resets all coverage data to zero when
written to.
/sys/kernel/debug/gcov/path/to/compile/dir/file.gcda
The actual gcov data file as understood by the gcov
tool. Resets file coverage data to zero when written to.
/sys/kernel/debug/gcov/path/to/compile/dir/file.gcno
Symbolic link to a static data file required by the gcov
tool. This file is generated by gcc when compiling with
option -ftest-coverage.
5. Modules
==========
Kernel modules may contain cleanup code which is only run during
module unload time. The gcov mechanism provides a means to collect
coverage data for such code by keeping a copy of the data associated
with the unloaded module. This data remains available through debugfs.
Once the module is loaded again, the associated coverage counters are
initialized with the data from its previous instantiation.
This behavior can be deactivated by specifying the gcov_persist kernel
parameter:
gcov_persist=0
At run-time, a user can also choose to discard data for an unloaded
module by writing to its data file or the global reset file.
6. Separated build and test machines
====================================
The gcov kernel profiling infrastructure is designed to work out-of-the
box for setups where kernels are built and run on the same machine. In
cases where the kernel runs on a separate machine, special preparations
must be made, depending on where the gcov tool is used:
a) gcov is run on the TEST machine
The gcov tool version on the test machine must be compatible with the
gcc version used for kernel build. Also the following files need to be
copied from build to test machine:
from the source tree:
- all C source files + headers
from the build tree:
- all C source files + headers
- all .gcda and .gcno files
- all links to directories
It is important to note that these files need to be placed into the
exact same file system location on the test machine as on the build
machine. If any of the path components is symbolic link, the actual
directory needs to be used instead (due to make's CURDIR handling).
b) gcov is run on the BUILD machine
The following files need to be copied after each test case from test
to build machine:
from the gcov directory in sysfs:
- all .gcda files
- all links to .gcno files
These files can be copied to any location on the build machine. gcov
must then be called with the -o option pointing to that directory.
Example directory setup on the build machine:
/tmp/linux: kernel source tree
/tmp/out: kernel build directory as specified by make O=
/tmp/coverage: location of the files copied from the test machine
[user@build] cd /tmp/out
[user@build] gcov -o /tmp/coverage/tmp/out/init main.c
7. Troubleshooting
==================
Problem: Compilation aborts during linker step.
Cause: Profiling flags are specified for source files which are not
linked to the main kernel or which are linked by a custom
linker procedure.
Solution: Exclude affected source files from profiling by specifying
GCOV_PROFILE := n or GCOV_PROFILE_basename.o := n in the
corresponding Makefile.
Appendix A: gather_on_build.sh
==============================
Sample script to gather coverage meta files on the build machine
(see 6a):
#!/bin/bash
KSRC=$1
KOBJ=$2
DEST=$3
if [ -z "$KSRC" ] || [ -z "$KOBJ" ] || [ -z "$DEST" ]; then
echo "Usage: $0 <ksrc directory> <kobj directory> <output.tar.gz>" >&2
exit 1
fi
KSRC=$(cd $KSRC; printf "all:\n\t@echo \${CURDIR}\n" | make -f -)
KOBJ=$(cd $KOBJ; printf "all:\n\t@echo \${CURDIR}\n" | make -f -)
find $KSRC $KOBJ \( -name '*.gcno' -o -name '*.[ch]' -o -type l \) -a \
-perm /u+r,g+r | tar cfz $DEST -P -T -
if [ $? -eq 0 ] ; then
echo "$DEST successfully created, copy to test system and unpack with:"
echo " tar xfz $DEST -P"
else
echo "Could not create file $DEST"
fi
Appendix B: gather_on_test.sh
=============================
Sample script to gather coverage data files on the test machine
(see 6b):
#!/bin/bash
DEST=$1
GCDA=/sys/kernel/debug/gcov
if [ -z "$DEST" ] ; then
echo "Usage: $0 <output.tar.gz>" >&2
exit 1
fi
find $GCDA -name '*.gcno' -o -name '*.gcda' | tar cfz $DEST -T -
if [ $? -eq 0 ] ; then
echo "$DEST successfully created, copy to build system and unpack with:"
echo " tar xfz $DEST"
else
echo "Could not create file $DEST"
fi
......@@ -48,6 +48,7 @@ parameter is applicable:
EFI EFI Partitioning (GPT) is enabled
EIDE EIDE/ATAPI support is enabled.
FB The frame buffer device is enabled.
GCOV GCOV profiling is enabled.
HW Appropriate hardware is enabled.
IA-64 IA-64 architecture is enabled.
IMA Integrity measurement architecture is enabled.
......@@ -796,6 +797,12 @@ and is between 256 and 4096 characters. It is defined in the file
Format: off | on
default: on
gcov_persist= [GCOV] When non-zero (default), profiling data for
kernel modules is saved and remains accessible via
debugfs, even when the module is unloaded/reloaded.
When zero, profiling data is discarded and associated
debugfs files are removed at module unload time.
gdth= [HW,SCSI]
See header of drivers/scsi/gdth.c.
......
......@@ -330,6 +330,7 @@ AFLAGS_MODULE = $(MODFLAGS)
LDFLAGS_MODULE =
CFLAGS_KERNEL =
AFLAGS_KERNEL =
CFLAGS_GCOV = -fprofile-arcs -ftest-coverage
# Use LINUXINCLUDE when you must reference the include/ directory.
......@@ -356,7 +357,7 @@ export CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE
export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
# When compiling out-of-tree modules, put MODVERDIR in the module
......@@ -1216,8 +1217,8 @@ clean: archclean $(clean-dirs)
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name '*.symtypes' -o -name 'modules.order' \
-o -name 'Module.markers' -o -name '.tmp_*.o.*' \) \
-type f -print | xargs rm -f
-o -name 'Module.markers' -o -name '.tmp_*.o.*' \
-o -name '*.gcno' \) -type f -print | xargs rm -f
# mrproper - Delete all generated files, including .config
#
......@@ -1421,8 +1422,8 @@ clean: $(clean-dirs)
$(call cmd,rmfiles)
@find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
-type f -print | xargs rm -f
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name '*.gcno' \) -type f -print | xargs rm -f
help:
@echo ' Building external modules.'
......
......@@ -112,3 +112,5 @@ config HAVE_DMA_API_DEBUG
config HAVE_DEFAULT_NO_SPIN_MUTEXES
bool
source "kernel/gcov/Kconfig"
......@@ -16,6 +16,12 @@
#define __must_check __attribute__((warn_unused_result))
#endif
#ifdef CONFIG_GCOV_KERNEL
# if __GNUC_MINOR__ < 4
# error "GCOV profiling support for gcc versions below 3.4 not included"
# endif /* __GNUC_MINOR__ */
#endif /* CONFIG_GCOV_KERNEL */
/*
* A trick to suppress uninitialized variable warning without generating any
* code
......
......@@ -71,6 +71,7 @@ obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_GCOV_KERNEL) += gcov/
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KGDB) += kgdb.o
......
menu "GCOV-based kernel profiling"
config GCOV_KERNEL
bool "Enable gcov-based kernel profiling"
depends on DEBUG_FS && CONSTRUCTORS
default n
---help---
This option enables gcov-based code profiling (e.g. for code coverage
measurements).
If unsure, say N.
Additionally specify CONFIG_GCOV_PROFILE_ALL=y to get profiling data
for the entire kernel. To enable profiling for specific files or
directories, add a line similar to the following to the respective
Makefile:
For a single file (e.g. main.o):
GCOV_PROFILE_main.o := y
For all files in one directory:
GCOV_PROFILE := y
To exclude files from being profiled even when CONFIG_GCOV_PROFILE_ALL
is specified, use:
GCOV_PROFILE_main.o := n
and:
GCOV_PROFILE := n
Note that the debugfs filesystem has to be mounted to access
profiling data.
config GCOV_PROFILE_ALL
bool "Profile entire Kernel"
depends on GCOV_KERNEL
depends on S390 || X86_32
default n
---help---
This options activates profiling for the entire kernel.
If unsure, say N.
Note that a kernel compiled with profiling flags will be significantly
larger and run slower. Also be sure to exclude files from profiling
which are not linked to the kernel image to prevent linker errors.
endmenu
EXTRA_CFLAGS := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"'
obj-$(CONFIG_GCOV_KERNEL) := base.o fs.o gcc_3_4.o
/*
* This code maintains a list of active profiling data structures.
*
* Copyright IBM Corp. 2009
* Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
*
* Uses gcc-internal data definitions.
* Based on the gcov-kernel patch by:
* Hubertus Franke <frankeh@us.ibm.com>
* Nigel Hinds <nhinds@us.ibm.com>
* Rajan Ravindran <rajancr@us.ibm.com>
* Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
* Paul Larson
*/
#define pr_fmt(fmt) "gcov: " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include "gcov.h"
static struct gcov_info *gcov_info_head;
static int gcov_events_enabled;
static DEFINE_MUTEX(gcov_lock);
/*
* __gcov_init is called by gcc-generated constructor code for each object
* file compiled with -fprofile-arcs.
*/
void __gcov_init(struct gcov_info *info)
{
static unsigned int gcov_version;
mutex_lock(&gcov_lock);
if (gcov_version == 0) {
gcov_version = info->version;
/*
* Printing gcc's version magic may prove useful for debugging
* incompatibility reports.
*/
pr_info("version magic: 0x%x\n", gcov_version);
}
/*
* Add new profiling data structure to list and inform event
* listener.
*/
info->next = gcov_info_head;
gcov_info_head = info;
if (gcov_events_enabled)
gcov_event(GCOV_ADD, info);
mutex_unlock(&gcov_lock);
}
EXPORT_SYMBOL(__gcov_init);
/*
* These functions may be referenced by gcc-generated profiling code but serve
* no function for kernel profiling.
*/
void __gcov_flush(void)
{
/* Unused. */
}
EXPORT_SYMBOL(__gcov_flush);
void __gcov_merge_add(gcov_type *counters, unsigned int n_counters)
{
/* Unused. */
}
EXPORT_SYMBOL(__gcov_merge_add);
void __gcov_merge_single(gcov_type *counters, unsigned int n_counters)
{
/* Unused. */
}
EXPORT_SYMBOL(__gcov_merge_single);
void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters)
{
/* Unused. */
}
EXPORT_SYMBOL(__gcov_merge_delta);
/**
* gcov_enable_events - enable event reporting through gcov_event()
*
* Turn on reporting of profiling data load/unload-events through the
* gcov_event() callback. Also replay all previous events once. This function
* is needed because some events are potentially generated too early for the
* callback implementation to handle them initially.
*/
void gcov_enable_events(void)
{
struct gcov_info *info;
mutex_lock(&gcov_lock);
gcov_events_enabled = 1;
/* Perform event callback for previously registered entries. */
for (info = gcov_info_head; info; info = info->next)
gcov_event(GCOV_ADD, info);
mutex_unlock(&gcov_lock);
}
#ifdef CONFIG_MODULES
static inline int within(void *addr, void *start, unsigned long size)
{
return ((addr >= start) && (addr < start + size));
}
/* Update list and generate events when modules are unloaded. */
static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
void *data)
{
struct module *mod = data;
struct gcov_info *info;
struct gcov_info *prev;
if (event != MODULE_STATE_GOING)
return NOTIFY_OK;
mutex_lock(&gcov_lock);
prev = NULL;
/* Remove entries located in module from linked list. */
for (info = gcov_info_head; info; info = info->next) {
if (within(info, mod->module_core, mod->core_size)) {
if (prev)
prev->next = info->next;
else
gcov_info_head = info->next;
if (gcov_events_enabled)
gcov_event(GCOV_REMOVE, info);
} else
prev = info;
}
mutex_unlock(&gcov_lock);
return NOTIFY_OK;
}
static struct notifier_block gcov_nb = {
.notifier_call = gcov_module_notifier,
};
static int __init gcov_init(void)
{
return register_module_notifier(&gcov_nb);
}
device_initcall(gcov_init);
#endif /* CONFIG_MODULES */
/*
* This code exports profiling data as debugfs files to userspace.
*
* Copyright IBM Corp. 2009
* Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
*
* Uses gcc-internal data definitions.
* Based on the gcov-kernel patch by:
* Hubertus Franke <frankeh@us.ibm.com>
* Nigel Hinds <nhinds@us.ibm.com>
* Rajan Ravindran <rajancr@us.ibm.com>
* Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
* Paul Larson
* Yi CDL Yang
*/
#define pr_fmt(fmt) "gcov: " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include "gcov.h"
/**
* struct gcov_node - represents a debugfs entry
* @list: list head for child node list
* @children: child nodes
* @all: list head for list of all nodes
* @parent: parent node
* @info: associated profiling data structure if not a directory
* @ghost: when an object file containing profiling data is unloaded we keep a
* copy of the profiling data here to allow collecting coverage data
* for cleanup code. Such a node is called a "ghost".
* @dentry: main debugfs entry, either a directory or data file
* @links: associated symbolic links
* @name: data file basename
*
* struct gcov_node represents an entity within the gcov/ subdirectory
* of debugfs. There are directory and data file nodes. The latter represent
* the actual synthesized data file plus any associated symbolic links which
* are needed by the gcov tool to work correctly.
*/
struct gcov_node {
struct list_head list;
struct list_head children;
struct list_head all;
struct gcov_node *parent;
struct gcov_info *info;
struct gcov_info *ghost;
struct dentry *dentry;
struct dentry **links;
char name[0];
};
static const char objtree[] = OBJTREE;
static const char srctree[] = SRCTREE;
static struct gcov_node root_node;
static struct dentry *reset_dentry;
static LIST_HEAD(all_head);
static DEFINE_MUTEX(node_lock);
/* If non-zero, keep copies of profiling data for unloaded modules. */
static int gcov_persist = 1;
static int __init gcov_persist_setup(char *str)
{
unsigned long val;
if (strict_strtoul(str, 0, &val)) {
pr_warning("invalid gcov_persist parameter '%s'\n", str);
return 0;
}
gcov_persist = val;
pr_info("setting gcov_persist to %d\n", gcov_persist);
return 1;
}
__setup("gcov_persist=", gcov_persist_setup);
/*
* seq_file.start() implementation for gcov data files. Note that the
* gcov_iterator interface is designed to be more restrictive than seq_file
* (no start from arbitrary position, etc.), to simplify the iterator
* implementation.
*/
static void *gcov_seq_start(struct seq_file *seq, loff_t *pos)
{
loff_t i;
gcov_iter_start(seq->private);
for (i = 0; i < *pos; i++) {
if (gcov_iter_next(seq->private))
return NULL;
}
return seq->private;
}
/* seq_file.next() implementation for gcov data files. */
static void *gcov_seq_next(struct seq_file *seq, void *data, loff_t *pos)
{
struct gcov_iterator *iter = data;
if (gcov_iter_next(iter))
return NULL;
(*pos)++;
return iter;
}
/* seq_file.show() implementation for gcov data files. */
static int gcov_seq_show(struct seq_file *seq, void *data)
{
struct gcov_iterator *iter = data;
if (gcov_iter_write(iter, seq))
return -EINVAL;
return 0;
}
static void gcov_seq_stop(struct seq_file *seq, void *data)
{
/* Unused. */
}
static const struct seq_operations gcov_seq_ops = {
.start = gcov_seq_start,