kcov: improve documentation

Improve KCOV documentation:

- Use KCOV instead of kcov, as the former is more widely-used.

- Mention Clang in compiler requirements.

- Use ``annotations`` for inline code.

- Rework remote coverage collection documentation for better clarity.

- Various smaller changes.

[andreyknvl@google.com: v2]
  Link: https://lkml.kernel.org/r/583f41c49eef15210fa813e8229730d11427efa7.1677614637.git.andreyknvl@google.com
[andreyknvl@google.com: fix ``annotation`` for KCOV_REMOTE_ENABLE]
  Link: https://lkml.kernel.org/r/72be5c215c275f35891229b90622ed859f196a46.1677684837.git.andreyknvl@google.com
Link: https://lkml.kernel.org/r/0b5efd70e31bba7912cf9a6c951f0e76a8df27df.1677517724.git.andreyknvl@google.com
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
Reviewed-by: Bagas Sanjaya <bagasdotme@gmail.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Marco Elver <elver@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Andrey Konovalov 2023-02-27 18:17:03 +01:00 committed by Andrew Morton
parent f9641a36d3
commit 7b32137bc0
1 changed files with 97 additions and 62 deletions

View File

@ -1,42 +1,50 @@
kcov: code coverage for fuzzing KCOV: code coverage for fuzzing
=============================== ===============================
kcov exposes kernel code coverage information in a form suitable for coverage- KCOV collects and exposes kernel code coverage information in a form suitable
guided fuzzing (randomized testing). Coverage data of a running kernel is for coverage-guided fuzzing. Coverage data of a running kernel is exported via
exported via the "kcov" debugfs file. Coverage collection is enabled on a task the ``kcov`` debugfs file. Coverage collection is enabled on a task basis, and
basis, and thus it can capture precise coverage of a single system call. thus KCOV can capture precise coverage of a single system call.
Note that kcov does not aim to collect as much coverage as possible. It aims Note that KCOV does not aim to collect as much coverage as possible. It aims
to collect more or less stable coverage that is function of syscall inputs. to collect more or less stable coverage that is a function of syscall inputs.
To achieve this goal it does not collect coverage in soft/hard interrupts To achieve this goal, it does not collect coverage in soft/hard interrupts
and instrumentation of some inherently non-deterministic parts of kernel is (unless remove coverage collection is enabled, see below) and from some
disabled (e.g. scheduler, locking). inherently non-deterministic parts of the kernel (e.g. scheduler, locking).
kcov is also able to collect comparison operands from the instrumented code Besides collecting code coverage, KCOV can also collect comparison operands.
(this feature currently requires that the kernel is compiled with clang). See the "Comparison operands collection" section for details.
Besides collecting coverage data from syscall handlers, KCOV can also collect
coverage for annotated parts of the kernel executing in background kernel
tasks or soft interrupts. See the "Remote coverage collection" section for
details.
Prerequisites Prerequisites
------------- -------------
Configure the kernel with:: KCOV relies on compiler instrumentation and requires GCC 6.1.0 or later
or any Clang version supported by the kernel.
Collecting comparison operands is supported with GCC 8+ or with Clang.
To enable KCOV, configure the kernel with::
CONFIG_KCOV=y CONFIG_KCOV=y
CONFIG_KCOV requires gcc 6.1.0 or later. To enable comparison operands collection, set::
If the comparison operands need to be collected, set::
CONFIG_KCOV_ENABLE_COMPARISONS=y CONFIG_KCOV_ENABLE_COMPARISONS=y
Profiling data will only become accessible once debugfs has been mounted:: Coverage data only becomes accessible once debugfs has been mounted::
mount -t debugfs none /sys/kernel/debug mount -t debugfs none /sys/kernel/debug
Coverage collection Coverage collection
------------------- -------------------
The following program demonstrates coverage collection from within a test The following program demonstrates how to use KCOV to collect coverage for a
program using kcov: single syscall from within a test program:
.. code-block:: c .. code-block:: c
@ -84,7 +92,7 @@ program using kcov:
perror("ioctl"), exit(1); perror("ioctl"), exit(1);
/* Reset coverage from the tail of the ioctl() call. */ /* Reset coverage from the tail of the ioctl() call. */
__atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
/* That's the target syscal call. */ /* Call the target syscall call. */
read(-1, NULL, 0); read(-1, NULL, 0);
/* Read number of PCs collected. */ /* Read number of PCs collected. */
n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
@ -103,7 +111,7 @@ program using kcov:
return 0; return 0;
} }
After piping through addr2line output of the program looks as follows:: After piping through ``addr2line`` the output of the program looks as follows::
SyS_read SyS_read
fs/read_write.c:562 fs/read_write.c:562
@ -121,12 +129,13 @@ After piping through addr2line output of the program looks as follows::
fs/read_write.c:562 fs/read_write.c:562
If a program needs to collect coverage from several threads (independently), If a program needs to collect coverage from several threads (independently),
it needs to open /sys/kernel/debug/kcov in each thread separately. it needs to open ``/sys/kernel/debug/kcov`` in each thread separately.
The interface is fine-grained to allow efficient forking of test processes. The interface is fine-grained to allow efficient forking of test processes.
That is, a parent process opens /sys/kernel/debug/kcov, enables trace mode, That is, a parent process opens ``/sys/kernel/debug/kcov``, enables trace mode,
mmaps coverage buffer and then forks child processes in a loop. Child processes mmaps coverage buffer, and then forks child processes in a loop. The child
only need to enable coverage (disable happens automatically on thread end). processes only need to enable coverage (it gets disabled automatically when
a thread exits).
Comparison operands collection Comparison operands collection
------------------------------ ------------------------------
@ -205,52 +214,78 @@ Comparison operands collection is similar to coverage collection:
return 0; return 0;
} }
Note that the kcov modes (coverage collection or comparison operands) are Note that the KCOV modes (collection of code coverage or comparison operands)
mutually exclusive. are mutually exclusive.
Remote coverage collection Remote coverage collection
-------------------------- --------------------------
With KCOV_ENABLE coverage is collected only for syscalls that are issued Besides collecting coverage data from handlers of syscalls issued from a
from the current process. With KCOV_REMOTE_ENABLE it's possible to collect userspace process, KCOV can also collect coverage for parts of the kernel
coverage for arbitrary parts of the kernel code, provided that those parts executing in other contexts - so-called "remote" coverage.
are annotated with kcov_remote_start()/kcov_remote_stop().
This allows to collect coverage from two types of kernel background Using KCOV to collect remote coverage requires:
threads: the global ones, that are spawned during kernel boot in a limited
number of instances (e.g. one USB hub_event() worker thread is spawned per
USB HCD); and the local ones, that are spawned when a user interacts with
some kernel interface (e.g. vhost workers); as well as from soft
interrupts.
To enable collecting coverage from a global background thread or from a 1. Modifying kernel code to annotate the code section from where coverage
softirq, a unique global handle must be assigned and passed to the should be collected with ``kcov_remote_start`` and ``kcov_remote_stop``.
corresponding kcov_remote_start() call. Then a userspace process can pass
a list of such handles to the KCOV_REMOTE_ENABLE ioctl in the handles
array field of the kcov_remote_arg struct. This will attach the used kcov
device to the code sections, that are referenced by those handles.
Since there might be many local background threads spawned from different 2. Using ``KCOV_REMOTE_ENABLE`` instead of ``KCOV_ENABLE`` in the userspace
userspace processes, we can't use a single global handle per annotation. process that collects coverage.
Instead, the userspace process passes a non-zero handle through the
common_handle field of the kcov_remote_arg struct. This common handle gets
saved to the kcov_handle field in the current task_struct and needs to be
passed to the newly spawned threads via custom annotations. Those threads
should in turn be annotated with kcov_remote_start()/kcov_remote_stop().
Internally kcov stores handles as u64 integers. The top byte of a handle Both ``kcov_remote_start`` and ``kcov_remote_stop`` annotations and the
is used to denote the id of a subsystem that this handle belongs to, and ``KCOV_REMOTE_ENABLE`` ioctl accept handles that identify particular coverage
the lower 4 bytes are used to denote the id of a thread instance within collection sections. The way a handle is used depends on the context where the
that subsystem. A reserved value 0 is used as a subsystem id for common matching code section executes.
handles as they don't belong to a particular subsystem. The bytes 4-7 are
currently reserved and must be zero. In the future the number of bytes
used for the subsystem or handle ids might be increased.
When a particular userspace process collects coverage via a common KCOV supports collecting remote coverage from the following contexts:
handle, kcov will collect coverage for each code section that is annotated
to use the common handle obtained as kcov_handle from the current 1. Global kernel background tasks. These are the tasks that are spawned during
task_struct. However non common handles allow to collect coverage kernel boot in a limited number of instances (e.g. one USB ``hub_event``
selectively from different subsystems. worker is spawned per one USB HCD).
2. Local kernel background tasks. These are spawned when a userspace process
interacts with some kernel interface and are usually killed when the process
exits (e.g. vhost workers).
3. Soft interrupts.
For #1 and #3, a unique global handle must be chosen and passed to the
corresponding ``kcov_remote_start`` call. Then a userspace process must pass
this handle to ``KCOV_REMOTE_ENABLE`` in the ``handles`` array field of the
``kcov_remote_arg`` struct. This will attach the used KCOV device to the code
section referenced by this handle. Multiple global handles identifying
different code sections can be passed at once.
For #2, the userspace process instead must pass a non-zero handle through the
``common_handle`` field of the ``kcov_remote_arg`` struct. This common handle
gets saved to the ``kcov_handle`` field in the current ``task_struct`` and
needs to be passed to the newly spawned local tasks via custom kernel code
modifications. Those tasks should in turn use the passed handle in their
``kcov_remote_start`` and ``kcov_remote_stop`` annotations.
KCOV follows a predefined format for both global and common handles. Each
handle is a ``u64`` integer. Currently, only the one top and the lower 4 bytes
are used. Bytes 4-7 are reserved and must be zero.
For global handles, the top byte of the handle denotes the id of a subsystem
this handle belongs to. For example, KCOV uses ``1`` as the USB subsystem id.
The lower 4 bytes of a global handle denote the id of a task instance within
that subsystem. For example, each ``hub_event`` worker uses the USB bus number
as the task instance id.
For common handles, a reserved value ``0`` is used as a subsystem id, as such
handles don't belong to a particular subsystem. The lower 4 bytes of a common
handle identify a collective instance of all local tasks spawned by the
userspace process that passed a common handle to ``KCOV_REMOTE_ENABLE``.
In practice, any value can be used for common handle instance id if coverage
is only collected from a single userspace process on the system. However, if
common handles are used by multiple processes, unique instance ids must be
used for each process. One option is to use the process id as the common
handle instance id.
The following program demonstrates using KCOV to collect coverage from both
local tasks spawned by the process and the global task that handles USB bus #1:
.. code-block:: c .. code-block:: c