2. Programming the performance counter registers

We have described how the user interface fills in the desired configuration of the counters and transmits the information to the kernel. It is the job of the ->setup() method to actually program the performance counter registers. Clearly, the details of how this is done is architecture-specific; it is also model-specific on many architectures. For example, i386 provides methods for each model type that programs the counter registers correctly (see the op_model_* files in arch/i386/oprofile for the details). The method reads the values stored in the virtual oprofilefs files and programs the registers appropriately, ready for starting the actual profiling session.

The architecture-specific drivers make sure to save the old register settings before doing OProfile setup. They are restored when OProfile shuts down. This is useful, for example, on i386, where the NMI watchdog uses the same performance counter registers as OProfile; they cannot run concurrently, but OProfile makes sure to restore the setup it found before it was running.

In addition to programming the counter registers themselves, other setup is often necessary. For example, on i386, the local APIC needs programming in order to make the counter's overflow interrupt appear as an NMI (non-maskable interrupt). This allows sampling (and therefore profiling) of regions where "normal" interrupts are masked, enabling more reliable profiles.

2.1. Starting and stopping the counters

Initiating a profiling session is done via writing an ASCII '1' to the file /dev/oprofile/enable. This sets up the core, and calls into the architecture-specific driver to actually enable each configured counter. Again, the details of how this is done is model-specific (for example, the Athlon models can disable or enable on a per-counter basis, unlike the PPro models).

2.2. IA64 and perfmon

The IA64 architecture provides a different interface from the other architectures, using the existing perfmon driver. Register programming is handled entirely in user-space (see daemon/opd_perfmon.c for the details). A process is forked for each CPU, which creates a perfmon context and sets the counter registers appropriately via the sys_perfmonctl interface. In addition, the actual initiation and termination of the profiling session is handled via the same interface using PFM_START and PFM_STOP. On IA64, then, there are no oprofilefs files for the performance counters, as the kernel driver does not program the registers itself.

Instead, the perfmon driver for OProfile simply registers with the OProfile core with an OProfile-specific UUID. During a profiling session, the perfmon core calls into the OProfile perfmon driver and samples are registered with the OProfile core itself as usual (with oprofile_add_sample()).