Chapter 5. Generating useful output

Table of Contents

1. Handling the profile specification
2. Collating the candidate sample files
2.1. Classifying sample files
2.2. Creating inverted profile lists
3. Generating profile data
3.1. Processing the binary image
3.2. Processing the sample files
4. Generating output

All of the tools used to generate human-readable output have to take roughly the same steps to collect the data for processing. First, the profile specification given by the user has to be parsed. Next, a list of sample files matching the specification has to obtained. Using this list, we need to locate the binary file for each sample file, and then use them to extract meaningful data, before a final collation and presentation to the user.

1. Handling the profile specification

The profile specification presented by the user is parsed in the function profile_spec::create(). This creates an object representing the specification. Then we use profile_spec::generate_file_list() to search for all sample files and match them against the profile_spec.

To enable this matching process to work, the attributes of each sample file is encoded in its filename. This is a low-tech approach to matching specifications against candidate sample files, but it works reasonably well. A typical sample file might look like these:

/var/lib/oprofile/samples/current/{root}/bin/ls/{dep}/{root}/bin/ls/{cg}/{root}/bin/ls/CPU_CLK_UNHALTED.100000.0.all.all.all
/var/lib/oprofile/samples/current/{root}/bin/ls/{dep}/{root}/bin/ls/CPU_CLK_UNHALTED.100000.0.all.all.all
/var/lib/oprofile/samples/current/{root}/bin/ls/{dep}/{root}/bin/ls/CPU_CLK_UNHALTED.100000.0.7423.7424.0
/var/lib/oprofile/samples/current/{kern}/r128/{dep}/{kern}/r128/CPU_CLK_UNHALTED.100000.0.all.all.all

This looks unnecessarily complex, but it's actually fairly simple. First we have the session of the sample, by default located here /var/lib/oprofile/samples/current. This location can be changed by specifying the --session-dir option at command-line. This session could equally well be inside an archive from oparchive. Next we have one of the tokens {root} or {kern}. {root} indicates that the binary is found on a file system, and we will encode its path in the next section (e.g. /bin/ls). {kern} indicates a kernel module - on 2.6 kernels the path information is not available from the kernel, so we have to special-case kernel modules like this; we encode merely the name of the module as loaded.

Next there is a {dep} token, indicating another token/path which identifies the dependent binary image. This is used even for the "primary" binary (i.e. the one that was execve()d), as it simplifies processing. Finally, if this sample file is a normal flat profile, the actual file is next in the path. If it's a call-graph sample file, we need one further specification, to allow us to identify cross-binary arcs in the call graph.

The actual sample file name is dot-separated, where the fields are, in order: event name, event count, unit mask, task group ID, task ID, and CPU number.

This sample file can be reliably parsed (with parse_filename()) into a filename_spec. Finally, we can check whether to include the sample file in the final results by comparing this filename_spec against the profile_spec the user specified (for the interested, see valid_candidate() and profile_spec::match). Then comes the really complicated bit...