Table of Contents
Now we can move onto user-space in our description of how raw interrupt
samples are processed into useful information. As we described in
previous sections, the kernel OProfile driver creates a large buffer of
sample data consisting of offset values, interspersed with
notification of changes in context. These context changes indicate how
following samples should be attributed, and include task switches, CPU
changes, and which dcookie the sample value is against. By processing
this buffer entry-by-entry, we can determine where the samples should
be accredited to. This is particularly important when using the
--separate
.
The file daemon/opd_trans.c
contains the basic routine
for the buffer processing. The struct transient
structure is used to hold changes in context. Its members are modified
as we process each entry; it is passed into the routines in
daemon/opd_sfile.c
for actually logging the sample
to a particular sample file (which will be held in
$SESSION_DIR/samples/current
).
The buffer format is designed for conciseness, as high sampling rates
can easily generate a lot of data. Thus, context changes are prefixed
by an escape code, identified by is_escape_code()
.
If an escape code is found, the next entry in the buffer identifies
what type of context change is being read. These are handed off to
various handlers (see the handlers
array), which
modify the transient structure as appropriate. If it's not an escape
code, then it must be a PC offset value, and the very next entry will
be the numeric hardware counter. These values are read and recorded
in the transient structure; we then do a lookup to find the correct
sample file, and log the sample, as described in the next section.
Samples from kernel code require a little special handling. Because
the binary text which the sample is against does not correspond to
any file that the kernel directly knows about, the OProfile driver
stores the absolute PC value in the buffer, instead of the file offset.
Of course, we need an offset against some particular binary. To handle
this, we keep a list of loaded modules by parsing
/proc/modules
as needed. When a module is loaded,
a notification is placed in the OProfile buffer, and this triggers a
re-read. We store the module name, and the loading address and size.
This is also done for the main kernel image, as specified by the user.
The absolute PC value is matched against each address range, and
modified into an offset when the matching module is found. See
daemon/opd_kernel.c
for the details.