Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,301 @@
.. SPDX-License-Identifier: GPL-2.0
=================
Boot-time tracing
=================
:Author: Masami Hiramatsu <mhiramat@kernel.org>
Overview
========
Boot-time tracing allows users to trace boot-time process including
device initialization with full features of ftrace including per-event
filter and actions, histograms, kprobe-events and synthetic-events,
and trace instances.
Since kernel command line is not enough to control these complex features,
this uses bootconfig file to describe tracing feature programming.
Options in the Boot Config
==========================
Here is the list of available options for boot time tracing in
boot config file [1]_. All options are under "ftrace." or "kernel."
prefix. See kernel parameters for the options which starts
with "kernel." prefix [2]_.
.. [1] See :ref:`Documentation/admin-guide/bootconfig.rst <bootconfig>`
.. [2] See :ref:`Documentation/admin-guide/kernel-parameters.rst <kernelparameters>`
Ftrace Global Options
---------------------
Ftrace global options have "kernel." prefix in boot config, which means
these options are passed as a part of kernel legacy command line.
kernel.tp_printk
Output trace-event data on printk buffer too.
kernel.dump_on_oops [= MODE]
Dump ftrace on Oops. If MODE = 1 or omitted, dump trace buffer
on all CPUs. If MODE = 2, dump a buffer on a CPU which kicks Oops.
kernel.traceoff_on_warning
Stop tracing if WARN_ON() occurs.
kernel.fgraph_max_depth = MAX_DEPTH
Set MAX_DEPTH to maximum depth of fgraph tracer.
kernel.fgraph_filters = FILTER[, FILTER2...]
Add fgraph tracing function filters.
kernel.fgraph_notraces = FILTER[, FILTER2...]
Add fgraph non-tracing function filters.
Ftrace Per-instance Options
---------------------------
These options can be used for each instance including global ftrace node.
ftrace.[instance.INSTANCE.]options = OPT1[, OPT2[...]]
Enable given ftrace options.
ftrace.[instance.INSTANCE.]tracing_on = 0|1
Enable/Disable tracing on this instance when starting boot-time tracing.
(you can enable it by the "traceon" event trigger action)
ftrace.[instance.INSTANCE.]trace_clock = CLOCK
Set given CLOCK to ftrace's trace_clock.
ftrace.[instance.INSTANCE.]buffer_size = SIZE
Configure ftrace buffer size to SIZE. You can use "KB" or "MB"
for that SIZE.
ftrace.[instance.INSTANCE.]alloc_snapshot
Allocate snapshot buffer.
ftrace.[instance.INSTANCE.]cpumask = CPUMASK
Set CPUMASK as trace cpu-mask.
ftrace.[instance.INSTANCE.]events = EVENT[, EVENT2[...]]
Enable given events on boot. You can use a wild card in EVENT.
ftrace.[instance.INSTANCE.]tracer = TRACER
Set TRACER to current tracer on boot. (e.g. function)
ftrace.[instance.INSTANCE.]ftrace.filters
This will take an array of tracing function filter rules.
ftrace.[instance.INSTANCE.]ftrace.notraces
This will take an array of NON-tracing function filter rules.
Ftrace Per-Event Options
------------------------
These options are setting per-event options.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.enable
Enable GROUP:EVENT tracing.
ftrace.[instance.INSTANCE.]event.GROUP.enable
Enable all event tracing within GROUP.
ftrace.[instance.INSTANCE.]event.enable
Enable all event tracing.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.filter = FILTER
Set FILTER rule to the GROUP:EVENT.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.actions = ACTION[, ACTION2[...]]
Set ACTIONs to the GROUP:EVENT.
ftrace.[instance.INSTANCE.]event.kprobes.EVENT.probes = PROBE[, PROBE2[...]]
Defines new kprobe event based on PROBEs. It is able to define
multiple probes on one event, but those must have same type of
arguments. This option is available only for the event which
group name is "kprobes".
ftrace.[instance.INSTANCE.]event.synthetic.EVENT.fields = FIELD[, FIELD2[...]]
Defines new synthetic event with FIELDs. Each field should be
"type varname".
Note that kprobe and synthetic event definitions can be written under
instance node, but those are also visible from other instances. So please
take care for event name conflict.
Ftrace Histogram Options
------------------------
Since it is too long to write a histogram action as a string for per-event
action option, there are tree-style options under per-event 'hist' subkey
for the histogram actions. For the detail of the each parameter,
please read the event histogram document (Documentation/trace/histogram.rst)
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]keys = KEY1[, KEY2[...]]
Set histogram key parameters. (Mandatory)
The 'N' is a digit string for the multiple histogram. You can omit it
if there is one histogram on the event.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]values = VAL1[, VAL2[...]]
Set histogram value parameters.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]sort = SORT1[, SORT2[...]]
Set histogram sort parameter options.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]size = NR_ENTRIES
Set histogram size (number of entries).
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]name = NAME
Set histogram name.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]var.VARIABLE = EXPR
Define a new VARIABLE by EXPR expression.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]<pause|continue|clear>
Set histogram control parameter. You can set one of them.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]onmatch.[M.]event = GROUP.EVENT
Set histogram 'onmatch' handler matching event parameter.
The 'M' is a digit string for the multiple 'onmatch' handler. You can omit it
if there is one 'onmatch' handler on this histogram.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]onmatch.[M.]trace = EVENT[, ARG1[...]]
Set histogram 'trace' action for 'onmatch'.
EVENT must be a synthetic event name, and ARG1... are parameters
for that event. Mandatory if 'onmatch.event' option is set.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]onmax.[M.]var = VAR
Set histogram 'onmax' handler variable parameter.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]onchange.[M.]var = VAR
Set histogram 'onchange' handler variable parameter.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]<onmax|onchange>.[M.]save = ARG1[, ARG2[...]]
Set histogram 'save' action parameters for 'onmax' or 'onchange' handler.
This option or below 'snapshot' option is mandatory if 'onmax.var' or
'onchange.var' option is set.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]<onmax|onchange>.[M.]snapshot
Set histogram 'snapshot' action for 'onmax' or 'onchange' handler.
This option or above 'save' option is mandatory if 'onmax.var' or
'onchange.var' option is set.
ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.filter = FILTER_EXPR
Set histogram filter expression. You don't need 'if' in the FILTER_EXPR.
Note that this 'hist' option can conflict with the per-event 'actions'
option if the 'actions' option has a histogram action.
When to Start
=============
All boot-time tracing options starting with ``ftrace`` will be enabled at the
end of core_initcall. This means you can trace the events from postcore_initcall.
Most of the subsystems and architecture dependent drivers will be initialized
after that (arch_initcall or subsys_initcall). Thus, you can trace those with
boot-time tracing.
If you want to trace events before core_initcall, you can use the options
starting with ``kernel``. Some of them will be enabled earlier than the initcall
processing (for example, ``kernel.ftrace=function`` and ``kernel.trace_event``
will start before the initcall.)
Examples
========
For example, to add filter and actions for each event, define kprobe
events, and synthetic events with histogram, write a boot config like
below::
ftrace.event {
task.task_newtask {
filter = "pid < 128"
enable
}
kprobes.vfs_read {
probes = "vfs_read $arg1 $arg2"
filter = "common_pid < 200"
enable
}
synthetic.initcall_latency {
fields = "unsigned long func", "u64 lat"
hist {
keys = func.sym, lat
values = lat
sort = lat
}
}
initcall.initcall_start.hist {
keys = func
var.ts0 = common_timestamp.usecs
}
initcall.initcall_finish.hist {
keys = func
var.lat = common_timestamp.usecs - $ts0
onmatch {
event = initcall.initcall_start
trace = initcall_latency, func, $lat
}
}
}
Also, boot-time tracing supports "instance" node, which allows us to run
several tracers for different purpose at once. For example, one tracer
is for tracing functions starting with "user\_", and others tracing
"kernel\_" functions, you can write boot config as below::
ftrace.instance {
foo {
tracer = "function"
ftrace.filters = "user_*"
}
bar {
tracer = "function"
ftrace.filters = "kernel_*"
}
}
The instance node also accepts event nodes so that each instance
can customize its event tracing.
With the trigger action and kprobes, you can trace function-graph while
a function is called. For example, this will trace all function calls in
the pci_proc_init()::
ftrace {
tracing_on = 0
tracer = function_graph
event.kprobes {
start_event {
probes = "pci_proc_init"
actions = "traceon"
}
end_event {
probes = "pci_proc_init%return"
actions = "traceoff"
}
}
}
This boot-time tracing also supports ftrace kernel parameters via boot
config.
For example, following kernel parameters::
trace_options=sym-addr trace_event=initcall:* tp_printk trace_buf_size=1M ftrace=function ftrace_filter="vfs*"
This can be written in boot config like below::
kernel {
trace_options = sym-addr
trace_event = "initcall:*"
tp_printk
trace_buf_size = 1M
ftrace = function
ftrace_filter = "vfs*"
}
Note that parameters start with "kernel" prefix instead of "ftrace".
@@ -0,0 +1,294 @@
.. SPDX-License-Identifier: GPL-2.0
======================================
CoreSight System Configuration Manager
======================================
:Author: Mike Leach <mike.leach@linaro.org>
:Date: October 2020
Introduction
============
The CoreSight System Configuration manager is an API that allows the
programming of the CoreSight system with pre-defined configurations that
can then be easily enabled from sysfs or perf.
Many CoreSight components can be programmed in complex ways - especially ETMs.
In addition, components can interact across the CoreSight system, often via
the cross trigger components such as CTI and CTM. These system settings can
be defined and enabled as named configurations.
Basic Concepts
==============
This section introduces the basic concepts of a CoreSight system configuration.
Features
--------
A feature is a named set of programming for a CoreSight device. The programming
is device dependent, and can be defined in terms of absolute register values,
resource usage and parameter values.
The feature is defined using a descriptor. This descriptor is used to load onto
a matching device, either when the feature is loaded into the system, or when the
CoreSight device is registered with the configuration manager.
The load process involves interpreting the descriptor into a set of register
accesses in the driver - the resource usage and parameter descriptions
translated into appropriate register accesses. This interpretation makes it easy
and efficient for the feature to be programmed onto the device when required.
The feature will not be active on the device until the feature is enabled, and
the device itself is enabled. When the device is enabled then enabled features
will be programmed into the device hardware.
A feature is enabled as part of a configuration being enabled on the system.
Parameter Value
~~~~~~~~~~~~~~~
A parameter value is a named value that may be set by the user prior to the
feature being enabled that can adjust the behaviour of the operation programmed
by the feature.
For example, this could be a count value in a programmed operation that repeats
at a given rate. When the feature is enabled then the current value of the
parameter is used in programming the device.
The feature descriptor defines a default value for a parameter, which is used
if the user does not supply a new value.
Users can update parameter values using the configfs API for the CoreSight
system - which is described below.
The current value of the parameter is loaded into the device when the feature
is enabled on that device.
Configurations
--------------
A configuration defines a set of features that are to be used in a trace
session where the configuration is selected. For any trace session only one
configuration may be selected.
The features defined may be on any type of device that is registered
to support system configuration. A configuration may select features to be
enabled on a class of devices - i.e. any ETMv4, or specific devices, e.g. a
specific CTI on the system.
As with the feature, a descriptor is used to define the configuration.
This will define the features that must be enabled as part of the configuration
as well as any preset values that can be used to override default parameter
values.
Preset Values
~~~~~~~~~~~~~
Preset values are easily selectable sets of parameter values for the features
that the configuration uses. The number of values in a single preset set, equals
the sum of parameter values in the features used by the configuration.
e.g. a configuration consists of 3 features, one has 2 parameters, one has
a single parameter, and another has no parameters. A single preset set will
therefore have 3 values.
Presets are optionally defined by the configuration, up to 15 can be defined.
If no preset is selected, then the parameter values defined in the feature
are used as normal.
Operation
~~~~~~~~~
The following steps take place in the operation of a configuration.
1) In this example, the configuration is 'autofdo', which has an
associated feature 'strobing' that works on ETMv4 CoreSight Devices.
2) The configuration is enabled. For example 'perf' may select the
configuration as part of its command line::
perf record -e cs_etm/autofdo/ myapp
which will enable the 'autofdo' configuration.
3) perf starts tracing on the system. As each ETMv4 that perf uses for
trace is enabled, the configuration manager will check if the ETMv4
has a feature that relates to the currently active configuration.
In this case 'strobing' is enabled & programmed into the ETMv4.
4) When the ETMv4 is disabled, any registers marked as needing to be
saved will be read back.
5) At the end of the perf session, the configuration will be disabled.
Viewing Configurations and Features
===================================
The set of configurations and features that are currently loaded into the
system can be viewed using the configfs API.
Mount configfs as normal and the 'cs-syscfg' subsystem will appear::
$ ls /config
cs-syscfg stp-policy
This has two sub-directories::
$ cd cs-syscfg/
$ ls
configurations features
The system has the configuration 'autofdo' built in. It may be examined as
follows::
$ cd configurations/
$ ls
autofdo
$ cd autofdo/
$ ls
description feature_refs preset1 preset3 preset5 preset7 preset9
enable preset preset2 preset4 preset6 preset8
$ cat description
Setup ETMs with strobing for autofdo
$ cat feature_refs
strobing
Each preset declared has a 'preset<n>' subdirectory declared. The values for
the preset can be examined::
$ cat preset1/values
strobing.window = 0x1388 strobing.period = 0x2
$ cat preset2/values
strobing.window = 0x1388 strobing.period = 0x4
The 'enable' and 'preset' files allow the control of a configuration when
using CoreSight with sysfs.
The features referenced by the configuration can be examined in the features
directory::
$ cd ../../features/strobing/
$ ls
description matches nr_params params
$ cat description
Generate periodic trace capture windows.
parameter 'window': a number of CPU cycles (W)
parameter 'period': trace enabled for W cycles every period x W cycles
$ cat matches
SRC_ETMV4
$ cat nr_params
2
Move to the params directory to examine and adjust parameters::
cd params
$ ls
period window
$ cd period
$ ls
value
$ cat value
0x2710
# echo 15000 > value
# cat value
0x3a98
Parameters adjusted in this way are reflected in all device instances that have
loaded the feature.
Using Configurations in perf
============================
The configurations loaded into the CoreSight configuration management are
also declared in the perf 'cs_etm' event infrastructure so that they can
be selected when running trace under perf::
$ ls /sys/devices/cs_etm
cpu0 cpu2 events nr_addr_filters power subsystem uevent
cpu1 cpu3 format perf_event_mux_interval_ms sinks type
The key directory here is 'events' - a generic perf directory which allows
selection on the perf command line. As with the sinks entries, this provides
a hash of the configuration name.
The entry in the 'events' directory uses perfs built in syntax generator
to substitute the syntax for the name when evaluating the command::
$ ls events/
autofdo
$ cat events/autofdo
configid=0xa7c3dddd
The 'autofdo' configuration may be selected on the perf command line::
$ perf record -e cs_etm/autofdo/u --per-thread <application>
A preset to override the current parameter values can also be selected::
$ perf record -e cs_etm/autofdo,preset=1/u --per-thread <application>
When configurations are selected in this way, then the trace sink used is
automatically selected.
Using Configurations in sysfs
=============================
Coresight can be controlled using sysfs. When this is in use then a configuration
can be made active for the devices that are used in the sysfs session.
In a configuration there are 'enable' and 'preset' files.
To enable a configuration for use with sysfs::
$ cd configurations/autofdo
$ echo 1 > enable
This will then use any default parameter values in the features - which can be
adjusted as described above.
To use a preset<n> set of parameter values::
$ echo 3 > preset
This will select preset3 for the configuration.
The valid values for preset are 0 - to deselect presets, and any value of
<n> where a preset<n> sub-directory is present.
Note that the active sysfs configuration is a global parameter, therefore
only a single configuration can be active for sysfs at any one time.
Attempting to enable a second configuration will result in an error.
Additionally, attempting to disable the configuration while in use will
also result in an error.
The use of the active configuration by sysfs is independent of the configuration
used in perf.
Creating and Loading Custom Configurations
==========================================
Custom configurations and / or features can be dynamically loaded into the
system by using a loadable module.
An example of a custom configuration is found in ./samples/coresight.
This creates a new configuration that uses the existing built in
strobing feature, but provides a different set of presets.
When the module is loaded, then the configuration appears in the configfs
file system and is selectable in the same way as the built in configuration
described above.
Configurations can use previously loaded features. The system will ensure
that it is not possible to unload a feature that is currently in use, by
enforcing the unload order as the strict reverse of the load order.
@@ -0,0 +1,193 @@
==========================
Coresight CPU Debug Module
==========================
:Author: Leo Yan <leo.yan@linaro.org>
:Date: April 5th, 2017
Introduction
------------
Coresight CPU debug module is defined in ARMv8-a architecture reference manual
(ARM DDI 0487A.k) Chapter 'Part H: External debug', the CPU can integrate
debug module and it is mainly used for two modes: self-hosted debug and
external debug. Usually the external debug mode is well known as the external
debugger connects with SoC from JTAG port; on the other hand the program can
explore debugging method which rely on self-hosted debug mode, this document
is to focus on this part.
The debug module provides sample-based profiling extension, which can be used
to sample CPU program counter, secure state and exception level, etc; usually
every CPU has one dedicated debug module to be connected. Based on self-hosted
debug mechanism, Linux kernel can access these related registers from mmio
region when the kernel panic happens. The callback notifier for kernel panic
will dump related registers for every CPU; finally this is good for assistant
analysis for panic.
Implementation
--------------
- During driver registration, it uses EDDEVID and EDDEVID1 - two device ID
registers to decide if sample-based profiling is implemented or not. On some
platforms this hardware feature is fully or partially implemented; and if
this feature is not supported then registration will fail.
- At the time this documentation was written, the debug driver mainly relies on
information gathered by the kernel panic callback notifier from three
sampling registers: EDPCSR, EDVIDSR and EDCIDSR: from EDPCSR we can get
program counter; EDVIDSR has information for secure state, exception level,
bit width, etc; EDCIDSR is context ID value which contains the sampled value
of CONTEXTIDR_EL1.
- The driver supports a CPU running in either AArch64 or AArch32 mode. The
registers naming convention is a bit different between them, AArch64 uses
'ED' for register prefix (ARM DDI 0487A.k, chapter H9.1) and AArch32 uses
'DBG' as prefix (ARM DDI 0487A.k, chapter G5.1). The driver is unified to
use AArch64 naming convention.
- ARMv8-a (ARM DDI 0487A.k) and ARMv7-a (ARM DDI 0406C.b) have different
register bits definition. So the driver consolidates two difference:
If PCSROffset=0b0000, on ARMv8-a the feature of EDPCSR is not implemented;
but ARMv7-a defines "PCSR samples are offset by a value that depends on the
instruction set state". For ARMv7-a, the driver checks furthermore if CPU
runs with ARM or thumb instruction set and calibrate PCSR value, the
detailed description for offset is in ARMv7-a ARM (ARM DDI 0406C.b) chapter
C11.11.34 "DBGPCSR, Program Counter Sampling Register".
If PCSROffset=0b0010, ARMv8-a defines "EDPCSR implemented, and samples have
no offset applied and do not sample the instruction set state in AArch32
state". So on ARMv8 if EDDEVID1.PCSROffset is 0b0010 and the CPU operates
in AArch32 state, EDPCSR is not sampled; when the CPU operates in AArch64
state EDPCSR is sampled and no offset are applied.
Clock and power domain
----------------------
Before accessing debug registers, we should ensure the clock and power domain
have been enabled properly. In ARMv8-a ARM (ARM DDI 0487A.k) chapter 'H9.1
Debug registers', the debug registers are spread into two domains: the debug
domain and the CPU domain.
::
+---------------+
| |
| |
+----------+--+ |
dbg_clock -->| |**| |<-- cpu_clock
| Debug |**| CPU |
dbg_power_domain -->| |**| |<-- cpu_power_domain
+----------+--+ |
| |
| |
+---------------+
For debug domain, the user uses DT binding "clocks" and "power-domains" to
specify the corresponding clock source and power supply for the debug logic.
The driver calls the pm_runtime_{put|get} operations as needed to handle the
debug power domain.
For CPU domain, the different SoC designs have different power management
schemes and finally this heavily impacts external debug module. So we can
divide into below cases:
- On systems with a sane power controller which can behave correctly with
respect to CPU power domain, the CPU power domain can be controlled by
register EDPRCR in driver. The driver firstly writes bit EDPRCR.COREPURQ
to power up the CPU, and then writes bit EDPRCR.CORENPDRQ for emulation
of CPU power down. As result, this can ensure the CPU power domain is
powered on properly during the period when access debug related registers;
- Some designs will power down an entire cluster if all CPUs on the cluster
are powered down - including the parts of the debug registers that should
remain powered in the debug power domain. The bits in EDPRCR are not
respected in these cases, so these designs do not support debug over
power down in the way that the CoreSight / Debug designers anticipated.
This means that even checking EDPRSR has the potential to cause a bus hang
if the target register is unpowered.
In this case, accessing to the debug registers while they are not powered
is a recipe for disaster; so we need preventing CPU low power states at boot
time or when user enable module at the run time. Please see chapter
"How to use the module" for detailed usage info for this.
Device Tree Bindings
--------------------
See Documentation/devicetree/bindings/arm/arm,coresight-cpu-debug.yaml for
details.
How to use the module
---------------------
If you want to enable debugging functionality at boot time, you can add
"coresight_cpu_debug.enable=1" to the kernel command line parameter.
The driver also can work as module, so can enable the debugging when insmod
module::
# insmod coresight_cpu_debug.ko debug=1
When boot time or insmod module you have not enabled the debugging, the driver
uses the debugfs file system to provide a knob to dynamically enable or disable
debugging:
To enable it, write a '1' into /sys/kernel/debug/coresight_cpu_debug/enable::
# echo 1 > /sys/kernel/debug/coresight_cpu_debug/enable
To disable it, write a '0' into /sys/kernel/debug/coresight_cpu_debug/enable::
# echo 0 > /sys/kernel/debug/coresight_cpu_debug/enable
As explained in chapter "Clock and power domain", if you are working on one
platform which has idle states to power off debug logic and the power
controller cannot work well for the request from EDPRCR, then you should
firstly constraint CPU idle states before enable CPU debugging feature; so can
ensure the accessing to debug logic.
If you want to limit idle states at boot time, you can use "nohlt" or
"cpuidle.off=1" in the kernel command line.
At the runtime you can disable idle states with below methods:
It is possible to disable CPU idle states by way of the PM QoS
subsystem, more specifically by using the "/dev/cpu_dma_latency"
interface (see Documentation/power/pm_qos_interface.rst for more
details). As specified in the PM QoS documentation the requested
parameter will stay in effect until the file descriptor is released.
For example::
# exec 3<> /dev/cpu_dma_latency; echo 0 >&3
...
Do some work...
...
# exec 3<>-
The same can also be done from an application program.
Disable specific CPU's specific idle state from cpuidle sysfs (see
Documentation/admin-guide/pm/cpuidle.rst)::
# echo 1 > /sys/devices/system/cpu/cpu$cpu/cpuidle/state$state/disable
Output format
-------------
Here is an example of the debugging output format::
ARM external debug module:
coresight-cpu-debug 850000.debug: CPU[0]:
coresight-cpu-debug 850000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
coresight-cpu-debug 850000.debug: EDPCSR: handle_IPI+0x174/0x1d8
coresight-cpu-debug 850000.debug: EDCIDSR: 00000000
coresight-cpu-debug 850000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
coresight-cpu-debug 852000.debug: CPU[1]:
coresight-cpu-debug 852000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
coresight-cpu-debug 852000.debug: EDPCSR: debug_notifier_call+0x23c/0x358
coresight-cpu-debug 852000.debug: EDCIDSR: 00000000
coresight-cpu-debug 852000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
@@ -0,0 +1,32 @@
.. SPDX-License-Identifier: GPL-2.0
=============================
Coresight Dummy Trace Module
=============================
:Author: Hao Zhang <quic_hazha@quicinc.com>
:Date: June 2023
Introduction
------------
The Coresight dummy trace module is for the specific devices that kernel don't
have permission to access or configure, e.g., CoreSight TPDMs on Qualcomm
platforms. For these devices, a dummy driver is needed to register them as
Coresight devices. The module may also be used to define components that may
not have any programming interfaces, so that paths can be created in the driver.
It provides Coresight API for operations on dummy devices, such as enabling and
disabling them. It also provides the Coresight dummy sink/source paths for
debugging.
Config details
--------------
There are two types of nodes, dummy sink and dummy source. These nodes
are available at ``/sys/bus/coresight/devices``.
Example output::
$ ls -l /sys/bus/coresight/devices | grep dummy
dummy_sink0 -> ../../../devices/platform/soc@0/soc@0:sink/dummy_sink0
dummy_source0 -> ../../../devices/platform/soc@0/soc@0:source/dummy_source0
@@ -0,0 +1,226 @@
.. SPDX-License-Identifier: GPL-2.0
=============================================
CoreSight Embedded Cross Trigger (CTI & CTM).
=============================================
:Author: Mike Leach <mike.leach@linaro.org>
:Date: November 2019
Hardware Description
--------------------
The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes
individual input and output hardware signals known as triggers to and from
devices and interconnects them via the Cross Trigger Matrix (CTM) to other
devices via numbered channels, in order to propagate events between devices.
e.g.::
0000000 in_trigs :::::::
0 C 0----------->: : +======>(other CTI channel IO)
0 P 0<-----------: : v
0 U 0 out_trigs : : Channels ***** :::::::
0000000 : CTI :<=========>*CTM*<====>: CTI :---+
####### in_trigs : : (id 0-3) ***** ::::::: v
# ETM #----------->: : ^ #######
# #<-----------: : +---# ETR #
####### out_trigs ::::::: #######
The CTI driver enables the programming of the CTI to attach triggers to
channels. When an input trigger becomes active, the attached channel will
become active. Any output trigger attached to that channel will also
become active. The active channel is propagated to other CTIs via the CTM,
activating connected output triggers there, unless filtered by the CTI
channel gate.
It is also possible to activate a channel using system software directly
programming registers in the CTI.
The CTIs are registered by the system to be associated with CPUs and/or other
CoreSight devices on the trace data path. When these devices are enabled the
attached CTIs will also be enabled. By default/on power up the CTIs have
no programmed trigger/channel attachments, so will not affect the system
until explicitly programmed.
The hardware trigger connections between CTIs and devices is implementation
defined, unless the CPU/ETM combination is a v8 architecture, in which case
the connections have an architecturally defined standard layout.
The hardware trigger signals can also be connected to non-CoreSight devices
(e.g. UART), or be propagated off chip as hardware IO lines.
All the CTI devices are associated with a CTM. On many systems there will be a
single effective CTM (one CTM, or multiple CTMs all interconnected), but it is
possible that systems can have nets of CTIs+CTM that are not interconnected by
a CTM to each other. On these systems a CTM index is declared to associate
CTI devices that are interconnected via a given CTM.
Sysfs files and directories
---------------------------
The CTI devices appear on the existing CoreSight bus alongside the other
CoreSight devices::
>$ ls /sys/bus/coresight/devices
cti_cpu0 cti_cpu2 cti_sys0 etm0 etm2 funnel0 replicator0 tmc_etr0
cti_cpu1 cti_cpu3 cti_sys1 etm1 etm3 funnel1 tmc_etf0 tpiu0
The ``cti_cpu<N>`` named CTIs are associated with a CPU, and any ETM used by
that core. The ``cti_sys<N>`` CTIs are general system infrastructure CTIs that
can be associated with other CoreSight devices, or other system hardware
capable of generating or using trigger signals.::
>$ ls /sys/bus/coresight/devices/etm0/cti_cpu0
channels ctmid enable nr_trigger_cons mgmt power powered regs
connections subsystem triggers0 triggers1 uevent
*Key file items are:-*
* ``enable``: enables/disables the CTI. Read to determine current state.
If this shows as enabled (1), but ``powered`` shows unpowered (0), then
the enable indicates a request to enabled when the device is powered.
* ``ctmid`` : associated CTM - only relevant if system has multiple CTI+CTM
clusters that are not interconnected.
* ``nr_trigger_cons`` : total connections - triggers<N> directories.
* ``powered`` : Read to determine if the CTI is currently powered.
*Sub-directories:-*
* ``triggers<N>``: contains list of triggers for an individual connection.
* ``channels``: Contains the channel API - CTI main programming interface.
* ``regs``: Gives access to the raw programmable CTI regs.
* ``mgmt``: the standard CoreSight management registers.
* ``connections``: Links to connected *CoreSight* devices. The number of
links can be 0 to ``nr_trigger_cons``. Actual number given by ``nr_links``
in this directory.
triggers<N> directories
~~~~~~~~~~~~~~~~~~~~~~~
Individual trigger connection information. This describes trigger signals for
CoreSight and non-CoreSight connections.
Each triggers directory has a set of parameters describing the triggers for
the connection.
* ``name`` : name of connection
* ``in_signals`` : input trigger signal indexes used in this connection.
* ``in_types`` : functional types for in signals.
* ``out_signals`` : output trigger signals for this connection.
* ``out_types`` : functional types for out signals.
e.g::
>$ ls ./cti_cpu0/triggers0/
in_signals in_types name out_signals out_types
>$ cat ./cti_cpu0/triggers0/name
cpu0
>$ cat ./cti_cpu0/triggers0/out_signals
0-2
>$ cat ./cti_cpu0/triggers0/out_types
pe_edbgreq pe_dbgrestart pe_ctiirq
>$ cat ./cti_cpu0/triggers0/in_signals
0-1
>$ cat ./cti_cpu0/triggers0/in_types
pe_dbgtrigger pe_pmuirq
If a connection has zero signals in either the 'in' or 'out' triggers then
those parameters will be omitted.
Channels API Directory
~~~~~~~~~~~~~~~~~~~~~~
This provides an easy way to attach triggers to channels, without needing
the multiple register operations that are required if manipulating the
'regs' sub-directory elements directly.
A number of files provide this API::
>$ ls ./cti_sys0/channels/
chan_clear chan_inuse chan_xtrigs_out trigin_attach
chan_free chan_pulse chan_xtrigs_reset trigin_detach
chan_gate_disable chan_set chan_xtrigs_sel trigout_attach
chan_gate_enable chan_xtrigs_in trig_filter_enable trigout_detach
trigout_filtered
Most access to these elements take the form::
echo <chan> [<trigger>] > /<device_path>/<operation>
where the optional <trigger> is only needed for trigXX_attach | detach
operations.
e.g.::
>$ echo 0 1 > ./cti_sys0/channels/trigout_attach
>$ echo 0 > ./cti_sys0/channels/chan_set
Attaches trigout(1) to channel(0), then activates channel(0) generating a
set state on cti_sys0.trigout(1)
*API operations*
* ``trigin_attach, trigout_attach``: Attach a channel to a trigger signal.
* ``trigin_detach, trigout_detach``: Detach a channel from a trigger signal.
* ``chan_set``: Set the channel - the set state will be propagated around
the CTM to other connected devices.
* ``chan_clear``: Clear the channel.
* ``chan_pulse``: Set the channel for a single CoreSight clock cycle.
* ``chan_gate_enable``: Write operation sets the CTI gate to propagate
(enable) the channel to other devices. This operation takes a channel
number. CTI gate is enabled for all channels by default at power up. Read
to list the currently enabled channels on the gate.
* ``chan_gate_disable``: Write channel number to disable gate for that
channel.
* ``chan_inuse``: Show the current channels attached to any signal
* ``chan_free``: Show channels with no attached signals.
* ``chan_xtrigs_sel``: write a channel number to select a channel to view,
read to show the selected channel number.
* ``chan_xtrigs_in``: Read to show the input triggers attached to
the selected view channel.
* ``chan_xtrigs_out``:Read to show the output triggers attached to
the selected view channel.
* ``trig_filter_enable``: Defaults to enabled, disable to allow potentially
dangerous output signals to be set.
* ``trigout_filtered``: Trigger out signals that are prevented from being
set if filtering ``trig_filter_enable`` is enabled. One use is to prevent
accidental ``EDBGREQ`` signals stopping a core.
* ``chan_xtrigs_reset``: Write 1 to clear all channel / trigger programming.
Resets device hardware to default state.
The example below attaches input trigger index 1 to channel 2, and output
trigger index 6 to the same channel. It then examines the state of the
channel / trigger connections using the appropriate sysfs attributes.
The settings mean that if either input trigger 1, or channel 2 go active then
trigger out 6 will go active. We then enable the CTI, and use the software
channel control to activate channel 2. We see the active channel on the
``choutstatus`` register and the active signal on the ``trigoutstatus``
register. Finally clearing the channel removes this.
e.g.::
.../cti_sys0/channels# echo 2 1 > trigin_attach
.../cti_sys0/channels# echo 2 6 > trigout_attach
.../cti_sys0/channels# cat chan_free
0-1,3
.../cti_sys0/channels# cat chan_inuse
2
.../cti_sys0/channels# echo 2 > chan_xtrigs_sel
.../cti_sys0/channels# cat chan_xtrigs_trigin
1
.../cti_sys0/channels# cat chan_xtrigs_trigout
6
.../cti_sys0/# echo 1 > enable
.../cti_sys0/channels# echo 2 > chan_set
.../cti_sys0/channels# cat ../regs/choutstatus
0x4
.../cti_sys0/channels# cat ../regs/trigoutstatus
0x40
.../cti_sys0/channels# echo 2 > chan_clear
.../cti_sys0/channels# cat ../regs/trigoutstatus
0x0
.../cti_sys0/channels# cat ../regs/choutstatus
0x0
@@ -0,0 +1,827 @@
===============================================
ETMv4 sysfs linux driver programming reference.
===============================================
:Author: Mike Leach <mike.leach@linaro.org>
:Date: October 11th, 2019
Supplement to existing ETMv4 driver documentation.
Sysfs files and directories
---------------------------
Root: ``/sys/bus/coresight/devices/etm<N>``
The following paragraphs explain the association between sysfs files and the
ETMv4 registers that they effect. Note the register names are given without
the ‘TRC’ prefix.
----
:File: ``mode`` (rw)
:Trace Registers: {CONFIGR + others}
:Notes:
Bit select trace features. See ‘mode’ section below. Bits
in this will cause equivalent programming of trace config and
other registers to enable the features requested.
:Syntax & eg:
``echo bitfield > mode``
bitfield up to 32 bits setting trace features.
:Example:
``$> echo 0x012 > mode``
----
:File: ``reset`` (wo)
:Trace Registers: All
:Notes:
Reset all programming to trace nothing / no logic programmed.
:Syntax:
``echo 1 > reset``
----
:File: ``enable_source`` (wo)
:Trace Registers: PRGCTLR, All hardware regs.
:Notes:
- > 0 : Programs up the hardware with the current values held in the driver
and enables trace.
- = 0 : disable trace hardware.
:Syntax:
``echo 1 > enable_source``
----
:File: ``cpu`` (ro)
:Trace Registers: None.
:Notes:
CPU ID that this ETM is attached to.
:Example:
``$> cat cpu``
``$> 0``
----
:File: ``ts_source`` (ro)
:Trace Registers: None.
:Notes:
When FEAT_TRF is implemented, value of TRFCR_ELx.TS used for trace session. Otherwise -1
indicates an unknown time source. Check trcidr0.tssize to see if a global timestamp is
available.
:Example:
``$> cat ts_source``
``$> 1``
----
:File: ``addr_idx`` (rw)
:Trace Registers: None.
:Notes:
Virtual register to index address comparator and range
features. Set index for first of the pair in a range.
:Syntax:
``echo idx > addr_idx``
Where idx < nr_addr_cmp x 2
----
:File: ``addr_range`` (rw)
:Trace Registers: ACVR[idx, idx+1], VIIECTLR
:Notes:
Pair of addresses for a range selected by addr_idx. Include
/ exclude according to the optional parameter, or if omitted
uses the current ‘mode’ setting. Select comparator range in
control register. Error if index is odd value.
:Depends: ``mode, addr_idx``
:Syntax:
``echo addr1 addr2 [exclude] > addr_range``
Where addr1 and addr2 define the range and addr1 < addr2.
Optional exclude value:-
- 0 for include
- 1 for exclude.
:Example:
``$> echo 0x0000 0x2000 0 > addr_range``
----
:File: ``addr_single`` (rw)
:Trace Registers: ACVR[idx]
:Notes:
Set a single address comparator according to addr_idx. This
is used if the address comparator is used as part of event
generation logic etc.
:Depends: ``addr_idx``
:Syntax:
``echo addr1 > addr_single``
----
:File: ``addr_start`` (rw)
:Trace Registers: ACVR[idx], VISSCTLR
:Notes:
Set a trace start address comparator according to addr_idx.
Select comparator in control register.
:Depends: ``addr_idx``
:Syntax:
``echo addr1 > addr_start``
----
:File: ``addr_stop`` (rw)
:Trace Registers: ACVR[idx], VISSCTLR
:Notes:
Set a trace stop address comparator according to addr_idx.
Select comparator in control register.
:Depends: ``addr_idx``
:Syntax:
``echo addr1 > addr_stop``
----
:File: ``addr_context`` (rw)
:Trace Registers: ACATR[idx,{6:4}]
:Notes:
Link context ID comparator to address comparator addr_idx
:Depends: ``addr_idx``
:Syntax:
``echo ctxt_idx > addr_context``
Where ctxt_idx is the index of the linked context id / vmid
comparator.
----
:File: ``addr_ctxtype`` (rw)
:Trace Registers: ACATR[idx,{3:2}]
:Notes:
Input value string. Set type for linked context ID comparator
:Depends: ``addr_idx``
:Syntax:
``echo type > addr_ctxtype``
Type one of {all, vmid, ctxid, none}
:Example:
``$> echo ctxid > addr_ctxtype``
----
:File: ``addr_exlevel_s_ns`` (rw)
:Trace Registers: ACATR[idx,{14:8}]
:Notes:
Set the ELx secure and non-secure matching bits for the
selected address comparator
:Depends: ``addr_idx``
:Syntax:
``echo val > addr_exlevel_s_ns``
val is a 7 bit value for exception levels to exclude. Input
value shifted to correct bits in register.
:Example:
``$> echo 0x4F > addr_exlevel_s_ns``
----
:File: ``addr_instdatatype`` (rw)
:Trace Registers: ACATR[idx,{1:0}]
:Notes:
Set the comparator address type for matching. Driver only
supports setting instruction address type.
:Depends: ``addr_idx``
----
:File: ``addr_cmp_view`` (ro)
:Trace Registers: ACVR[idx, idx+1], ACATR[idx], VIIECTLR
:Notes:
Read the currently selected address comparator. If part of
address range then display both addresses.
:Depends: ``addr_idx``
:Syntax:
``cat addr_cmp_view``
:Example:
``$> cat addr_cmp_view``
``addr_cmp[0] range 0x0 0xffffffffffffffff include ctrl(0x4b00)``
----
:File: ``nr_addr_cmp`` (ro)
:Trace Registers: From IDR4
:Notes:
Number of address comparator pairs
----
:File: ``sshot_idx`` (rw)
:Trace Registers: None
:Notes:
Select single shot register set.
----
:File: ``sshot_ctrl`` (rw)
:Trace Registers: SSCCR[idx]
:Notes:
Access a single shot comparator control register.
:Depends: ``sshot_idx``
:Syntax:
``echo val > sshot_ctrl``
Writes val into the selected control register.
----
:File: ``sshot_status`` (ro)
:Trace Registers: SSCSR[idx]
:Notes:
Read a single shot comparator status register
:Depends: ``sshot_idx``
:Syntax:
``cat sshot_status``
Read status.
:Example:
``$> cat sshot_status``
``0x1``
----
:File: ``sshot_pe_ctrl`` (rw)
:Trace Registers: SSPCICR[idx]
:Notes:
Access a single shot PE comparator input control register.
:Depends: ``sshot_idx``
:Syntax:
``echo val > sshot_pe_ctrl``
Writes val into the selected control register.
----
:File: ``ns_exlevel_vinst`` (rw)
:Trace Registers: VICTLR{23:20}
:Notes:
Program non-secure exception level filters. Set / clear NS
exception filter bits. Setting ‘1’ excludes trace from the
exception level.
:Syntax:
``echo bitfield > ns_exlevel_viinst``
Where bitfield contains bits to set clear for EL0 to EL2
:Example:
``%> echo 0x4 > ns_exlevel_viinst``
Excludes EL2 NS trace.
----
:File: ``vinst_pe_cmp_start_stop`` (rw)
:Trace Registers: VIPCSSCTLR
:Notes:
Access PE start stop comparator input control registers
----
:File: ``bb_ctrl`` (rw)
:Trace Registers: BBCTLR
:Notes:
Define ranges that Branch Broadcast will operate in.
Default (0x0) is all addresses.
:Depends: BB enabled.
----
:File: ``cyc_threshold`` (rw)
:Trace Registers: CCCTLR
:Notes:
Set the threshold for which cycle counts will be emitted.
Error if attempt to set below minimum defined in IDR3, masked
to width of valid bits.
:Depends: CC enabled.
----
:File: ``syncfreq`` (rw)
:Trace Registers: SYNCPR
:Notes:
Set trace synchronisation period. Power of 2 value, 0 (off)
or 8-20. Driver defaults to 12 (every 4096 bytes).
----
:File: ``cntr_idx`` (rw)
:Trace Registers: none
:Notes:
Select the counter to access
:Syntax:
``echo idx > cntr_idx``
Where idx < nr_cntr
----
:File: ``cntr_ctrl`` (rw)
:Trace Registers: CNTCTLR[idx]
:Notes:
Set counter control value.
:Depends: ``cntr_idx``
:Syntax:
``echo val > cntr_ctrl``
Where val is per ETMv4 spec.
----
:File: ``cntrldvr`` (rw)
:Trace Registers: CNTRLDVR[idx]
:Notes:
Set counter reload value.
:Depends: ``cntr_idx``
:Syntax:
``echo val > cntrldvr``
Where val is per ETMv4 spec.
----
:File: ``nr_cntr`` (ro)
:Trace Registers: From IDR5
:Notes:
Number of counters implemented.
----
:File: ``ctxid_idx`` (rw)
:Trace Registers: None
:Notes:
Select the context ID comparator to access
:Syntax:
``echo idx > ctxid_idx``
Where idx < numcidc
----
:File: ``ctxid_pid`` (rw)
:Trace Registers: CIDCVR[idx]
:Notes:
Set the context ID comparator value
:Depends: ``ctxid_idx``
----
:File: ``ctxid_masks`` (rw)
:Trace Registers: CIDCCTLR0, CIDCCTLR1, CIDCVR<0-7>
:Notes:
Pair of values to set the byte masks for 1-8 context ID
comparators. Automatically clears masked bytes to 0 in CID
value registers.
:Syntax:
``echo m3m2m1m0 [m7m6m5m4] > ctxid_masks``
32 bit values made up of mask bytes, where mN represents a
byte mask value for Context ID comparator N.
Second value not required on systems that have fewer than 4
context ID comparators
----
:File: ``numcidc`` (ro)
:Trace Registers: From IDR4
:Notes:
Number of Context ID comparators
----
:File: ``vmid_idx`` (rw)
:Trace Registers: None
:Notes:
Select the VM ID comparator to access.
:Syntax:
``echo idx > vmid_idx``
Where idx < numvmidc
----
:File: ``vmid_val`` (rw)
:Trace Registers: VMIDCVR[idx]
:Notes:
Set the VM ID comparator value
:Depends: ``vmid_idx``
----
:File: ``vmid_masks`` (rw)
:Trace Registers: VMIDCCTLR0, VMIDCCTLR1, VMIDCVR<0-7>
:Notes:
Pair of values to set the byte masks for 1-8 VM ID comparators.
Automatically clears masked bytes to 0 in VMID value registers.
:Syntax:
``echo m3m2m1m0 [m7m6m5m4] > vmid_masks``
Where mN represents a byte mask value for VMID comparator N.
Second value not required on systems that have fewer than 4
VMID comparators.
----
:File: ``numvmidc`` (ro)
:Trace Registers: From IDR4
:Notes:
Number of VMID comparators
----
:File: ``res_idx`` (rw)
:Trace Registers: None.
:Notes:
Select the resource selector control to access. Must be 2 or
higher as selectors 0 and 1 are hardwired.
:Syntax:
``echo idx > res_idx``
Where 2 <= idx < nr_resource x 2
----
:File: ``res_ctrl`` (rw)
:Trace Registers: RSCTLR[idx]
:Notes:
Set resource selector control value. Value per ETMv4 spec.
:Depends: ``res_idx``
:Syntax:
``echo val > res_cntr``
Where val is per ETMv4 spec.
----
:File: ``nr_resource`` (ro)
:Trace Registers: From IDR4
:Notes:
Number of resource selector pairs
----
:File: ``event`` (rw)
:Trace Registers: EVENTCTRL0R
:Notes:
Set up to 4 implemented event fields.
:Syntax:
``echo ev3ev2ev1ev0 > event``
Where evN is an 8 bit event field. Up to 4 event fields make up the
32-bit input value. Number of valid fields is implementation dependent,
defined in IDR0.
----
:File: ``event_instren`` (rw)
:Trace Registers: EVENTCTRL1R
:Notes:
Choose events which insert event packets into trace stream.
:Depends: EVENTCTRL0R
:Syntax:
``echo bitfield > event_instren``
Where bitfield is up to 4 bits according to number of event fields.
----
:File: ``event_ts`` (rw)
:Trace Registers: TSCTLR
:Notes:
Set the event that will generate timestamp requests.
:Depends: ``TS activated``
:Syntax:
``echo evfield > event_ts``
Where evfield is an 8 bit event selector.
----
:File: ``seq_idx`` (rw)
:Trace Registers: None
:Notes:
Sequencer event register select - 0 to 2
----
:File: ``seq_state`` (rw)
:Trace Registers: SEQSTR
:Notes:
Sequencer current state - 0 to 3.
----
:File: ``seq_event`` (rw)
:Trace Registers: SEQEVR[idx]
:Notes:
State transition event registers
:Depends: ``seq_idx``
:Syntax:
``echo evBevF > seq_event``
Where evBevF is a 16 bit value made up of two event selectors,
- evB : back
- evF : forwards.
----
:File: ``seq_reset_event`` (rw)
:Trace Registers: SEQRSTEVR
:Notes:
Sequencer reset event
:Syntax:
``echo evfield > seq_reset_event``
Where evfield is an 8 bit event selector.
----
:File: ``nrseqstate`` (ro)
:Trace Registers: From IDR5
:Notes:
Number of sequencer states (0 or 4)
----
:File: ``nr_pe_cmp`` (ro)
:Trace Registers: From IDR4
:Notes:
Number of PE comparator inputs
----
:File: ``nr_ext_inp`` (ro)
:Trace Registers: From IDR5
:Notes:
Number of external inputs
----
:File: ``nr_ss_cmp`` (ro)
:Trace Registers: From IDR4
:Notes:
Number of Single Shot control registers
----
*Note:* When programming any address comparator the driver will tag the
comparator with a type used - i.e. RANGE, SINGLE, START, STOP. Once this tag
is set, then only the values can be changed using the same sysfs file / type
used to program it.
Thus::
% echo 0 > addr_idx ; select address comparator 0
% echo 0x1000 0x5000 0 > addr_range ; set address range on comparators 0, 1.
% echo 0x2000 > addr_start ; error as comparator 0 is a range comparator
% echo 2 > addr_idx ; select address comparator 2
% echo 0x2000 > addr_start ; this is OK as comparator 2 is unused.
% echo 0x3000 > addr_stop ; error as comparator 2 set as start address.
% echo 2 > addr_idx ; select address comparator 3
% echo 0x3000 > addr_stop ; this is OK
To remove programming on all the comparators (and all the other hardware) use
the reset parameter::
% echo 1 > reset
The ‘mode’ sysfs parameter.
---------------------------
This is a bitfield selection parameter that sets the overall trace mode for the
ETM. The table below describes the bits, using the defines from the driver
source file, along with a description of the feature these represent. Many
features are optional and therefore dependent on implementation in the
hardware.
Bit assignments shown below:-
----
**bit (0):**
ETM_MODE_EXCLUDE
**description:**
This is the default value for the include / exclude function when
setting address ranges. Set 1 for exclude range. When the mode
parameter is set this value is applied to the currently indexed
address range.
.. _coresight-branch-broadcast:
**bit (4):**
ETM_MODE_BB
**description:**
Set to enable branch broadcast if supported in hardware [IDR0]. The primary use for this feature
is when code is patched dynamically at run time and the full program flow may not be able to be
reconstructed using only conditional branches.
There is currently no support in Perf for supplying modified binaries to the decoder, so this
feature is only intended to be used for debugging purposes or with a 3rd party tool.
Choosing this option will result in a significant increase in the amount of trace generated -
possible danger of overflows, or fewer instructions covered. Note, that this option also
overrides any setting of :ref:`ETM_MODE_RETURNSTACK <coresight-return-stack>`, so where a branch
broadcast range overlaps a return stack range, return stacks will not be available for that
range.
.. _coresight-cycle-accurate:
**bit (5):**
ETMv4_MODE_CYCACC
**description:**
Set to enable cycle accurate trace if supported [IDR0].
**bit (6):**
ETMv4_MODE_CTXID
**description:**
Set to enable context ID tracing if supported in hardware [IDR2].
**bit (7):**
ETM_MODE_VMID
**description:**
Set to enable virtual machine ID tracing if supported [IDR2].
.. _coresight-timestamp:
**bit (11):**
ETMv4_MODE_TIMESTAMP
**description:**
Set to enable timestamp generation if supported [IDR0].
.. _coresight-return-stack:
**bit (12):**
ETM_MODE_RETURNSTACK
**description:**
Set to enable trace return stack use if supported [IDR0].
**bit (13-14):**
ETM_MODE_QELEM(val)
**description:**
‘val’ determines level of Q element support enabled if
implemented by the ETM [IDR0]
**bit (19):**
ETM_MODE_ATB_TRIGGER
**description:**
Set to enable the ATBTRIGGER bit in the event control register
[EVENTCTLR1] if supported [IDR5].
**bit (20):**
ETM_MODE_LPOVERRIDE
**description:**
Set to enable the LPOVERRIDE bit in the event control register
[EVENTCTLR1], if supported [IDR5].
**bit (21):**
ETM_MODE_ISTALL_EN
**description:**
Set to enable the ISTALL bit in the stall control register
[STALLCTLR]
**bit (23):**
ETM_MODE_INSTPRIO
**description:**
Set to enable the INSTPRIORITY bit in the stall control register
[STALLCTLR] , if supported [IDR0].
**bit (24):**
ETM_MODE_NOOVERFLOW
**description:**
Set to enable the NOOVERFLOW bit in the stall control register
[STALLCTLR], if supported [IDR3].
**bit (25):**
ETM_MODE_TRACE_RESET
**description:**
Set to enable the TRCRESET bit in the viewinst control register
[VICTLR] , if supported [IDR3].
**bit (26):**
ETM_MODE_TRACE_ERR
**description:**
Set to enable the TRCCTRL bit in the viewinst control register
[VICTLR].
**bit (27):**
ETM_MODE_VIEWINST_STARTSTOP
**description:**
Set the initial state value of the ViewInst start / stop logic
in the viewinst control register [VICTLR]
**bit (30):**
ETM_MODE_EXCL_KERN
**description:**
Set default trace setup to exclude kernel mode trace (see note a)
**bit (31):**
ETM_MODE_EXCL_USER
**description:**
Set default trace setup to exclude user space trace (see note a)
----
*Note a)* On startup the ETM is programmed to trace the complete address space
using address range comparator 0. ‘mode’ bits 30 / 31 modify this setting to
set EL exclude bits for NS state in either user space (EL0) or kernel space
(EL1) in the address range comparator. (the default setting excludes all
secure EL, and NS EL2)
Once the reset parameter has been used, and/or custom programming has been
implemented - using these bits will result in the EL bits for address
comparator 0 being set in the same way.
*Note b)* Bits 2-3, 8-10, 15-16, 18, 22, control features that only work with
data trace. As A-profile data trace is architecturally prohibited in ETMv4,
these have been omitted here. Possible uses could be where a kernel has
support for control of R or M profile infrastructure as part of a heterogeneous
system.
Bits 17, 28-29 are unused.
@@ -0,0 +1,117 @@
.. SPDX-License-Identifier: GPL-2.0
================
CoreSight - Perf
================
:Author: Carsten Haitzler <carsten.haitzler@arm.com>
:Date: June 29th, 2022
Perf is able to locally access CoreSight trace data and store it to the
output perf data files. This data can then be later decoded to give the
instructions that were traced for debugging or profiling purposes. You
can log such data with a perf record command like::
perf record -e cs_etm//u testbinary
This would run some test binary (testbinary) until it exits and record
a perf.data trace file. That file would have AUX sections if CoreSight
is working correctly. You can dump the content of this file as
readable text with a command like::
perf report --stdio --dump -i perf.data
You should find some sections of this file have AUX data blocks like::
0x1e78 [0x30]: PERF_RECORD_AUXTRACE size: 0x11dd0 offset: 0 ref: 0x1b614fc1061b0ad1 idx: 0 tid: 531230 cpu: -1
. ... CoreSight ETM Trace data: size 73168 bytes
Idx:0; ID:10; I_ASYNC : Alignment Synchronisation.
Idx:12; ID:10; I_TRACE_INFO : Trace Info.; INFO=0x0 { CC.0 }
Idx:17; ID:10; I_ADDR_L_64IS0 : Address, Long, 64 bit, IS0.; Addr=0x0000000000000000;
Idx:26; ID:10; I_TRACE_ON : Trace On.
Idx:27; ID:10; I_ADDR_CTXT_L_64IS0 : Address & Context, Long, 64 bit, IS0.; Addr=0x0000FFFFB6069140; Ctxt: AArch64,EL0, NS;
Idx:38; ID:10; I_ATOM_F6 : Atom format 6.; EEEEEEEEEEEEEEEEEEEEEEEE
Idx:39; ID:10; I_ATOM_F6 : Atom format 6.; EEEEEEEEEEEEEEEEEEEEEEEE
Idx:40; ID:10; I_ATOM_F6 : Atom format 6.; EEEEEEEEEEEEEEEEEEEEEEEE
Idx:41; ID:10; I_ATOM_F6 : Atom format 6.; EEEEEEEEEEEN
...
If you see these above, then your system is tracing CoreSight data
correctly.
To compile perf with CoreSight support in the tools/perf directory do::
make CORESIGHT=1
This requires OpenCSD to build. You may install distribution packages
for the support such as libopencsd and libopencsd-dev or download it
and build yourself. Upstream OpenCSD is located at:
https://github.com/Linaro/OpenCSD
For complete information on building perf with CoreSight support and
more extensive usage look at:
https://github.com/Linaro/OpenCSD/blob/master/HOWTO.md
Kernel CoreSight Support
------------------------
You will also want CoreSight support enabled in your kernel config.
Ensure it is enabled with::
CONFIG_CORESIGHT=y
There are various other CoreSight options you probably also want
enabled like::
CONFIG_CORESIGHT_LINKS_AND_SINKS=y
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
CONFIG_CORESIGHT_CATU=y
CONFIG_CORESIGHT_SINK_TPIU=y
CONFIG_CORESIGHT_SINK_ETBV10=y
CONFIG_CORESIGHT_SOURCE_ETM4X=y
CONFIG_CORESIGHT_CTI=y
CONFIG_CORESIGHT_CTI_INTEGRATION_REGS=y
Please refer to the kernel configuration help for more information.
Fine-grained tracing with AUX pause and resume
----------------------------------------------
Arm CoreSight may generate a large amount of hardware trace data, which
will lead to overhead in recording and distract users when reviewing
profiling result. To mitigate the issue of excessive trace data, Perf
provides AUX pause and resume functionality for fine-grained tracing.
The AUX pause and resume can be triggered by associated events. These
events can be ftrace tracepoints (including static and dynamic
tracepoints) or PMU events (e.g. CPU PMU cycle event). To create a perf
session with AUX pause / resume, three configuration terms are
introduced:
- "aux-action=start-paused": it is specified for the cs_etm PMU event to
launch in a paused state.
- "aux-action=pause": an associated event is specified with this term
to pause AUX trace.
- "aux-action=resume": an associated event is specified with this term
to resume AUX trace.
Example for triggering AUX pause and resume with ftrace tracepoints::
perf record -e cs_etm/aux-action=start-paused/k,syscalls:sys_enter_openat/aux-action=resume/,syscalls:sys_exit_openat/aux-action=pause/ ls
Example for triggering AUX pause and resume with PMU event::
perf record -a -e cs_etm/aux-action=start-paused/k \
-e cycles/aux-action=pause,period=10000000/ \
-e cycles/aux-action=resume,period=1050000/ -- sleep 1
Perf test - Verify kernel and userspace perf CoreSight work
-----------------------------------------------------------
There are a set of Perf tests for CoreSight which can be run with::
sudo perf test coresight
@@ -0,0 +1,52 @@
.. SPDX-License-Identifier: GPL-2.0
=================================================================
The trace performance monitoring and diagnostics aggregator(TPDA)
=================================================================
:Author: Jinlong Mao <quic_jinlmao@quicinc.com>
:Date: January 2023
Hardware Description
--------------------
TPDA - The trace performance monitoring and diagnostics aggregator or
TPDA in short serves as an arbitration and packetization engine for the
performance monitoring and diagnostics network specification.
The primary use case of the TPDA is to provide packetization, funneling
and timestamping of Monitor data.
Sysfs files and directories
---------------------------
Root: ``/sys/bus/coresight/devices/tpda<N>``
Config details
---------------------------
The tpdm and tpda nodes should be observed at the coresight path
"/sys/bus/coresight/devices".
e.g.
/sys/bus/coresight/devices # ls -l | grep tpd
tpda0 -> ../../../devices/platform/soc@0/6004000.tpda/tpda0
tpdm0 -> ../../../devices/platform/soc@0/6c08000.mm.tpdm/tpdm0
We can use the commands are similar to the below to validate TPDMs.
Enable coresight sink first. The port of tpda which is connected to
the tpdm will be enabled after commands below.
echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
The test data will be collected in the coresight sink which is enabled.
If rwp register of the sink is keeping updating when do
integration_test (by cat tmc_etf0/mgmt/rwp), it means there is data
generated from TPDM to sink.
There must be a tpda between tpdm and the sink. When there are some
other trace event hw components in the same HW block with tpdm, tpdm
and these hw components will connect to the coresight funnel. When
there is only tpdm trace hw in the HW block, tpdm will connect to
tpda directly.
@@ -0,0 +1,45 @@
.. SPDX-License-Identifier: GPL-2.0
==========================================================
Trace performance monitoring and diagnostics monitor(TPDM)
==========================================================
:Author: Jinlong Mao <quic_jinlmao@quicinc.com>
:Date: January 2023
Hardware Description
--------------------
TPDM - The trace performance monitoring and diagnostics monitor or TPDM in
short serves as data collection component for various dataset types.
The primary use case of the TPDM is to collect data from different data
sources and send it to a TPDA for packetization, timestamping and funneling.
Sysfs files and directories
---------------------------
Root: ``/sys/bus/coresight/devices/tpdm<N>``
----
:File: ``enable_source`` (RW)
:Notes:
- > 0 : enable the datasets of TPDM.
- = 0 : disable the datasets of TPDM.
:Syntax:
``echo 1 > enable_source``
----
:File: ``integration_test`` (wo)
:Notes:
Integration test will generate test data for tpdm.
:Syntax:
``echo value > integration_test``
value - 1 or 2.
----
.. This text is intentionally added to make Sphinx happy.
@@ -0,0 +1,38 @@
.. SPDX-License-Identifier: GPL-2.0
==============================
Trace Buffer Extension (TRBE).
==============================
:Author: Anshuman Khandual <anshuman.khandual@arm.com>
:Date: November 2020
Hardware Description
--------------------
Trace Buffer Extension (TRBE) is a percpu hardware which captures in system
memory, CPU traces generated from a corresponding percpu tracing unit. This
gets plugged in as a coresight sink device because the corresponding trace
generators (ETE), are plugged in as source device.
The TRBE is not compliant to CoreSight architecture specifications, but is
driven via the CoreSight driver framework to support the ETE (which is
CoreSight compliant) integration.
Sysfs files and directories
---------------------------
The TRBE devices appear on the existing coresight bus alongside the other
coresight devices::
>$ ls /sys/bus/coresight/devices
trbe0 trbe1 trbe2 trbe3
The ``trbe<N>`` named TRBEs are associated with a CPU.::
>$ ls /sys/bus/coresight/devices/trbe0/
align flag
*Key file items are:-*
* ``align``: TRBE write pointer alignment
* ``flag``: TRBE updates memory with access and dirty flags
@@ -0,0 +1,698 @@
======================================
Coresight - HW Assisted Tracing on ARM
======================================
:Author: Mathieu Poirier <mathieu.poirier@linaro.org>
:Date: September 11th, 2014
Introduction
------------
Coresight is an umbrella of technologies allowing for the debugging of ARM
based SoC. It includes solutions for JTAG and HW assisted tracing. This
document is concerned with the latter.
HW assisted tracing is becoming increasingly useful when dealing with systems
that have many SoCs and other components like GPU and DMA engines. ARM has
developed a HW assisted tracing solution by means of different components, each
being added to a design at synthesis time to cater to specific tracing needs.
Components are generally categorised as source, link and sinks and are
(usually) discovered using the AMBA bus.
"Sources" generate a compressed stream representing the processor instruction
path based on tracing scenarios as configured by users. From there the stream
flows through the coresight system (via ATB bus) using links that are connecting
the emanating source to a sink(s). Sinks serve as endpoints to the coresight
implementation, either storing the compressed stream in a memory buffer or
creating an interface to the outside world where data can be transferred to a
host without fear of filling up the onboard coresight memory buffer.
At typical coresight system would look like this::
*****************************************************************
**************************** AMBA AXI ****************************===||
***************************************************************** ||
^ ^ | ||
| | * **
0000000 ::::: 0000000 ::::: ::::: @@@@@@@ ||||||||||||
0 CPU 0<-->: C : 0 CPU 0<-->: C : : C : @ STM @ || System ||
|->0000000 : T : |->0000000 : T : : T :<--->@@@@@ || Memory ||
| #######<-->: I : | #######<-->: I : : I : @@@<-| ||||||||||||
| # ETM # ::::: | # PTM # ::::: ::::: @ |
| ##### ^ ^ | ##### ^ ! ^ ! . | |||||||||
| |->### | ! | |->### | ! | ! . | || DAP ||
| | # | ! | | # | ! | ! . | |||||||||
| | . | ! | | . | ! | ! . | | |
| | . | ! | | . | ! | ! . | | *
| | . | ! | | . | ! | ! . | | SWD/
| | . | ! | | . | ! | ! . | | JTAG
*****************************************************************<-|
*************************** AMBA Debug APB ************************
*****************************************************************
| . ! . ! ! . |
| . * . * * . |
*****************************************************************
******************** Cross Trigger Matrix (CTM) *******************
*****************************************************************
| . ^ . . |
| * ! * * |
*****************************************************************
****************** AMBA Advanced Trace Bus (ATB) ******************
*****************************************************************
| ! =============== |
| * ===== F =====<---------|
| ::::::::: ==== U ====
|-->:: CTI ::<!! === N ===
| ::::::::: ! == N ==
| ^ * == E ==
| ! &&&&&&&&& IIIIIII == L ==
|------>&& ETB &&<......II I =======
| ! &&&&&&&&& II I .
| ! I I .
| ! I REP I<..........
| ! I I
| !!>&&&&&&&&& II I *Source: ARM ltd.
|------>& TPIU &<......II I DAP = Debug Access Port
&&&&&&&&& IIIIIII ETM = Embedded Trace Macrocell
; PTM = Program Trace Macrocell
; CTI = Cross Trigger Interface
* ETB = Embedded Trace Buffer
To trace port TPIU= Trace Port Interface Unit
SWD = Serial Wire Debug
While on target configuration of the components is done via the APB bus,
all trace data are carried out-of-band on the ATB bus. The CTM provides
a way to aggregate and distribute signals between CoreSight components.
The coresight framework provides a central point to represent, configure and
manage coresight devices on a platform. This first implementation centers on
the basic tracing functionality, enabling components such ETM/PTM, funnel,
replicator, TMC, TPIU and ETB. Future work will enable more
intricate IP blocks such as STM and CTI.
Acronyms and Classification
---------------------------
Acronyms:
PTM:
Program Trace Macrocell
ETM:
Embedded Trace Macrocell
STM:
System trace Macrocell
ETB:
Embedded Trace Buffer
ITM:
Instrumentation Trace Macrocell
TPIU:
Trace Port Interface Unit
TMC-ETR:
Trace Memory Controller, configured as Embedded Trace Router
TMC-ETF:
Trace Memory Controller, configured as Embedded Trace FIFO
CTI:
Cross Trigger Interface
Classification:
Source:
ETMv3.x ETMv4, PTMv1.0, PTMv1.1, STM, STM500, ITM
Link:
Funnel, replicator (intelligent or not), TMC-ETR
Sinks:
ETBv1.0, ETB1.1, TPIU, TMC-ETF
Misc:
CTI
Device Tree Bindings
--------------------
See ``Documentation/devicetree/bindings/arm/arm,coresight-*.yaml`` for details.
As of this writing drivers for ITM, STMs and CTIs are not provided but are
expected to be added as the solution matures.
Framework and implementation
----------------------------
The coresight framework provides a central point to represent, configure and
manage coresight devices on a platform. Any coresight compliant device can
register with the framework for as long as they use the right APIs:
.. c:function:: struct coresight_device *coresight_register(struct coresight_desc *desc);
.. c:function:: void coresight_unregister(struct coresight_device *csdev);
The registering function is taking a ``struct coresight_desc *desc`` and
register the device with the core framework. The unregister function takes
a reference to a ``struct coresight_device *csdev`` obtained at registration time.
If everything goes well during the registration process the new devices will
show up under /sys/bus/coresight/devices, as showns here for a TC2 platform::
root:~# ls /sys/bus/coresight/devices/
replicator 20030000.tpiu 2201c000.ptm 2203c000.etm 2203e000.etm
20010000.etb 20040000.funnel 2201d000.ptm 2203d000.etm
root:~#
The functions take a ``struct coresight_device``, which looks like this::
struct coresight_desc {
enum coresight_dev_type type;
struct coresight_dev_subtype subtype;
const struct coresight_ops *ops;
struct coresight_platform_data *pdata;
struct device *dev;
const struct attribute_group **groups;
};
The "coresight_dev_type" identifies what the device is, i.e, source link or
sink while the "coresight_dev_subtype" will characterise that type further.
The ``struct coresight_ops`` is mandatory and will tell the framework how to
perform base operations related to the components, each component having
a different set of requirement. For that ``struct coresight_ops_sink``,
``struct coresight_ops_link`` and ``struct coresight_ops_source`` have been
provided.
The next field ``struct coresight_platform_data *pdata`` is acquired by calling
``of_get_coresight_platform_data()``, as part of the driver's _probe routine and
``struct device *dev`` gets the device reference embedded in the ``amba_device``::
static int etm_probe(struct amba_device *adev, const struct amba_id *id)
{
...
...
drvdata->dev = &adev->dev;
...
}
Specific class of device (source, link, or sink) have generic operations
that can be performed on them (see ``struct coresight_ops``). The ``**groups``
is a list of sysfs entries pertaining to operations
specific to that component only. "Implementation defined" customisations are
expected to be accessed and controlled using those entries.
Device Naming scheme
--------------------
The devices that appear on the "coresight" bus were named the same as their
parent devices, i.e, the real devices that appears on AMBA bus or the platform bus.
Thus the names were based on the Linux Open Firmware layer naming convention,
which follows the base physical address of the device followed by the device
type. e.g::
root:~# ls /sys/bus/coresight/devices/
20010000.etf 20040000.funnel 20100000.stm 22040000.etm
22140000.etm 230c0000.funnel 23240000.etm 20030000.tpiu
20070000.etr 20120000.replicator 220c0000.funnel
23040000.etm 23140000.etm 23340000.etm
However, with the introduction of ACPI support, the names of the real
devices are a bit cryptic and non-obvious. Thus, a new naming scheme was
introduced to use more generic names based on the type of the device. The
following rules apply::
1) Devices that are bound to CPUs, are named based on the CPU logical
number.
e.g, ETM bound to CPU0 is named "etm0"
2) All other devices follow a pattern, "<device_type_prefix>N", where :
<device_type_prefix> - A prefix specific to the type of the device
N - a sequential number assigned based on the order
of probing.
e.g, tmc_etf0, tmc_etr0, funnel0, funnel1
Thus, with the new scheme the devices could appear as ::
root:~# ls /sys/bus/coresight/devices/
etm0 etm1 etm2 etm3 etm4 etm5 funnel0
funnel1 funnel2 replicator0 stm0 tmc_etf0 tmc_etr0 tpiu0
Some of the examples below might refer to old naming scheme and some
to the newer scheme, to give a confirmation that what you see on your
system is not unexpected. One must use the "names" as they appear on
the system under specified locations.
Topology Representation
-----------------------
Each CoreSight component has a ``connections`` directory which will contain
links to other CoreSight components. This allows the user to explore the trace
topology and for larger systems, determine the most appropriate sink for a
given source. The connection information can also be used to establish
which CTI devices are connected to a given component. This directory contains a
``nr_links`` attribute detailing the number of links in the directory.
For an ETM source, in this case ``etm0`` on a Juno platform, a typical
arrangement will be::
linaro-developer:~# ls - l /sys/bus/coresight/devices/etm0/connections
<file details> cti_cpu0 -> ../../../23020000.cti/cti_cpu0
<file details> nr_links
<file details> out:0 -> ../../../230c0000.funnel/funnel2
Following the out port to ``funnel2``::
linaro-developer:~# ls -l /sys/bus/coresight/devices/funnel2/connections
<file details> in:0 -> ../../../23040000.etm/etm0
<file details> in:1 -> ../../../23140000.etm/etm3
<file details> in:2 -> ../../../23240000.etm/etm4
<file details> in:3 -> ../../../23340000.etm/etm5
<file details> nr_links
<file details> out:0 -> ../../../20040000.funnel/funnel0
And again to ``funnel0``::
linaro-developer:~# ls -l /sys/bus/coresight/devices/funnel0/connections
<file details> in:0 -> ../../../220c0000.funnel/funnel1
<file details> in:1 -> ../../../230c0000.funnel/funnel2
<file details> nr_links
<file details> out:0 -> ../../../20010000.etf/tmc_etf0
Finding the first sink ``tmc_etf0``. This can be used to collect data
as a sink, or as a link to propagate further along the chain::
linaro-developer:~# ls -l /sys/bus/coresight/devices/tmc_etf0/connections
<file details> cti_sys0 -> ../../../20020000.cti/cti_sys0
<file details> in:0 -> ../../../20040000.funnel/funnel0
<file details> nr_links
<file details> out:0 -> ../../../20150000.funnel/funnel4
via ``funnel4``::
linaro-developer:~# ls -l /sys/bus/coresight/devices/funnel4/connections
<file details> in:0 -> ../../../20010000.etf/tmc_etf0
<file details> in:1 -> ../../../20140000.etf/tmc_etf1
<file details> nr_links
<file details> out:0 -> ../../../20120000.replicator/replicator0
and a ``replicator0``::
linaro-developer:~# ls -l /sys/bus/coresight/devices/replicator0/connections
<file details> in:0 -> ../../../20150000.funnel/funnel4
<file details> nr_links
<file details> out:0 -> ../../../20030000.tpiu/tpiu0
<file details> out:1 -> ../../../20070000.etr/tmc_etr0
Arriving at the final sink in the chain, ``tmc_etr0``::
linaro-developer:~# ls -l /sys/bus/coresight/devices/tmc_etr0/connections
<file details> cti_sys0 -> ../../../20020000.cti/cti_sys0
<file details> in:0 -> ../../../20120000.replicator/replicator0
<file details> nr_links
As described below, when using sysfs it is sufficient to enable a sink and
a source for successful trace. The framework will correctly enable all
intermediate links as required.
Note: ``cti_sys0`` appears in two of the connections lists above.
CTIs can connect to multiple devices and are arranged in a star topology
via the CTM. See (Documentation/trace/coresight/coresight-ect.rst)
[#fourth]_ for further details.
Looking at this device we see 4 connections::
linaro-developer:~# ls -l /sys/bus/coresight/devices/cti_sys0/connections
<file details> nr_links
<file details> stm0 -> ../../../20100000.stm/stm0
<file details> tmc_etf0 -> ../../../20010000.etf/tmc_etf0
<file details> tmc_etr0 -> ../../../20070000.etr/tmc_etr0
<file details> tpiu0 -> ../../../20030000.tpiu/tpiu0
How to use the tracer modules
-----------------------------
There are two ways to use the Coresight framework:
1. using the perf cmd line tools.
2. interacting directly with the Coresight devices using the sysFS interface.
Preference is given to the former as using the sysFS interface
requires a deep understanding of the Coresight HW. The following sections
provide details on using both methods.
Using the sysFS interface
~~~~~~~~~~~~~~~~~~~~~~~~~
Before trace collection can start, a coresight sink needs to be identified.
There is no limit on the amount of sinks (nor sources) that can be enabled at
any given moment. As a generic operation, all device pertaining to the sink
class will have an "active" entry in sysfs::
root:/sys/bus/coresight/devices# ls
replicator 20030000.tpiu 2201c000.ptm 2203c000.etm 2203e000.etm
20010000.etb 20040000.funnel 2201d000.ptm 2203d000.etm
root:/sys/bus/coresight/devices# ls 20010000.etb
enable_sink status trigger_cntr
root:/sys/bus/coresight/devices# echo 1 > 20010000.etb/enable_sink
root:/sys/bus/coresight/devices# cat 20010000.etb/enable_sink
1
root:/sys/bus/coresight/devices#
At boot time the current etm3x driver will configure the first address
comparator with "_stext" and "_etext", essentially tracing any instruction
that falls within that range. As such "enabling" a source will immediately
trigger a trace capture::
root:/sys/bus/coresight/devices# echo 1 > 2201c000.ptm/enable_source
root:/sys/bus/coresight/devices# cat 2201c000.ptm/enable_source
1
root:/sys/bus/coresight/devices# cat 20010000.etb/status
Depth: 0x2000
Status: 0x1
RAM read ptr: 0x0
RAM wrt ptr: 0x19d3 <----- The write pointer is moving
Trigger cnt: 0x0
Control: 0x1
Flush status: 0x0
Flush ctrl: 0x2001
root:/sys/bus/coresight/devices#
Trace collection is stopped the same way::
root:/sys/bus/coresight/devices# echo 0 > 2201c000.ptm/enable_source
root:/sys/bus/coresight/devices#
The content of the ETB buffer can be harvested directly from /dev::
root:/sys/bus/coresight/devices# dd if=/dev/20010000.etb \
of=~/cstrace.bin
64+0 records in
64+0 records out
32768 bytes (33 kB) copied, 0.00125258 s, 26.2 MB/s
root:/sys/bus/coresight/devices#
The file cstrace.bin can be decompressed using "ptm2human", DS-5 or Trace32.
Following is a DS-5 output of an experimental loop that increments a variable up
to a certain value. The example is simple and yet provides a glimpse of the
wealth of possibilities that coresight provides.
::
Info Tracing enabled
Instruction 106378866 0x8026B53C E52DE004 false PUSH {lr}
Instruction 0 0x8026B540 E24DD00C false SUB sp,sp,#0xc
Instruction 0 0x8026B544 E3A03000 false MOV r3,#0
Instruction 0 0x8026B548 E58D3004 false STR r3,[sp,#4]
Instruction 0 0x8026B54C E59D3004 false LDR r3,[sp,#4]
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
Timestamp Timestamp: 17106715833
Instruction 319 0x8026B54C E59D3004 false LDR r3,[sp,#4]
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
Instruction 9 0x8026B54C E59D3004 false LDR r3,[sp,#4]
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
Instruction 7 0x8026B54C E59D3004 false LDR r3,[sp,#4]
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
Instruction 7 0x8026B54C E59D3004 false LDR r3,[sp,#4]
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
Instruction 10 0x8026B54C E59D3004 false LDR r3,[sp,#4]
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
Instruction 6 0x8026B560 EE1D3F30 false MRC p15,#0x0,r3,c13,c0,#1
Instruction 0 0x8026B564 E1A0100D false MOV r1,sp
Instruction 0 0x8026B568 E3C12D7F false BIC r2,r1,#0x1fc0
Instruction 0 0x8026B56C E3C2203F false BIC r2,r2,#0x3f
Instruction 0 0x8026B570 E59D1004 false LDR r1,[sp,#4]
Instruction 0 0x8026B574 E59F0010 false LDR r0,[pc,#16] ; [0x8026B58C] = 0x80550368
Instruction 0 0x8026B578 E592200C false LDR r2,[r2,#0xc]
Instruction 0 0x8026B57C E59221D0 false LDR r2,[r2,#0x1d0]
Instruction 0 0x8026B580 EB07A4CF true BL {pc}+0x1e9344 ; 0x804548c4
Info Tracing enabled
Instruction 13570831 0x8026B584 E28DD00C false ADD sp,sp,#0xc
Instruction 0 0x8026B588 E8BD8000 true LDM sp!,{pc}
Timestamp Timestamp: 17107041535
Using perf framework
~~~~~~~~~~~~~~~~~~~~
Coresight tracers are represented using the Perf framework's Performance
Monitoring Unit (PMU) abstraction. As such the perf framework takes charge of
controlling when tracing gets enabled based on when the process of interest is
scheduled. When configured in a system, Coresight PMUs will be listed when
queried by the perf command line tool:
linaro@linaro-nano:~$ ./perf list pmu
List of pre-defined events (to be used in -e):
cs_etm// [Kernel PMU event]
Regardless of the number of tracers available in a system (usually equal to the
amount of processor cores), the "cs_etm" PMU will be listed only once.
A Coresight PMU works the same way as any other PMU, i.e the name of the PMU is
provided along with configuration options within forward slashes '/' (see
`Config option formats`_).
Advanced Perf framework usage
-----------------------------
Sink selection
~~~~~~~~~~~~~~
An appropriate sink will be selected automatically for use with Perf, but since
there will typically be more than one sink, the name of the sink to use may be
specified as a special config option prefixed with '@'.
The available sinks are listed in sysFS under
($SYSFS)/bus/event_source/devices/cs_etm/sinks/::
root@localhost:/sys/bus/event_source/devices/cs_etm/sinks# ls
tmc_etf0 tmc_etr0 tpiu0
root@linaro-nano:~# perf record -e cs_etm/@tmc_etr0/u --per-thread program
More information on the above and other example on how to use Coresight with
the perf tools can be found in the "HOWTO.md" file of the openCSD gitHub
repository [#third]_.
AutoFDO analysis using the perf tools
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
perf can be used to record and analyze trace of programs.
Execution can be recorded using 'perf record' with the cs_etm event,
specifying the name of the sink to record to, e.g::
perf record -e cs_etm//u --per-thread
The 'perf report' and 'perf script' commands can be used to analyze execution,
synthesizing instruction and branch events from the instruction trace.
'perf inject' can be used to replace the trace data with the synthesized events.
The --itrace option controls the type and frequency of synthesized events
(see perf documentation).
Note that only 64-bit programs are currently supported - further work is
required to support instruction decode of 32-bit Arm programs.
Tracing PID
~~~~~~~~~~~
The kernel can be built to write the PID value into the PE ContextID registers.
For a kernel running at EL1, the PID is stored in CONTEXTIDR_EL1. A PE may
implement Arm Virtualization Host Extensions (VHE), which the kernel can
run at EL2 as a virtualisation host; in this case, the PID value is stored in
CONTEXTIDR_EL2.
perf provides PMU formats that program the ETM to insert these values into the
trace data; the PMU formats are defined as below:
"contextid1": Available on both EL1 kernel and EL2 kernel. When the
kernel is running at EL1, "contextid1" enables the PID
tracing; when the kernel is running at EL2, this enables
tracing the PID of guest applications.
"contextid2": Only usable when the kernel is running at EL2. When
selected, enables PID tracing on EL2 kernel.
"contextid": Will be an alias for the option that enables PID
tracing. I.e,
contextid == contextid1, on EL1 kernel.
contextid == contextid2, on EL2 kernel.
perf will always enable PID tracing at the relevant EL, this is accomplished by
automatically enable the "contextid" config - but for EL2 it is possible to make
specific adjustments using configs "contextid1" and "contextid2", E.g. if a user
wants to trace PIDs for both host and guest, the two configs "contextid1" and
"contextid2" can be set at the same time:
perf record -e cs_etm/contextid1,contextid2/u -- vm
Generating coverage files for Feedback Directed Optimization: AutoFDO
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'perf inject' accepts the --itrace option in which case tracing data is
removed and replaced with the synthesized events. e.g.
::
perf inject --itrace --strip -i perf.data -o perf.data.new
Below is an example of using ARM ETM for autoFDO. It requires autofdo
(https://github.com/google/autofdo) and gcc version 5. The bubble
sort example is from the AutoFDO tutorial (https://gcc.gnu.org/wiki/AutoFDO/Tutorial).
::
$ gcc-5 -O3 sort.c -o sort
$ taskset -c 2 ./sort
Bubble sorting array of 30000 elements
5910 ms
$ perf record -e cs_etm//u --per-thread taskset -c 2 ./sort
Bubble sorting array of 30000 elements
12543 ms
[ perf record: Woken up 35 times to write data ]
[ perf record: Captured and wrote 69.640 MB perf.data ]
$ perf inject -i perf.data -o inj.data --itrace=il64 --strip
$ create_gcov --binary=./sort --profile=inj.data --gcov=sort.gcov -gcov_version=1
$ gcc-5 -O3 -fauto-profile=sort.gcov sort.c -o sort_autofdo
$ taskset -c 2 ./sort_autofdo
Bubble sorting array of 30000 elements
5806 ms
Config option formats
~~~~~~~~~~~~~~~~~~~~~
The following strings can be provided between // on the perf command line to enable various options.
They are also listed in the folder /sys/bus/event_source/devices/cs_etm/format/
.. list-table::
:header-rows: 1
* - Option
- Description
* - branch_broadcast
- Session local version of the system wide setting:
:ref:`ETM_MODE_BB <coresight-branch-broadcast>`
* - contextid
- See `Tracing PID`_
* - contextid1
- See `Tracing PID`_
* - contextid2
- See `Tracing PID`_
* - configid
- Selection for a custom configuration. This is an implementation detail and not used directly,
see :ref:`trace/coresight/coresight-config:Using Configurations in perf`
* - preset
- Override for parameters in a custom configuration, see
:ref:`trace/coresight/coresight-config:Using Configurations in perf`
* - sinkid
- Hashed version of the string to select a sink, automatically set when using the @ notation.
This is an internal implementation detail and is not used directly, see `Using perf
framework`_.
* - cycacc
- Session local version of the system wide setting: :ref:`ETMv4_MODE_CYCACC
<coresight-cycle-accurate>`
* - retstack
- Session local version of the system wide setting: :ref:`ETM_MODE_RETURNSTACK
<coresight-return-stack>`
* - timestamp
- Controls generation and interval of timestamps.
0 = off, 1 = minimum interval .. 15 = maximum interval.
Values 1 - 14 use a counter that decrements every cycle to generate a
timestamp on underflow. The reload value for the counter is 2 ^ (interval
- 1). If the value is 1 then the reload value is 1, if the value is 11
then the reload value is 1024 etc.
Setting the maximum interval (15) will disable the counter generated
timestamps, freeing the counter resource, leaving only ones emitted when
a SYNC packet is generated. The sync interval is controlled with
TRCSYNCPR.PERIOD which is every 4096 bytes of trace by default.
* - cc_threshold
- Cycle count threshold value. If nothing is provided here or the provided value is 0, then the
default value i.e 0x100 will be used. If provided value is less than minimum cycles threshold
value, as indicated via TRCIDR3.CCITMIN, then the minimum value will be used instead.
How to use the STM module
-------------------------
Using the System Trace Macrocell module is the same as the tracers - the only
difference is that clients are driving the trace capture rather
than the program flow through the code.
As with any other CoreSight component, specifics about the STM tracer can be
found in sysfs with more information on each entry being found in [#first]_::
root@genericarmv8:~# ls /sys/bus/coresight/devices/stm0
enable_source hwevent_select port_enable subsystem uevent
hwevent_enable mgmt port_select traceid
root@genericarmv8:~#
Like any other source a sink needs to be identified and the STM enabled before
being used::
root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/stm0/enable_source
From there user space applications can request and use channels using the devfs
interface provided for that purpose by the generic STM API::
root@genericarmv8:~# ls -l /dev/stm0
crw------- 1 root root 10, 61 Jan 3 18:11 /dev/stm0
root@genericarmv8:~#
Details on how to use the generic STM API can be found here:
- Documentation/trace/stm.rst [#second]_.
The CTI & CTM Modules
---------------------
The CTI (Cross Trigger Interface) provides a set of trigger signals between
individual CTIs and components, and can propagate these between all CTIs via
channels on the CTM (Cross Trigger Matrix).
A separate documentation file is provided to explain the use of these devices.
(Documentation/trace/coresight/coresight-ect.rst) [#fourth]_.
CoreSight System Configuration
------------------------------
CoreSight components can be complex devices with many programming options.
Furthermore, components can be programmed to interact with each other across the
complete system.
A CoreSight System Configuration manager is provided to allow these complex programming
configurations to be selected and used easily from perf and sysfs.
See the separate document for further information.
(Documentation/trace/coresight/coresight-config.rst) [#fifth]_.
.. [#first] Documentation/ABI/testing/sysfs-bus-coresight-devices-stm
.. [#second] Documentation/trace/stm.rst
.. [#third] https://github.com/Linaro/perf-opencsd
.. [#fourth] Documentation/trace/coresight/coresight-ect.rst
.. [#fifth] Documentation/trace/coresight/coresight-config.rst
@@ -0,0 +1,9 @@
==============================
CoreSight - ARM Hardware Trace
==============================
.. toctree::
:maxdepth: 2
:glob:
*
@@ -0,0 +1,362 @@
===================================================
Using Coresight for Kernel panic and Watchdog reset
===================================================
Introduction
------------
This documentation is about using Linux coresight trace support to
debug kernel panic and watchdog reset scenarios.
Coresight trace during Kernel panic
-----------------------------------
From the coresight driver point of view, addressing the kernel panic
situation has four main requirements.
a. Support for allocation of trace buffer pages from reserved memory area.
Platform can advertise this using a new device tree property added to
relevant coresight nodes.
b. Support for stopping coresight blocks at the time of panic
c. Saving required metadata in the specified format
d. Support for reading trace data captured at the time of panic
Allocation of trace buffer pages from reserved RAM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A new optional device tree property "memory-region" is added to the
Coresight TMC device nodes, that would give the base address and size of trace
buffer.
Static allocation of trace buffers would ensure that both IOMMU enabled
and disabled cases are handled. Also, platforms that support persistent
RAM will allow users to read trace data in the subsequent boot without
booting the crashdump kernel.
Note:
For ETR sink devices, this reserved region will be used for both trace
capture and trace data retrieval.
For ETF sink devices, internal SRAM would be used for trace capture,
and they would be synced to reserved region for retrieval.
Disabling coresight blocks at the time of panic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to avoid the situation of losing relevant trace data after a
kernel panic, it would be desirable to stop the coresight blocks at the
time of panic.
This can be achieved by configuring the comparator, CTI and sink
devices as below::
Trigger on panic
Comparator --->External out --->CTI -->External In---->ETR/ETF stop
Saving metadata at the time of kernel panic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Coresight metadata involves all additional data that are required for a
successful trace decode in addition to the trace data. This involves
ETR/ETF/ETB register snapshot etc.
A new optional device property "memory-region" is added to
the ETR/ETF/ETB device nodes for this.
Reading trace data captured at the time of panic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Trace data captured at the time of panic, can be read from rebooted kernel
or from crashdump kernel using a special device file /dev/crash_tmc_xxx.
This device file is created only when there is a valid crashdata available.
General flow of trace capture and decode in case of kernel panic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Enable source and sink on all the cores using the sysfs interface.
ETR sinks should have trace buffers allocated from reserved memory,
by selecting "resrv" buffer mode from sysfs.
2. Run relevant tests.
3. On a kernel panic, all coresight blocks are disabled, necessary
metadata is synced by kernel panic handler.
System would eventually reboot or boot a crashdump kernel.
4. For platforms that supports crashdump kernel, raw trace data can be
dumped using the coresight sysfs interface from the crashdump kernel
itself. Persistent RAM is not a requirement in this case.
5. For platforms that supports persistent RAM, trace data can be dumped
using the coresight sysfs interface in the subsequent Linux boot.
Crashdump kernel is not a requirement in this case. Persistent RAM
ensures that trace data is intact across reboot.
Coresight trace during Watchdog reset
-------------------------------------
The main difference between addressing the watchdog reset and kernel panic
case are below,
a. Saving coresight metadata need to be taken care by the
SCP(system control processor) firmware in the specified format,
instead of kernel.
b. Reserved memory region given by firmware for trace buffer and metadata
has to be in persistent RAM.
Note: This is a requirement for watchdog reset case but optional
in kernel panic case.
Watchdog reset can be supported only on platforms that meet the above
two requirements.
Sample commands for testing a Kernel panic case with ETR sink
-------------------------------------------------------------
1. Boot Linux kernel with "crash_kexec_post_notifiers" added to the kernel
bootargs. This is mandatory if the user would like to read the tracedata
from the crashdump kernel.
2. Enable the preloaded ETM configuration::
#echo 1 > /sys/kernel/config/cs-syscfg/configurations/panicstop/enable
3. Configure CTI using sysfs interface::
#./cti_setup.sh
#cat cti_setup.sh
cd /sys/bus/coresight/devices/
ap_cti_config () {
#ETM trig out[0] trigger to Channel 0
echo 0 4 > channels/trigin_attach
}
etf_cti_config () {
#ETF Flush in trigger from Channel 0
echo 0 1 > channels/trigout_attach
echo 1 > channels/trig_filter_enable
}
etr_cti_config () {
#ETR Flush in from Channel 0
echo 0 1 > channels/trigout_attach
echo 1 > channels/trig_filter_enable
}
ctidevs=`find . -name "cti*"`
for i in $ctidevs
do
cd $i
connection=`find . -name "ete*"`
if [ ! -z "$connection" ]
then
echo "AP CTI config for $i"
ap_cti_config
fi
connection=`find . -name "tmc_etf*"`
if [ ! -z "$connection" ]
then
echo "ETF CTI config for $i"
etf_cti_config
fi
connection=`find . -name "tmc_etr*"`
if [ ! -z "$connection" ]
then
echo "ETR CTI config for $i"
etr_cti_config
fi
cd ..
done
Note: CTI connections are SOC specific and hence the above script is
added just for reference.
4. Choose reserved buffer mode for ETR buffer::
#echo "resrv" > /sys/bus/coresight/devices/tmc_etr0/buf_mode_preferred
5. Enable stop on flush trigger configuration::
#echo 1 > /sys/bus/coresight/devices/tmc_etr0/stop_on_flush
6. Start Coresight tracing on cores 1 and 2 using sysfs interface
7. Run some application on core 1::
#taskset -c 1 dd if=/dev/urandom of=/dev/null &
8. Invoke kernel panic on core 2::
#echo 1 > /proc/sys/kernel/panic
#taskset -c 2 echo c > /proc/sysrq-trigger
9. From rebooted kernel or crashdump kernel, read crashdata::
#dd if=/dev/crash_tmc_etr0 of=/trace/cstrace.bin
10. Run opencsd decoder tools/scripts to generate the instruction trace.
Sample instruction trace dump
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Core1 dump::
A etm4_enable_hw: ffff800008ae1dd4
CONTEXT EL2 etm4_enable_hw: ffff800008ae1dd4
I etm4_enable_hw: ffff800008ae1dd4:
d503201f nop
I etm4_enable_hw: ffff800008ae1dd8:
d503201f nop
I etm4_enable_hw: ffff800008ae1ddc:
d503201f nop
I etm4_enable_hw: ffff800008ae1de0:
d503201f nop
I etm4_enable_hw: ffff800008ae1de4:
d503201f nop
I etm4_enable_hw: ffff800008ae1de8:
d503233f paciasp
I etm4_enable_hw: ffff800008ae1dec:
a9be7bfd stp x29, x30, [sp, #-32]!
I etm4_enable_hw: ffff800008ae1df0:
910003fd mov x29, sp
I etm4_enable_hw: ffff800008ae1df4:
a90153f3 stp x19, x20, [sp, #16]
I etm4_enable_hw: ffff800008ae1df8:
2a0003f4 mov w20, w0
I etm4_enable_hw: ffff800008ae1dfc:
900085b3 adrp x19, ffff800009b95000 <reserved_mem+0xc48>
I etm4_enable_hw: ffff800008ae1e00:
910f4273 add x19, x19, #0x3d0
I etm4_enable_hw: ffff800008ae1e04:
f8747a60 ldr x0, [x19, x20, lsl #3]
E etm4_enable_hw: ffff800008ae1e08:
b4000140 cbz x0, ffff800008ae1e30 <etm4_starting_cpu+0x50>
I 149.039572921 etm4_enable_hw: ffff800008ae1e30:
a94153f3 ldp x19, x20, [sp, #16]
I 149.039572921 etm4_enable_hw: ffff800008ae1e34:
52800000 mov w0, #0x0 // #0
I 149.039572921 etm4_enable_hw: ffff800008ae1e38:
a8c27bfd ldp x29, x30, [sp], #32
..snip
149.052324811 chacha_block_generic: ffff800008642d80:
9100a3e0 add x0,
I 149.052324811 chacha_block_generic: ffff800008642d84:
b86178a2 ldr w2, [x5, x1, lsl #2]
I 149.052324811 chacha_block_generic: ffff800008642d88:
8b010803 add x3, x0, x1, lsl #2
I 149.052324811 chacha_block_generic: ffff800008642d8c:
b85fc063 ldur w3, [x3, #-4]
I 149.052324811 chacha_block_generic: ffff800008642d90:
0b030042 add w2, w2, w3
I 149.052324811 chacha_block_generic: ffff800008642d94:
b8217882 str w2, [x4, x1, lsl #2]
I 149.052324811 chacha_block_generic: ffff800008642d98:
91000421 add x1, x1, #0x1
I 149.052324811 chacha_block_generic: ffff800008642d9c:
f100443f cmp x1, #0x11
Core 2 dump::
A etm4_enable_hw: ffff800008ae1dd4
CONTEXT EL2 etm4_enable_hw: ffff800008ae1dd4
I etm4_enable_hw: ffff800008ae1dd4:
d503201f nop
I etm4_enable_hw: ffff800008ae1dd8:
d503201f nop
I etm4_enable_hw: ffff800008ae1ddc:
d503201f nop
I etm4_enable_hw: ffff800008ae1de0:
d503201f nop
I etm4_enable_hw: ffff800008ae1de4:
d503201f nop
I etm4_enable_hw: ffff800008ae1de8:
d503233f paciasp
I etm4_enable_hw: ffff800008ae1dec:
a9be7bfd stp x29, x30, [sp, #-32]!
I etm4_enable_hw: ffff800008ae1df0:
910003fd mov x29, sp
I etm4_enable_hw: ffff800008ae1df4:
a90153f3 stp x19, x20, [sp, #16]
I etm4_enable_hw: ffff800008ae1df8:
2a0003f4 mov w20, w0
I etm4_enable_hw: ffff800008ae1dfc:
900085b3 adrp x19, ffff800009b95000 <reserved_mem+0xc48>
I etm4_enable_hw: ffff800008ae1e00:
910f4273 add x19, x19, #0x3d0
I etm4_enable_hw: ffff800008ae1e04:
f8747a60 ldr x0, [x19, x20, lsl #3]
E etm4_enable_hw: ffff800008ae1e08:
b4000140 cbz x0, ffff800008ae1e30 <etm4_starting_cpu+0x50>
I 149.046243445 etm4_enable_hw: ffff800008ae1e30:
a94153f3 ldp x19, x20, [sp, #16]
I 149.046243445 etm4_enable_hw: ffff800008ae1e34:
52800000 mov w0, #0x0 // #0
I 149.046243445 etm4_enable_hw: ffff800008ae1e38:
a8c27bfd ldp x29, x30, [sp], #32
I 149.046243445 etm4_enable_hw: ffff800008ae1e3c:
d50323bf autiasp
E 149.046243445 etm4_enable_hw: ffff800008ae1e40:
d65f03c0 ret
A ete_sysreg_write: ffff800008adfa18
..snip
I 149.05422547 panic: ffff800008096300:
a90363f7 stp x23, x24, [sp, #48]
I 149.05422547 panic: ffff800008096304:
6b00003f cmp w1, w0
I 149.05422547 panic: ffff800008096308:
3a411804 ccmn w0, #0x1, #0x4, ne // ne = any
N 149.05422547 panic: ffff80000809630c:
540001e0 b.eq ffff800008096348 <panic+0xe0> // b.none
I 149.05422547 panic: ffff800008096310:
f90023f9 str x25, [sp, #64]
E 149.05422547 panic: ffff800008096314:
97fe44ef bl ffff8000080276d0 <panic_smp_self_stop>
A panic: ffff80000809634c
I 149.05422547 panic: ffff80000809634c:
910102d5 add x21, x22, #0x40
I 149.05422547 panic: ffff800008096350:
52800020 mov w0, #0x1 // #1
E 149.05422547 panic: ffff800008096354:
94166b8b bl ffff800008631180 <bust_spinlocks>
N 149.054225518 bust_spinlocks: ffff800008631180:
340000c0 cbz w0, ffff800008631198 <bust_spinlocks+0x18>
I 149.054225518 bust_spinlocks: ffff800008631184:
f000a321 adrp x1, ffff800009a98000 <pbufs.0+0xbb8>
I 149.054225518 bust_spinlocks: ffff800008631188:
b9405c20 ldr w0, [x1, #92]
I 149.054225518 bust_spinlocks: ffff80000863118c:
11000400 add w0, w0, #0x1
I 149.054225518 bust_spinlocks: ffff800008631190:
b9005c20 str w0, [x1, #92]
E 149.054225518 bust_spinlocks: ffff800008631194:
d65f03c0 ret
A panic: ffff800008096358
Perf based testing
------------------
Starting perf session
~~~~~~~~~~~~~~~~~~~~~
ETF::
perf record -e cs_etm/panicstop,@tmc_etf1/ -C 1
perf record -e cs_etm/panicstop,@tmc_etf2/ -C 2
ETR::
perf record -e cs_etm/panicstop,@tmc_etr0/ -C 1,2
Reading trace data after panic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Same sysfs based method explained above can be used to retrieve and
decode the trace data after the reboot on kernel panic.
@@ -0,0 +1,83 @@
.. SPDX-License-Identifier: GPL-2.0
======================================
UltraSoc - HW Assisted Tracing on SoC
======================================
:Author: Qi Liu <liuqi115@huawei.com>
:Date: January 2023
Introduction
------------
UltraSoc SMB is a per SCCL (Super CPU Cluster) hardware. It provides a
way to buffer and store CPU trace messages in a region of shared system
memory. The device acts as a coresight sink device and the
corresponding trace generators (ETM) are attached as source devices.
Sysfs files and directories
---------------------------
The SMB devices appear on the existing coresight bus alongside other
devices::
$# ls /sys/bus/coresight/devices/
ultra_smb0 ultra_smb1 ultra_smb2 ultra_smb3
The ``ultra_smb<N>`` names SMB device associated with SCCL.::
$# ls /sys/bus/coresight/devices/ultra_smb0
enable_sink mgmt
$# ls /sys/bus/coresight/devices/ultra_smb0/mgmt
buf_size buf_status read_pos write_pos
Key file items are:
* ``read_pos``: Shows the value on the read pointer register.
* ``write_pos``: Shows the value on the write pointer register.
* ``buf_status``: Shows the value on the status register.
BIT(0) is zero value which means the buffer is empty.
* ``buf_size``: Shows the buffer size of each device.
Firmware Bindings
-----------------
The device is only supported with ACPI. Its binding describes device
identifier, resource information and graph structure.
The device is identified as ACPI HID "HISI03A1". Device resources are allocated
using the _CRS method. Each device must present two base address; the first one
is the configuration base address of the device, the second one is the 32-bit
base address of shared system memory.
Example::
Device(USMB) { \
Name(_HID, "HISI03A1") \
Name(_CRS, ResourceTemplate() { \
QWordMemory (ResourceConsumer, , MinFixed, MaxFixed, NonCacheable, \
ReadWrite, 0x0, 0x95100000, 0x951FFFFF, 0x0, 0x100000) \
QWordMemory (ResourceConsumer, , MinFixed, MaxFixed, Cacheable, \
ReadWrite, 0x0, 0x50000000, 0x53FFFFFF, 0x0, 0x4000000) \
}) \
Name(_DSD, Package() { \
ToUUID("ab02a46b-74c7-45a2-bd68-f7d344ef2153"), \
/* Use CoreSight Graph ACPI bindings to describe connections topology */
Package() { \
0, \
1, \
Package() { \
1, \
ToUUID("3ecbc8b6-1d0e-4fb3-8107-e627f805c6cd"), \
8, \
Package() {0x8, 0, \_SB.S00.SL11.CL28.F008, 0}, \
Package() {0x9, 0, \_SB.S00.SL11.CL29.F009, 0}, \
Package() {0xa, 0, \_SB.S00.SL11.CL2A.F010, 0}, \
Package() {0xb, 0, \_SB.S00.SL11.CL2B.F011, 0}, \
Package() {0xc, 0, \_SB.S00.SL11.CL2C.F012, 0}, \
Package() {0xd, 0, \_SB.S00.SL11.CL2D.F013, 0}, \
Package() {0xe, 0, \_SB.S00.SL11.CL2E.F014, 0}, \
Package() {0xf, 0, \_SB.S00.SL11.CL2F.F015, 0}, \
} \
} \
}) \
}
@@ -0,0 +1,180 @@
==============================
Using the tracer for debugging
==============================
Copyright 2024 Google LLC.
:Author: Steven Rostedt <rostedt@goodmis.org>
:License: The GNU Free Documentation License, Version 1.2
(dual licensed under the GPL v2)
- Written for: 6.12
Introduction
------------
The tracing infrastructure can be very useful for debugging the Linux
kernel. This document is a place to add various methods of using the tracer
for debugging.
First, make sure that the tracefs file system is mounted::
$ sudo mount -t tracefs tracefs /sys/kernel/tracing
Using trace_printk()
--------------------
trace_printk() is a very lightweight utility that can be used in any context
inside the kernel, with the exception of "noinstr" sections. It can be used
in normal, softirq, interrupt and even NMI context. The trace data is
written to the tracing ring buffer in a lockless way. To make it even
lighter weight, when possible, it will only record the pointer to the format
string, and save the raw arguments into the buffer. The format and the
arguments will be post processed when the ring buffer is read. This way the
trace_printk() format conversions are not done during the hot path, where
the trace is being recorded.
trace_printk() is meant only for debugging, and should never be added into
a subsystem of the kernel. If you need debugging traces, add trace events
instead. If a trace_printk() is found in the kernel, the following will
appear in the dmesg::
**********************************************************
** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **
** **
** trace_printk() being used. Allocating extra memory. **
** **
** This means that this is a DEBUG kernel and it is **
** unsafe for production use. **
** **
** If you see this message and you are not debugging **
** the kernel, report this immediately to your vendor! **
** **
** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **
**********************************************************
Debugging kernel crashes
------------------------
There is various methods of acquiring the state of the system when a kernel
crash occurs. This could be from the oops message in printk, or one could
use kexec/kdump. But these just show what happened at the time of the crash.
It can be very useful in knowing what happened up to the point of the crash.
The tracing ring buffer, by default, is a circular buffer that will
overwrite older events with newer ones. When a crash happens, the content of
the ring buffer will be all the events that lead up to the crash.
There are several kernel command line parameters that can be used to help in
this. The first is "ftrace_dump_on_oops". This will dump the tracing ring
buffer when a oops occurs to the console. This can be useful if the console
is being logged somewhere. If a serial console is used, it may be prudent to
make sure the ring buffer is relatively small, otherwise the dumping of the
ring buffer may take several minutes to hours to finish. Here's an example
of the kernel command line::
ftrace_dump_on_oops trace_buf_size=50K
Note, the tracing buffer is made up of per CPU buffers where each of these
buffers is broken up into sub-buffers that are by default PAGE_SIZE. The
above trace_buf_size option above sets each of the per CPU buffers to 50K,
so, on a machine with 8 CPUs, that's actually 400K total.
Persistent buffers across boots
-------------------------------
If the system memory allows it, the tracing ring buffer can be specified at
a specific location in memory. If the location is the same across boots and
the memory is not modified, the tracing buffer can be retrieved from the
following boot. There's two ways to reserve memory for the use of the ring
buffer.
The more reliable way (on x86) is to reserve memory with the "memmap" kernel
command line option and then use that memory for the trace_instance. This
requires a bit of knowledge of the physical memory layout of the system. The
advantage of using this method, is that the memory for the ring buffer will
always be the same::
memmap==12M$0x284500000 trace_instance=boot_map@0x284500000:12M
The memmap above reserves 12 megabytes of memory at the physical memory
location 0x284500000. Then the trace_instance option will create a trace
instance "boot_map" at that same location with the same amount of memory
reserved. As the ring buffer is broke up into per CPU buffers, the 12
megabytes will be broken up evenly between those CPUs. If you have 8 CPUs,
each per CPU ring buffer will be 1.5 megabytes in size. Note, that also
includes meta data, so the amount of memory actually used by the ring buffer
will be slightly smaller.
Another more generic but less robust way to allocate a ring buffer mapping
at boot is with the "reserve_mem" option::
reserve_mem=12M:4096:trace trace_instance=boot_map@trace
The reserve_mem option above will find 12 megabytes that are available at
boot up, and align it by 4096 bytes. It will label this memory as "trace"
that can be used by later command line options.
The trace_instance option creates a "boot_map" instance and will use the
memory reserved by reserve_mem that was labeled as "trace". This method is
more generic but may not be as reliable. Due to KASLR, the memory reserved
by reserve_mem may not be located at the same location. If this happens,
then the ring buffer will not be from the previous boot and will be reset.
Sometimes, by using a larger alignment, it can keep KASLR from moving things
around in such a way that it will move the location of the reserve_mem. By
using a larger alignment, you may find better that the buffer is more
consistent to where it is placed::
reserve_mem=12M:0x2000000:trace trace_instance=boot_map@trace
On boot up, the memory reserved for the ring buffer is validated. It will go
through a series of tests to make sure that the ring buffer contains valid
data. If it is, it will then set it up to be available to read from the
instance. If it fails any of the tests, it will clear the entire ring buffer
and initialize it as new.
The layout of this mapped memory may not be consistent from kernel to
kernel, so only the same kernel is guaranteed to work if the mapping is
preserved. Switching to a different kernel version may find a different
layout and mark the buffer as invalid.
NB: Both the mapped address and size must be page aligned for the architecture.
Using trace_printk() in the boot instance
-----------------------------------------
By default, the content of trace_printk() goes into the top level tracing
instance. But this instance is never preserved across boots. To have the
trace_printk() content, and some other internal tracing go to the preserved
buffer (like dump stacks), either set the instance to be the trace_printk()
destination from the kernel command line, or set it after boot up via the
trace_printk_dest option.
After boot up::
echo 1 > /sys/kernel/tracing/instances/boot_map/options/trace_printk_dest
From the kernel command line::
reserve_mem=12M:4096:trace trace_instance=boot_map^traceprintk^traceoff@trace
If setting it from the kernel command line, it is recommended to also
disable tracing with the "traceoff" flag, and enable tracing after boot up.
Otherwise the trace from the most recent boot will be mixed with the trace
from the previous boot, and may make it confusing to read.
Using a backup instance for keeping previous boot data
------------------------------------------------------
It is also possible to record trace data at system boot time by specifying
events with the persistent ring buffer, but in this case the data before the
reboot will be lost before it can be read. This problem can be solved by a
backup instance. From the kernel command line::
reserve_mem=12M:4096:trace trace_instance=boot_map@trace,sched,irq trace_instance=backup=boot_map
On boot up, the previous data in the "boot_map" is copied to the "backup"
instance, and the "sched:*" and "irq:*" events for the current boot are traced
in the "boot_map". Thus the user can read the previous boot data from the "backup"
instance without stopping the trace.
Note that this "backup" instance is readonly, and will be removed automatically
if you clear the trace data or read out all trace data from the "trace_pipe"
or the "trace_pipe_raw" files.
@@ -0,0 +1,273 @@
.. SPDX-License-Identifier: GPL-2.0
==================================
Eprobe - Event-based Probe Tracing
==================================
:Author: Steven Rostedt <rostedt@goodmis.org>
- Written for v6.17
Overview
========
Eprobes are dynamic events that are placed on existing events to either
dereference a field that is a pointer, or simply to limit what fields are
recorded in the trace event.
Eprobes depend on kprobe events so to enable this feature, build your kernel
with CONFIG_EPROBE_EVENTS=y.
Eprobes are created via the /sys/kernel/tracing/dynamic_events file.
Synopsis of eprobe_events
-------------------------
::
e[:[EGRP/][EEVENT]] GRP.EVENT [FETCHARGS] : Set a probe
-:[EGRP/][EEVENT] : Clear a probe
EGRP : Group name of the new event. If omitted, use "eprobes" for it.
EEVENT : Event name. If omitted, the event name is generated and will
be the same event name as the event it attached to.
GRP : Group name of the event to attach to.
EVENT : Event name of the event to attach to.
FETCHARGS : Arguments. Each probe can have up to 128 args.
$FIELD : Fetch the value of the event field called FIELD.
@ADDR : Fetch memory at ADDR (ADDR should be in kernel)
@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
$comm : Fetch current task comm.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
(x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
"string", "ustring", "symbol", "symstr" and "bitfield" are
supported.
(STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
a pointer to STRUCT and then derference the pointer defined by
->MEMBER. Note that when this is used, the FIELD name does not
need to be prefixed with a '$'.
Types
-----
The FETCHARGS above is very similar to the kprobe events as described in
Documentation/trace/kprobetrace.rst.
The difference between eprobes and kprobes FETCHARGS is that eprobes has a
$FIELD command that returns the content of the event field of the event
that is attached. Eprobes do not have access to registers, stacks and function
arguments that kprobes has.
If a field argument is a pointer, it may be dereferenced just like a memory
address using the FETCHARGS syntax.
Attaching to dynamic events
---------------------------
Eprobes may attach to dynamic events as well as to normal events. It may
attach to a kprobe event, a synthetic event or a fprobe event. This is useful
if the type of a field needs to be changed. See Example 2 below.
Usage examples
==============
Example 1
---------
The basic usage of eprobes is to limit the data that is being recorded into
the tracing buffer. For example, a common event to trace is the sched_switch
trace event. That has a format of::
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:char prev_comm[16]; offset:8; size:16; signed:0;
field:pid_t prev_pid; offset:24; size:4; signed:1;
field:int prev_prio; offset:28; size:4; signed:1;
field:long prev_state; offset:32; size:8; signed:1;
field:char next_comm[16]; offset:40; size:16; signed:0;
field:pid_t next_pid; offset:56; size:4; signed:1;
field:int next_prio; offset:60; size:4; signed:1;
The first four fields are common to all events and can not be limited. But the
rest of the event has 60 bytes of information. It records the names of the
previous and next tasks being scheduled out and in, as well as their pids and
priorities. It also records the state of the previous task. If only the pids
of the tasks are of interest, why waste the ring buffer with all the other
fields?
An eprobe can limit what gets recorded. Note, it does not help in performance,
as all the fields are recorded in a temporary buffer to process the eprobe.
::
# echo 'e:sched/switch sched.sched_switch prev=$prev_pid:u32 next=$next_pid:u32' >> /sys/kernel/tracing/dynamic_events
# echo 1 > /sys/kernel/tracing/events/sched/switch/enable
# cat /sys/kernel/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 2721/2721 #P:8
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
sshd-session-1082 [004] d..4. 5041.239906: switch: (sched.sched_switch) prev=1082 next=0
bash-1085 [001] d..4. 5041.240198: switch: (sched.sched_switch) prev=1085 next=141
kworker/u34:5-141 [001] d..4. 5041.240259: switch: (sched.sched_switch) prev=141 next=1085
<idle>-0 [004] d..4. 5041.240354: switch: (sched.sched_switch) prev=0 next=1082
bash-1085 [001] d..4. 5041.240385: switch: (sched.sched_switch) prev=1085 next=141
kworker/u34:5-141 [001] d..4. 5041.240410: switch: (sched.sched_switch) prev=141 next=1085
bash-1085 [001] d..4. 5041.240478: switch: (sched.sched_switch) prev=1085 next=0
sshd-session-1082 [004] d..4. 5041.240526: switch: (sched.sched_switch) prev=1082 next=0
<idle>-0 [001] d..4. 5041.247524: switch: (sched.sched_switch) prev=0 next=90
<idle>-0 [002] d..4. 5041.247545: switch: (sched.sched_switch) prev=0 next=16
kworker/1:1-90 [001] d..4. 5041.247580: switch: (sched.sched_switch) prev=90 next=0
rcu_sched-16 [002] d..4. 5041.247591: switch: (sched.sched_switch) prev=16 next=0
<idle>-0 [002] d..4. 5041.257536: switch: (sched.sched_switch) prev=0 next=16
rcu_sched-16 [002] d..4. 5041.257573: switch: (sched.sched_switch) prev=16 next=0
Note, without adding the "u32" after the prev_pid and next_pid, the values
would default showing in hexadecimal.
Example 2
---------
If a specific system call is to be recorded but the syscalls events are not
enabled, the raw_syscalls can still be used (syscalls are system call
events are not normal events, but are created from the raw_syscalls events
within the kernel). In order to trace the openat system call, one can create
an event probe on top of the raw_syscalls event:
::
# cd /sys/kernel/tracing
# cat events/raw_syscalls/sys_enter/format
name: sys_enter
ID: 395
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:long id; offset:8; size:8; signed:1;
field:unsigned long args[6]; offset:16; size:48; signed:0;
print fmt: "NR %ld (%lx, %lx, %lx, %lx, %lx, %lx)", REC->id, REC->args[0], REC->args[1], REC->args[2], REC->args[3], REC->args[4], REC->args[5]
From the source code, the sys_openat() has:
::
int sys_openat(int dirfd, const char *path, int flags, mode_t mode)
{
return my_syscall4(__NR_openat, dirfd, path, flags, mode);
}
The path is the second parameter, and that is what is wanted.
::
# echo 'e:openat raw_syscalls.sys_enter nr=$id filename=+8($args):ustring' >> dynamic_events
This is being run on x86_64 where the word size is 8 bytes and the openat
system call __NR_openat is set at 257.
::
# echo 'nr == 257' > events/eprobes/openat/filter
Now enable the event and look at the trace.
::
# echo 1 > events/eprobes/openat/enable
# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 4/4 #P:8
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
cat-1298 [003] ...2. 2060.875970: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
cat-1298 [003] ...2. 2060.876197: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
cat-1298 [003] ...2. 2060.879126: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
cat-1298 [003] ...2. 2060.879639: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
The filename shows "(fault)". This is likely because the filename has not been
pulled into memory yet and currently trace events cannot fault in memory that
is not present. When an eprobe tries to read memory that has not been faulted
in yet, it will show the "(fault)" text.
To get around this, as the kernel will likely pull in this filename and make
it present, attaching it to a synthetic event that can pass the address of the
filename from the entry of the event to the end of the event, this can be used
to show the filename when the system call returns.
Remove the old eprobe::
# echo 1 > events/eprobes/openat/enable
# echo '-:openat' >> dynamic_events
This time make an eprobe where the address of the filename is saved::
# echo 'e:openat_start raw_syscalls.sys_enter nr=$id filename=+8($args):x64' >> dynamic_events
Create a synthetic event that passes the address of the filename to the
end of the event::
# echo 's:filename u64 file' >> dynamic_events
# echo 'hist:keys=common_pid:f=filename if nr == 257' > events/eprobes/openat_start/trigger
# echo 'hist:keys=common_pid:file=$f:onmatch(eprobes.openat_start).trace(filename,$file) if id == 257' > events/raw_syscalls/sys_exit/trigger
Now that the address of the filename has been passed to the end of the
system call, create another eprobe to attach to the exit event to show the
string::
# echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events
# echo 1 > events/eprobes/openat/enable
# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 4/4 #P:8
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
cat-1331 [001] ...5. 2944.787977: openat: (synthetic.filename) filename="/etc/ld.so.cache"
cat-1331 [001] ...5. 2944.788480: openat: (synthetic.filename) filename="/lib/x86_64-linux-gnu/libc.so.6"
cat-1331 [001] ...5. 2944.793426: openat: (synthetic.filename) filename="/usr/lib/locale/locale-archive"
cat-1331 [001] ...5. 2944.831362: openat: (synthetic.filename) filename="trace"
Example 3
---------
If syscall trace events are available, the above would not need the first
eprobe, but it would still need the last one::
# echo 's:filename u64 file' >> dynamic_events
# echo 'hist:keys=common_pid:f=filename' > events/syscalls/sys_enter_openat/trigger
# echo 'hist:keys=common_pid:file=$f:onmatch(syscalls.sys_enter_openat).trace(filename,$file)' > events/syscalls/sys_exit_openat/trigger
# echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events
# echo 1 > events/eprobes/openat/enable
And this would produce the same result as Example 2.
@@ -0,0 +1,119 @@
============================
Subsystem Trace Points: kmem
============================
The kmem tracing system captures events related to object and page allocation
within the kernel. Broadly speaking there are five major subheadings.
- Slab allocation of small objects of unknown type (kmalloc)
- Slab allocation of small objects of known type
- Page allocation
- Per-CPU Allocator Activity
- External Fragmentation
This document describes what each of the tracepoints is and why they
might be useful.
1. Slab allocation of small objects of unknown type
===================================================
::
kmalloc call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s
kmalloc_node call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s node=%d
kfree call_site=%lx ptr=%p
Heavy activity for these events may indicate that a specific cache is
justified, particularly if kmalloc slab pages are getting significantly
internal fragmented as a result of the allocation pattern. By correlating
kmalloc with kfree, it may be possible to identify memory leaks and where
the allocation sites were.
2. Slab allocation of small objects of known type
=================================================
::
kmem_cache_alloc call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s
kmem_cache_alloc_node call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s node=%d
kmem_cache_free call_site=%lx ptr=%p
These events are similar in usage to the kmalloc-related events except that
it is likely easier to pin the event down to a specific cache. At the time
of writing, no information is available on what slab is being allocated from,
but the call_site can usually be used to extrapolate that information.
3. Page allocation
==================
::
mm_page_alloc page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s
mm_page_alloc_zone_locked page=%p pfn=%lu order=%u migratetype=%d cpu=%d percpu_refill=%d
mm_page_free page=%p pfn=%lu order=%d
mm_page_free_batched page=%p pfn=%lu order=%d cold=%d
These four events deal with page allocation and freeing. mm_page_alloc is
a simple indicator of page allocator activity. Pages may be allocated from
the per-CPU allocator (high performance) or the buddy allocator.
If pages are allocated directly from the buddy allocator, the
mm_page_alloc_zone_locked event is triggered. This event is important as high
amounts of activity imply high activity on the zone->lock. Taking this lock
impairs performance by disabling interrupts, dirtying cache lines between
CPUs and serialising many CPUs.
When a page is freed directly by the caller, the only mm_page_free event
is triggered. Significant amounts of activity here could indicate that the
callers should be batching their activities.
When pages are freed in batch, the also mm_page_free_batched is triggered.
Broadly speaking, pages are taken off the LRU lock in bulk and
freed in batch with a page list. Significant amounts of activity here could
indicate that the system is under memory pressure and can also indicate
contention on the lruvec->lru_lock.
4. Per-CPU Allocator Activity
=============================
::
mm_page_alloc_zone_locked page=%p pfn=%lu order=%u migratetype=%d cpu=%d percpu_refill=%d
mm_page_pcpu_drain page=%p pfn=%lu order=%d cpu=%d migratetype=%d
In front of the page allocator is a per-cpu page allocator. It exists only
for order-0 pages, reduces contention on the zone->lock and reduces the
amount of writing on struct page.
When a per-CPU list is empty or pages of the wrong type are allocated,
the zone->lock will be taken once and the per-CPU list refilled. The event
triggered is mm_page_alloc_zone_locked for each page allocated with the
event indicating whether it is for a percpu_refill or not.
When the per-CPU list is too full, a number of pages are freed, each one
which triggers a mm_page_pcpu_drain event.
The individual nature of the events is so that pages can be tracked
between allocation and freeing. A number of drain or refill pages that occur
consecutively imply the zone->lock being taken once. Large amounts of per-CPU
refills and drains could imply an imbalance between CPUs where too much work
is being concentrated in one place. It could also indicate that the per-CPU
lists should be a larger size. Finally, large amounts of refills on one CPU
and drains on another could be a factor in causing large amounts of cache
line bounces due to writes between CPUs and worth investigating if pages
can be allocated and freed on the same CPU through some algorithm change.
5. External Fragmentation
=========================
::
mm_page_alloc_extfrag page=%p pfn=%lu alloc_order=%d fallback_order=%d pageblock_order=%d alloc_migratetype=%d fallback_migratetype=%d fragmenting=%d change_ownership=%d
External fragmentation affects whether a high-order allocation will be
successful or not. For some types of hardware, this is important although
it is avoided where possible. If the system is using huge pages and needs
to be able to resize the pool over the lifetime of the system, this value
is important.
Large numbers of this event implies that memory is fragmenting and
high-order allocations will start failing at some time in the future. One
means of reducing the occurrence of this event is to increase the size of
min_free_kbytes in increments of 3*pageblock_size*nr_online_nodes where
pageblock_size is usually the size of the default hugepage size.
@@ -0,0 +1,40 @@
================
MSR Trace Events
================
The x86 kernel supports tracing most MSR (Model Specific Register) accesses.
To see the definition of the MSRs on Intel systems please see the SDM
at https://www.intel.com/sdm (Volume 3)
Available trace points:
/sys/kernel/tracing/events/msr/
Trace MSR reads:
read_msr
- msr: MSR number
- val: Value written
- failed: 1 if the access failed, otherwise 0
Trace MSR writes:
write_msr
- msr: MSR number
- val: Value written
- failed: 1 if the access failed, otherwise 0
Trace RDPMC in kernel:
rdpmc
The trace data can be post processed with the postprocess/decode_msr.py script::
cat /sys/kernel/tracing/trace | decode_msr.py /usr/src/linux/include/asm/msr-index.h
to add symbolic MSR names.
@@ -0,0 +1,45 @@
================
NMI Trace Events
================
These events normally show up here:
/sys/kernel/tracing/events/nmi
nmi_handler
-----------
You might want to use this tracepoint if you suspect that your
NMI handlers are hogging large amounts of CPU time. The kernel
will warn if it sees long-running handlers::
INFO: NMI handler took too long to run: 9.207 msecs
and this tracepoint will allow you to drill down and get some
more details.
Let's say you suspect that perf_event_nmi_handler() is causing
you some problems and you only want to trace that handler
specifically. You need to find its address::
$ grep perf_event_nmi_handler /proc/kallsyms
ffffffff81625600 t perf_event_nmi_handler
Let's also say you are only interested in when that function is
really hogging a lot of CPU time, like a millisecond at a time.
Note that the kernel's output is in milliseconds, but the input
to the filter is in nanoseconds! You can filter on 'delta_ns'::
cd /sys/kernel/tracing/events/nmi/nmi_handler
echo 'handler==0xffffffff81625600 && delta_ns>1000000' > filter
echo 1 > enable
Your output would then look like::
$ cat /sys/kernel/tracing/trace_pipe
<idle>-0 [000] d.h3 505.397558: nmi_handler: perf_event_nmi_handler() delta_ns: 3236765 handled: 1
<idle>-0 [000] d.h3 505.805893: nmi_handler: perf_event_nmi_handler() delta_ns: 3174234 handled: 1
<idle>-0 [000] d.h3 506.158206: nmi_handler: perf_event_nmi_handler() delta_ns: 3084642 handled: 1
<idle>-0 [000] d.h3 506.334346: nmi_handler: perf_event_nmi_handler() delta_ns: 3080351 handled: 1
@@ -0,0 +1,42 @@
.. SPDX-License-Identifier: GPL-2.0
======================================
Subsystem Trace Points: PCI Controller
======================================
Overview
========
The PCI controller tracing system provides tracepoints to monitor controller
level information for debugging purpose. The events normally show up here:
/sys/kernel/tracing/events/pci_controller
Cf. include/trace/events/pci_controller.h for the events definitions.
Available Tracepoints
=====================
pcie_ltssm_state_transition
---------------------------
Monitors PCIe LTSSM state transition including state and rate information
::
pcie_ltssm_state_transition "dev: %s state: %s rate: %s\n"
**Parameters**:
* ``dev`` - PCIe controller instance
* ``state`` - PCIe LTSSM state
* ``rate`` - PCIe date rate
**Example Usage**:
.. code-block:: shell
# Enable the tracepoint
echo 1 > /sys/kernel/debug/tracing/events/pci_controller/pcie_ltssm_state_transition/enable
# Monitor events (the following output is generated when a device is linking)
cat /sys/kernel/debug/tracing/trace_pipe
kworker/0:0-9 [000] ..... 5.600221: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_EQ2 rate: 8.0 GT/s
@@ -0,0 +1,74 @@
.. SPDX-License-Identifier: GPL-2.0
===========================
Subsystem Trace Points: PCI
===========================
Overview
========
The PCI tracing system provides tracepoints to monitor critical hardware events
that can impact system performance and reliability. These events normally show
up here:
/sys/kernel/tracing/events/pci
Cf. include/trace/events/pci.h for the events definitions.
Available Tracepoints
=====================
pci_hp_event
------------
Monitors PCI hotplug events including card insertion/removal and link
state changes.
::
pci_hp_event "%s slot:%s, event:%s\n"
**Event Types**:
* ``LINK_UP`` - PCIe link established
* ``LINK_DOWN`` - PCIe link lost
* ``CARD_PRESENT`` - Card detected in slot
* ``CARD_NOT_PRESENT`` - Card removed from slot
**Example Usage**::
# Enable the tracepoint
echo 1 > /sys/kernel/debug/tracing/events/pci/pci_hp_event/enable
# Monitor events (the following output is generated when a device is hotplugged)
cat /sys/kernel/debug/tracing/trace_pipe
irq/51-pciehp-88 [001] ..... 1311.177459: pci_hp_event: 0000:00:02.0 slot:10, event:CARD_PRESENT
irq/51-pciehp-88 [001] ..... 1311.177566: pci_hp_event: 0000:00:02.0 slot:10, event:LINK_UP
pcie_link_event
---------------
Monitors PCIe link speed changes and provides detailed link status information.
::
pcie_link_event "%s type:%d, reason:%d, cur_bus_speed:%d, max_bus_speed:%d, width:%u, flit_mode:%u, status:%s\n"
**Parameters**:
* ``type`` - PCIe device type (4=Root Port, etc.)
* ``reason`` - Reason for link change:
- ``0`` - Link retrain
- ``1`` - Bus enumeration
- ``2`` - Bandwidth notification enable
- ``3`` - Bandwidth notification IRQ
- ``4`` - Hotplug event
**Example Usage**::
# Enable the tracepoint
echo 1 > /sys/kernel/debug/tracing/events/pci/pcie_link_event/enable
# Monitor events (the following output is generated when a device is hotplugged)
cat /sys/kernel/debug/tracing/trace_pipe
irq/51-pciehp-88 [001] ..... 381.545386: pcie_link_event: 0000:00:02.0 type:4, reason:4, cur_bus_speed:20, max_bus_speed:23, width:1, flit_mode:0, status:DLLLA
@@ -0,0 +1,104 @@
=============================
Subsystem Trace Points: power
=============================
The power tracing system captures events related to power transitions
within the kernel. Broadly speaking there are three major subheadings:
- Power state switch which reports events related to suspend (S-states),
cpuidle (C-states) and cpufreq (P-states)
- System clock related changes
- Power domains related changes and transitions
This document describes what each of the tracepoints is and why they
might be useful.
Cf. include/trace/events/power.h for the events definitions.
1. Power state switch events
============================
1.1 Trace API
-----------------
A 'cpu' event class gathers the CPU-related events: cpuidle and
cpufreq.
::
cpu_idle "state=%lu cpu_id=%lu"
cpu_frequency "state=%lu cpu_id=%lu"
cpu_frequency_limits "min=%lu max=%lu cpu_id=%lu"
A suspend event is used to indicate the system going in and out of the
suspend mode:
::
machine_suspend "state=%lu"
Note: the value of '-1' or '4294967295' for state means an exit from the current state,
i.e. trace_cpu_idle(4, smp_processor_id()) means that the system
enters the idle state 4, while trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id())
means that the system exits the previous idle state.
The event which has 'state=4294967295' in the trace is very important to the user
space tools which are using it to detect the end of the current state, and so to
correctly draw the states diagrams and to calculate accurate statistics etc.
2. Clocks events
================
The clock events are used for clock enable/disable and for
clock rate change.
::
clock_enable "%s state=%lu cpu_id=%lu"
clock_disable "%s state=%lu cpu_id=%lu"
clock_set_rate "%s state=%lu cpu_id=%lu"
The first parameter gives the clock name (e.g. "gpio1_iclk").
The second parameter is '1' for enable, '0' for disable, the target
clock rate for set_rate.
3. Power domains events
=======================
The power domain events are used for power domains transitions
::
power_domain_target "%s state=%lu cpu_id=%lu"
The first parameter gives the power domain name (e.g. "mpu_pwrdm").
The second parameter is the power domain target state.
4. PM QoS events
================
The PM QoS events are used for QoS add/update/remove request and for
target/flags update.
::
pm_qos_update_target "action=%s prev_value=%d curr_value=%d"
pm_qos_update_flags "action=%s prev_value=0x%x curr_value=0x%x"
The first parameter gives the QoS action name (e.g. "ADD_REQ").
The second parameter is the previous QoS value.
The third parameter is the current QoS value to update.
There are also events used for device PM QoS add/update/remove request.
::
dev_pm_qos_add_request "device=%s type=%s new_value=%d"
dev_pm_qos_update_request "device=%s type=%s new_value=%d"
dev_pm_qos_remove_request "device=%s type=%s new_value=%d"
The first parameter gives the device name which tries to add/update/remove
QoS requests.
The second parameter gives the request type (e.g. "DEV_PM_QOS_RESUME_LATENCY").
The third parameter is value to be added/updated/removed.
And, there are events used for CPU latency QoS add/update/remove request.
::
pm_qos_add_request "value=%d"
pm_qos_update_request "value=%d"
pm_qos_remove_request "value=%d"
The parameter is the value to be added/updated/removed.
File diff suppressed because it is too large Load Diff
+198
View File
@@ -0,0 +1,198 @@
.. SPDX-License-Identifier: GPL-2.0
==================================
Fprobe - Function entry/exit probe
==================================
.. Author: Masami Hiramatsu <mhiramat@kernel.org>
Introduction
============
Fprobe is a function entry/exit probe based on the function-graph tracing
feature in ftrace.
Instead of tracing all functions, if you want to attach callbacks on specific
function entry and exit, similar to the kprobes and kretprobes, you can
use fprobe. Compared with kprobes and kretprobes, fprobe gives faster
instrumentation for multiple functions with single handler. This document
describes how to use fprobe.
The usage of fprobe
===================
The fprobe is a wrapper of ftrace (+ kretprobe-like return callback) to
attach callbacks to multiple function entry and exit. User needs to set up
the `struct fprobe` and pass it to `register_fprobe()`.
Typically, `fprobe` data structure is initialized with the `entry_handler`
and/or `exit_handler` as below.
.. code-block:: c
struct fprobe fp = {
.entry_handler = my_entry_callback,
.exit_handler = my_exit_callback,
};
To enable the fprobe, call one of register_fprobe(), register_fprobe_ips(), and
register_fprobe_syms(). These functions register the fprobe with different types
of parameters.
The register_fprobe() enables a fprobe by function-name filters.
E.g. this enables @fp on "func*()" function except "func2()".::
register_fprobe(&fp, "func*", "func2");
The register_fprobe_ips() enables a fprobe by ftrace-location addresses.
E.g.
.. code-block:: c
unsigned long ips[] = { 0x.... };
register_fprobe_ips(&fp, ips, ARRAY_SIZE(ips));
And the register_fprobe_syms() enables a fprobe by symbol names.
E.g.
.. code-block:: c
char syms[] = {"func1", "func2", "func3"};
register_fprobe_syms(&fp, syms, ARRAY_SIZE(syms));
To disable (remove from functions) this fprobe, call::
unregister_fprobe(&fp);
You can temporally (soft) disable the fprobe by::
disable_fprobe(&fp);
and resume by::
enable_fprobe(&fp);
The above is defined by including the header::
#include <linux/fprobe.h>
Same as ftrace, the registered callbacks will start being called some time
after the register_fprobe() is called and before it returns. See
Documentation/trace/ftrace.rst.
Also, the unregister_fprobe() will guarantee that both enter and exit
handlers are no longer being called by functions after unregister_fprobe()
returns as same as unregister_ftrace_function().
The fprobe entry/exit handler
=============================
The prototype of the entry/exit callback function are as follows:
.. code-block:: c
int entry_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data);
void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data);
Note that the @entry_ip is saved at function entry and passed to exit
handler.
If the entry callback function returns !0, the corresponding exit callback
will be cancelled.
@fp
This is the address of `fprobe` data structure related to this handler.
You can embed the `fprobe` to your data structure and get it by
container_of() macro from @fp. The @fp must not be NULL.
@entry_ip
This is the ftrace address of the traced function (both entry and exit).
Note that this may not be the actual entry address of the function but
the address where the ftrace is instrumented.
@ret_ip
This is the return address that the traced function will return to,
somewhere in the caller. This can be used at both entry and exit.
@fregs
This is the `ftrace_regs` data structure at the entry and exit. This
includes the function parameters, or the return values. So user can
access thos values via appropriate `ftrace_regs_*` APIs.
@entry_data
This is a local storage to share the data between entry and exit handlers.
This storage is NULL by default. If the user specify `exit_handler` field
and `entry_data_size` field when registering the fprobe, the storage is
allocated and passed to both `entry_handler` and `exit_handler`.
Entry data size and exit handlers on the same function
======================================================
Since the entry data is passed via per-task stack and it has limited size,
the entry data size per probe is limited to `15 * sizeof(long)`. You also need
to take care that the different fprobes are probing on the same function, this
limit becomes smaller. The entry data size is aligned to `sizeof(long)` and
each fprobe which has exit handler uses a `sizeof(long)` space on the stack,
you should keep the number of fprobes on the same function as small as
possible.
Share the callbacks with kprobes
================================
Since the recursion safeness of the fprobe (and ftrace) is a bit different
from the kprobes, this may cause an issue if user wants to run the same
code from the fprobe and the kprobes.
Kprobes has per-cpu 'current_kprobe' variable which protects the kprobe
handler from recursion in all cases. On the other hand, fprobe uses
only ftrace_test_recursion_trylock(). This allows interrupt context to
call another (or same) fprobe while the fprobe user handler is running.
This is not a matter if the common callback code has its own recursion
detection, or it can handle the recursion in the different contexts
(normal/interrupt/NMI.)
But if it relies on the 'current_kprobe' recursion lock, it has to check
kprobe_running() and use kprobe_busy_*() APIs.
Fprobe has FPROBE_FL_KPROBE_SHARED flag to do this. If your common callback
code will be shared with kprobes, please set FPROBE_FL_KPROBE_SHARED
*before* registering the fprobe, like:
.. code-block:: c
fprobe.flags = FPROBE_FL_KPROBE_SHARED;
register_fprobe(&fprobe, "func*", NULL);
This will protect your common callback from the nested call.
The missed counter
==================
The `fprobe` data structure has `fprobe::nmissed` counter field as same as
kprobes.
This counter counts up when;
- fprobe fails to take ftrace_recursion lock. This usually means that a function
which is traced by other ftrace users is called from the entry_handler.
- fprobe fails to setup the function exit because of failing to allocate the
data buffer from the per-task shadow stack.
The `fprobe::nmissed` field counts up in both cases. Therefore, the former
skips both of entry and exit callback and the latter skips the exit
callback, but in both case the counter will increase by 1.
Note that if you set the FTRACE_OPS_FL_RECURSION and/or FTRACE_OPS_FL_RCU to
`fprobe::ops::flags` (ftrace_ops::flags) when registering the fprobe, this
counter may not work correctly, because ftrace skips the fprobe function which
increase the counter.
Functions and structures
========================
.. kernel-doc:: include/linux/fprobe.h
.. kernel-doc:: kernel/trace/fprobe.c
@@ -0,0 +1,251 @@
.. SPDX-License-Identifier: GPL-2.0
==========================
Fprobe-based Event Tracing
==========================
.. Author: Masami Hiramatsu <mhiramat@kernel.org>
Overview
--------
Fprobe event is similar to the kprobe event, but limited to probe on
the function entry and exit only. It is good enough for many use cases
which only traces some specific functions.
This document also covers tracepoint probe events (tprobe) since this
is also works only on the tracepoint entry. User can trace a part of
tracepoint argument, or the tracepoint without trace-event, which is
not exposed on tracefs.
As same as other dynamic events, fprobe events and tracepoint probe
events are defined via `dynamic_events` interface file on tracefs.
Synopsis of fprobe-events
-------------------------
::
f[:[GRP1/][EVENT1]] SYM [FETCHARGS] : Probe on function entry
f[MAXACTIVE][:[GRP1/][EVENT1]] SYM%return [FETCHARGS] : Probe on function exit
t[:[GRP2/][EVENT2]] TRACEPOINT [FETCHARGS] : Probe on tracepoint
GRP1 : Group name for fprobe. If omitted, use "fprobes" for it.
GRP2 : Group name for tprobe. If omitted, use "tracepoints" for it.
EVENT1 : Event name for fprobe. If omitted, the event name is
"SYM__entry" or "SYM__exit".
EVENT2 : Event name for tprobe. If omitted, the event name is
the same as "TRACEPOINT", but if the "TRACEPOINT" starts
with a digit character, "_TRACEPOINT" is used.
MAXACTIVE : Maximum number of instances of the specified function that
can be probed simultaneously, or 0 for the default value
as defined in Documentation/trace/fprobe.rst
FETCHARGS : Arguments. Each probe can have up to 128 args.
ARG : Fetch "ARG" function argument using BTF (only for function
entry or tracepoint.) (\*1)
@ADDR : Fetch memory at ADDR (ADDR should be in kernel)
@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
$stackN : Fetch Nth entry of stack (N >= 0)
$stack : Fetch stack address.
$argN : Fetch the Nth function argument. (N >= 1) (\*2)
$retval : Fetch return value.(\*3)
$comm : Fetch current task comm.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*4)(\*5)
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
(x8/x16/x32/x64), "char", "string", "ustring", "symbol", "symstr"
and bitfield are supported.
(\*1) This is available only when BTF is enabled.
(\*2) only for the probe on function entry (offs == 0). Note, this argument access
is best effort, because depending on the argument type, it may be passed on
the stack. But this only support the arguments via registers.
(\*3) only for return probe. Note that this is also best effort. Depending on the
return value type, it might be passed via a pair of registers. But this only
accesses one register.
(\*4) this is useful for fetching a field of data structures.
(\*5) "u" means user-space dereference.
For the details of TYPE, see :ref:`kprobetrace documentation <kprobetrace_types>`.
Function arguments at exit
--------------------------
Function arguments can be accessed at exit probe using $arg<N> fetcharg. This
is useful to record the function parameter and return value at once, and
trace the difference of structure fields (for debugging a function whether it
correctly updates the given data structure or not)
See the :ref:`sample<fprobetrace_exit_args_sample>` below for how it works.
BTF arguments
-------------
BTF (BPF Type Format) argument allows user to trace function and tracepoint
parameters by its name instead of ``$argN``. This feature is available if the
kernel is configured with CONFIG_BPF_SYSCALL and CONFIG_DEBUG_INFO_BTF.
If user only specify the BTF argument, the event's argument name is also
automatically set by the given name. ::
# echo 'f:myprobe vfs_read count pos' >> dynamic_events
# cat dynamic_events
f:fprobes/myprobe vfs_read count=count pos=pos
It also chooses the fetch type from BTF information. For example, in the above
example, the ``count`` is unsigned long, and the ``pos`` is a pointer. Thus,
both are converted to 64bit unsigned long, but only ``pos`` has "%Lx"
print-format as below ::
# cat events/fprobes/myprobe/format
name: myprobe
ID: 1313
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 count; offset:16; size:8; signed:0;
field:u64 pos; offset:24; size:8; signed:0;
print fmt: "(%lx) count=%Lu pos=0x%Lx", REC->__probe_ip, REC->count, REC->pos
If user unsures the name of arguments, ``$arg*`` will be helpful. The ``$arg*``
is expanded to all function arguments of the function or the tracepoint. ::
# echo 'f:myprobe vfs_read $arg*' >> dynamic_events
# cat dynamic_events
f:fprobes/myprobe vfs_read file=file buf=buf count=count pos=pos
BTF also affects the ``$retval``. If user doesn't set any type, the retval
type is automatically picked from the BTF. If the function returns ``void``,
``$retval`` is rejected.
You can access the data fields of a data structure using allow operator ``->``
(for pointer type) and dot operator ``.`` (for data structure type.)::
# echo 't sched_switch preempt prev_pid=prev->pid next_pid=next->pid' >> dynamic_events
The field access operators, ``->`` and ``.`` can be combined for accessing deeper
members and other structure members pointed by the member. e.g. ``foo->bar.baz->qux``
If there is non-name union member, you can directly access it as the C code does.
For example::
struct {
union {
int a;
int b;
};
} *foo;
To access ``a`` and ``b``, use ``foo->a`` and ``foo->b`` in this case.
This data field access is available for the return value via ``$retval``,
e.g. ``$retval->name``.
For these BTF arguments and fields, ``:string`` and ``:ustring`` change the
behavior. If these are used for BTF argument or field, it checks whether
the BTF type of the argument or the data field is ``char *`` or ``char []``,
or not. If not, it rejects applying the string types. Also, with the BTF
support, you don't need a memory dereference operator (``+0(PTR)``) for
accessing the string pointed by a ``PTR``. It automatically adds the memory
dereference operator according to the BTF type. e.g. ::
# echo 't sched_switch prev->comm:string' >> dynamic_events
# echo 'f getname_flags%return $retval->name:string' >> dynamic_events
The ``prev->comm`` is an embedded char array in the data structure, and
``$retval->name`` is a char pointer in the data structure. But in both
cases, you can use ``:string`` type to get the string.
Usage examples
--------------
Here is an example to add fprobe events on ``vfs_read()`` function entry
and exit, with BTF arguments.
::
# echo 'f vfs_read $arg*' >> dynamic_events
# echo 'f vfs_read%return $retval' >> dynamic_events
# cat dynamic_events
f:fprobes/vfs_read__entry vfs_read file=file buf=buf count=count pos=pos
f:fprobes/vfs_read__exit vfs_read%return arg1=$retval
# echo 1 > events/fprobes/enable
# head -n 20 trace | tail
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
sh-70 [000] ...1. 335.883195: vfs_read__entry: (vfs_read+0x4/0x340) file=0xffff888005cf9a80 buf=0x7ffef36c6879 count=1 pos=0xffffc900005aff08
sh-70 [000] ..... 335.883208: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=1
sh-70 [000] ...1. 335.883220: vfs_read__entry: (vfs_read+0x4/0x340) file=0xffff888005cf9a80 buf=0x7ffef36c6879 count=1 pos=0xffffc900005aff08
sh-70 [000] ..... 335.883224: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=1
sh-70 [000] ...1. 335.883232: vfs_read__entry: (vfs_read+0x4/0x340) file=0xffff888005cf9a80 buf=0x7ffef36c687a count=1 pos=0xffffc900005aff08
sh-70 [000] ..... 335.883237: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=1
sh-70 [000] ...1. 336.050329: vfs_read__entry: (vfs_read+0x4/0x340) file=0xffff888005cf9a80 buf=0x7ffef36c6879 count=1 pos=0xffffc900005aff08
sh-70 [000] ..... 336.050343: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=1
You can see all function arguments and return values are recorded as signed int.
Also, here is an example of tracepoint events on ``sched_switch`` tracepoint.
To compare the result, this also enables the ``sched_switch`` traceevent too.
::
# echo 't sched_switch $arg*' >> dynamic_events
# echo 1 > events/sched/sched_switch/enable
# echo 1 > events/tracepoints/sched_switch/enable
# echo > trace
# head -n 20 trace | tail
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
sh-70 [000] d..2. 3912.083993: sched_switch: prev_comm=sh prev_pid=70 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
sh-70 [000] d..3. 3912.083995: sched_switch: (__probestub_sched_switch+0x4/0x10) preempt=0 prev=0xffff88800664e100 next=0xffffffff828229c0 prev_state=1
<idle>-0 [000] d..2. 3912.084183: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=rcu_preempt next_pid=16 next_prio=120
<idle>-0 [000] d..3. 3912.084184: sched_switch: (__probestub_sched_switch+0x4/0x10) preempt=0 prev=0xffffffff828229c0 next=0xffff888004208000 prev_state=0
rcu_preempt-16 [000] d..2. 3912.084196: sched_switch: prev_comm=rcu_preempt prev_pid=16 prev_prio=120 prev_state=I ==> next_comm=swapper/0 next_pid=0 next_prio=120
rcu_preempt-16 [000] d..3. 3912.084196: sched_switch: (__probestub_sched_switch+0x4/0x10) preempt=0 prev=0xffff888004208000 next=0xffffffff828229c0 prev_state=1026
<idle>-0 [000] d..2. 3912.085191: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=rcu_preempt next_pid=16 next_prio=120
<idle>-0 [000] d..3. 3912.085191: sched_switch: (__probestub_sched_switch+0x4/0x10) preempt=0 prev=0xffffffff828229c0 next=0xffff888004208000 prev_state=0
As you can see, the ``sched_switch`` trace-event shows *cooked* parameters, on
the other hand, the ``sched_switch`` tracepoint probe event shows *raw*
parameters. This means you can access any field values in the task
structure pointed by the ``prev`` and ``next`` arguments.
For example, usually ``task_struct::start_time`` is not traced, but with this
traceprobe event, you can trace that field as below.
::
# echo 't sched_switch comm=next->comm:string next->start_time' > dynamic_events
# head -n 20 trace | tail
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
sh-70 [000] d..3. 5606.686577: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="rcu_preempt" usage=1 start_time=245000000
rcu_preempt-16 [000] d..3. 5606.686602: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="sh" usage=1 start_time=1596095526
sh-70 [000] d..3. 5606.686637: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="swapper/0" usage=2 start_time=0
<idle>-0 [000] d..3. 5606.687190: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="rcu_preempt" usage=1 start_time=245000000
rcu_preempt-16 [000] d..3. 5606.687202: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="swapper/0" usage=2 start_time=0
<idle>-0 [000] d..3. 5606.690317: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="kworker/0:1" usage=1 start_time=137000000
kworker/0:1-14 [000] d..3. 5606.690339: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="swapper/0" usage=2 start_time=0
<idle>-0 [000] d..3. 5606.692368: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="kworker/0:1" usage=1 start_time=137000000
.. _fprobetrace_exit_args_sample:
The return probe allows us to access the results of some functions, which returns
the error code and its results are passed via function parameter, such as an
structure-initialization function.
For example, vfs_open() will link the file structure to the inode and update
mode. You can trace that changes with return probe.
::
# echo 'f vfs_open mode=file->f_mode:x32 inode=file->f_inode:x64' >> dynamic_events
# echo 'f vfs_open%%return mode=file->f_mode:x32 inode=file->f_inode:x64' >> dynamic_events
# echo 1 > events/fprobes/enable
# cat trace
sh-131 [006] ...1. 1945.714346: vfs_open__entry: (vfs_open+0x4/0x40) mode=0x2 inode=0x0
sh-131 [006] ...1. 1945.714358: vfs_open__exit: (do_open+0x274/0x3d0 <- vfs_open) mode=0x4d801e inode=0xffff888008470168
cat-143 [007] ...1. 1945.717949: vfs_open__entry: (vfs_open+0x4/0x40) mode=0x1 inode=0x0
cat-143 [007] ...1. 1945.717956: vfs_open__exit: (do_open+0x274/0x3d0 <- vfs_open) mode=0x4a801d inode=0xffff888005f78d28
cat-143 [007] ...1. 1945.720616: vfs_open__entry: (vfs_open+0x4/0x40) mode=0x1 inode=0x0
cat-143 [007] ...1. 1945.728263: vfs_open__exit: (do_open+0x274/0x3d0 <- vfs_open) mode=0xa800d inode=0xffff888004ada8d8
You can see the `file::f_mode` and `file::f_inode` are updated in `vfs_open()`.
@@ -0,0 +1,395 @@
======================
Function Tracer Design
======================
:Author: Mike Frysinger
.. caution::
This document is out of date. Some of the description below doesn't
match current implementation now.
Introduction
------------
Here we will cover the architecture pieces that the common function tracing
code relies on for proper functioning. Things are broken down into increasing
complexity so that you can start simple and at least get basic functionality.
Note that this focuses on architecture implementation details only. If you
want more explanation of a feature in terms of common code, review the common
ftrace.txt file.
Ideally, everyone who wishes to retain performance while supporting tracing in
their kernel should make it all the way to dynamic ftrace support.
Prerequisites
-------------
Ftrace relies on these features being implemented:
- STACKTRACE_SUPPORT - implement save_stack_trace()
- TRACE_IRQFLAGS_SUPPORT - implement include/asm/irqflags.h
HAVE_FUNCTION_TRACER
--------------------
You will need to implement the mcount and the ftrace_stub functions.
The exact mcount symbol name will depend on your toolchain. Some call it
"mcount", "_mcount", or even "__mcount". You can probably figure it out by
running something like::
$ echo 'main(){}' | gcc -x c -S -o - - -pg | grep mcount
call mcount
We'll make the assumption below that the symbol is "mcount" just to keep things
nice and simple in the examples.
Keep in mind that the ABI that is in effect inside of the mcount function is
*highly* architecture/toolchain specific. We cannot help you in this regard,
sorry. Dig up some old documentation and/or find someone more familiar than
you to bang ideas off of. Typically, register usage (argument/scratch/etc...)
is a major issue at this point, especially in relation to the location of the
mcount call (before/after function prologue). You might also want to look at
how glibc has implemented the mcount function for your architecture. It might
be (semi-)relevant.
The mcount function should check the function pointer ftrace_trace_function
to see if it is set to ftrace_stub. If it is, there is nothing for you to do,
so return immediately. If it isn't, then call that function in the same way
the mcount function normally calls __mcount_internal -- the first argument is
the "frompc" while the second argument is the "selfpc" (adjusted to remove the
size of the mcount call that is embedded in the function).
For example, if the function foo() calls bar(), when the bar() function calls
mcount(), the arguments mcount() will pass to the tracer are:
- "frompc" - the address bar() will use to return to foo()
- "selfpc" - the address bar() (with mcount() size adjustment)
Also keep in mind that this mcount function will be called *a lot*, so
optimizing for the default case of no tracer will help the smooth running of
your system when tracing is disabled. So the start of the mcount function is
typically the bare minimum with checking things before returning. That also
means the code flow should usually be kept linear (i.e. no branching in the nop
case). This is of course an optimization and not a hard requirement.
Here is some pseudo code that should help (these functions should actually be
implemented in assembly)::
void ftrace_stub(void)
{
return;
}
void mcount(void)
{
/* save any bare state needed in order to do initial checking */
extern void (*ftrace_trace_function)(unsigned long, unsigned long);
if (ftrace_trace_function != ftrace_stub)
goto do_trace;
/* restore any bare state */
return;
do_trace:
/* save all state needed by the ABI (see paragraph above) */
unsigned long frompc = ...;
unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
ftrace_trace_function(frompc, selfpc);
/* restore all state needed by the ABI */
}
Don't forget to export mcount for modules !
::
extern void mcount(void);
EXPORT_SYMBOL(mcount);
HAVE_FUNCTION_GRAPH_TRACER
--------------------------
Deep breath ... time to do some real work. Here you will need to update the
mcount function to check ftrace graph function pointers, as well as implement
some functions to save (hijack) and restore the return address.
The mcount function should check the function pointers ftrace_graph_return
(compare to ftrace_stub) and ftrace_graph_entry (compare to
ftrace_graph_entry_stub). If either of those is not set to the relevant stub
function, call the arch-specific function ftrace_graph_caller which in turn
calls the arch-specific function prepare_ftrace_return. Neither of these
function names is strictly required, but you should use them anyway to stay
consistent across the architecture ports -- easier to compare & contrast
things.
The arguments to prepare_ftrace_return are slightly different than what are
passed to ftrace_trace_function. The second argument "selfpc" is the same,
but the first argument should be a pointer to the "frompc". Typically this is
located on the stack. This allows the function to hijack the return address
temporarily to have it point to the arch-specific function return_to_handler.
That function will simply call the common ftrace_return_to_handler function and
that will return the original return address with which you can return to the
original call site.
Here is the updated mcount pseudo code::
void mcount(void)
{
...
if (ftrace_trace_function != ftrace_stub)
goto do_trace;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ extern void (*ftrace_graph_return)(...);
+ extern void (*ftrace_graph_entry)(...);
+ if (ftrace_graph_return != ftrace_stub ||
+ ftrace_graph_entry != ftrace_graph_entry_stub)
+ ftrace_graph_caller();
+#endif
/* restore any bare state */
...
Here is the pseudo code for the new ftrace_graph_caller assembly function::
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
void ftrace_graph_caller(void)
{
/* save all state needed by the ABI */
unsigned long *frompc = &...;
unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
/* passing frame pointer up is optional -- see below */
prepare_ftrace_return(frompc, selfpc, frame_pointer);
/* restore all state needed by the ABI */
}
#endif
For information on how to implement prepare_ftrace_return(), simply look at the
x86 version (the frame pointer passing is optional; see the next section for
more information). The only architecture-specific piece in it is the setup of
the fault recovery table (the asm(...) code). The rest should be the same
across architectures.
Here is the pseudo code for the new return_to_handler assembly function. Note
that the ABI that applies here is different from what applies to the mcount
code. Since you are returning from a function (after the epilogue), you might
be able to skimp on things saved/restored (usually just registers used to pass
return values).
::
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
void return_to_handler(void)
{
/* save all state needed by the ABI (see paragraph above) */
void (*original_return_point)(void) = ftrace_return_to_handler();
/* restore all state needed by the ABI */
/* this is usually either a return or a jump */
original_return_point();
}
#endif
HAVE_FUNCTION_GRAPH_FP_TEST
---------------------------
An arch may pass in a unique value (frame pointer) to both the entering and
exiting of a function. On exit, the value is compared and if it does not
match, then it will panic the kernel. This is largely a sanity check for bad
code generation with gcc. If gcc for your port sanely updates the frame
pointer under different optimization levels, then ignore this option.
However, adding support for it isn't terribly difficult. In your assembly code
that calls prepare_ftrace_return(), pass the frame pointer as the 3rd argument.
Then in the C version of that function, do what the x86 port does and pass it
along to ftrace_push_return_trace() instead of a stub value of 0.
Similarly, when you call ftrace_return_to_handler(), pass it the frame pointer.
HAVE_SYSCALL_TRACEPOINTS
------------------------
You need very few things to get the syscalls tracing in an arch.
- Support HAVE_ARCH_TRACEHOOK (see arch/Kconfig).
- Have a NR_syscalls variable in <asm/unistd.h> that provides the number
of syscalls supported by the arch.
- Support the TIF_SYSCALL_TRACEPOINT thread flags.
- Put the trace_sys_enter() and trace_sys_exit() tracepoints calls from ptrace
in the ptrace syscalls tracing path.
- If the system call table on this arch is more complicated than a simple array
of addresses of the system calls, implement an arch_syscall_addr to return
the address of a given system call.
- If the symbol names of the system calls do not match the function names on
this arch, define ARCH_HAS_SYSCALL_MATCH_SYM_NAME in asm/ftrace.h and
implement arch_syscall_match_sym_name with the appropriate logic to return
true if the function name corresponds with the symbol name.
- Tag this arch as HAVE_SYSCALL_TRACEPOINTS.
HAVE_DYNAMIC_FTRACE
-------------------
See scripts/recordmcount.pl for more info. Just fill in the arch-specific
details for how to locate the addresses of mcount call sites via objdump.
This option doesn't make much sense without also implementing dynamic ftrace.
You will first need HAVE_FUNCTION_TRACER, so scroll your reader back up if you
got over eager.
Once those are out of the way, you will need to implement:
- asm/ftrace.h:
- MCOUNT_ADDR
- ftrace_call_adjust()
- struct dyn_arch_ftrace{}
- asm code:
- mcount() (new stub)
- ftrace_caller()
- ftrace_call()
- ftrace_stub()
- C code:
- ftrace_dyn_arch_init()
- ftrace_make_nop()
- ftrace_make_call()
- ftrace_update_ftrace_func()
First you will need to fill out some arch details in your asm/ftrace.h.
Define MCOUNT_ADDR as the address of your mcount symbol similar to::
#define MCOUNT_ADDR ((unsigned long)mcount)
Since no one else will have a decl for that function, you will need to::
extern void mcount(void);
You will also need the helper function ftrace_call_adjust(). Most people
will be able to stub it out like so::
static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
return addr;
}
<details to be filled>
Lastly you will need the custom dyn_arch_ftrace structure. If you need
some extra state when runtime patching arbitrary call sites, this is the
place. For now though, create an empty struct::
struct dyn_arch_ftrace {
/* No extra data needed */
};
With the header out of the way, we can fill out the assembly code. While we
did already create a mcount() function earlier, dynamic ftrace only wants a
stub function. This is because the mcount() will only be used during boot
and then all references to it will be patched out never to return. Instead,
the guts of the old mcount() will be used to create a new ftrace_caller()
function. Because the two are hard to merge, it will most likely be a lot
easier to have two separate definitions split up by #ifdefs. Same goes for
the ftrace_stub() as that will now be inlined in ftrace_caller().
Before we get confused anymore, let's check out some pseudo code so you can
implement your own stuff in assembly::
void mcount(void)
{
return;
}
void ftrace_caller(void)
{
/* save all state needed by the ABI (see paragraph above) */
unsigned long frompc = ...;
unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
ftrace_call:
ftrace_stub(frompc, selfpc);
/* restore all state needed by the ABI */
ftrace_stub:
return;
}
This might look a little odd at first, but keep in mind that we will be runtime
patching multiple things. First, only functions that we actually want to trace
will be patched to call ftrace_caller(). Second, since we only have one tracer
active at a time, we will patch the ftrace_caller() function itself to call the
specific tracer in question. That is the point of the ftrace_call label.
With that in mind, let's move on to the C code that will actually be doing the
runtime patching. You'll need a little knowledge of your arch's opcodes in
order to make it through the next section.
Every arch has an init callback function. If you need to do something early on
to initialize some state, this is the time to do that. Otherwise, this simple
function below should be sufficient for most people::
int __init ftrace_dyn_arch_init(void)
{
return 0;
}
There are two functions that are used to do runtime patching of arbitrary
functions. The first is used to turn the mcount call site into a nop (which
is what helps us retain runtime performance when not tracing). The second is
used to turn the mcount call site into a call to an arbitrary location (but
typically that is ftracer_caller()). See the general function definition in
linux/ftrace.h for the functions::
ftrace_make_nop()
ftrace_make_call()
The rec->ip value is the address of the mcount call site that was collected
by the scripts/recordmcount.pl during build time.
The last function is used to do runtime patching of the active tracer. This
will be modifying the assembly code at the location of the ftrace_call symbol
inside of the ftrace_caller() function. So you should have sufficient padding
at that location to support the new function calls you'll be inserting. Some
people will be using a "call" type instruction while others will be using a
"branch" type instruction. Specifically, the function is::
ftrace_update_ftrace_func()
HAVE_DYNAMIC_FTRACE + HAVE_FUNCTION_GRAPH_TRACER
------------------------------------------------
The function grapher needs a few tweaks in order to work with dynamic ftrace.
Basically, you will need to:
- update:
- ftrace_caller()
- ftrace_graph_call()
- ftrace_graph_caller()
- implement:
- ftrace_enable_ftrace_graph_caller()
- ftrace_disable_ftrace_graph_caller()
<details to be filled>
Quick notes:
- add a nop stub after the ftrace_call location named ftrace_graph_call;
stub needs to be large enough to support a call to ftrace_graph_caller()
- update ftrace_graph_caller() to work with being called by the new
ftrace_caller() since some semantics may have changed
- ftrace_enable_ftrace_graph_caller() will runtime patch the
ftrace_graph_call location with a call to ftrace_graph_caller()
- ftrace_disable_ftrace_graph_caller() will runtime patch the
ftrace_graph_call location with nops
@@ -0,0 +1,348 @@
=================================
Using ftrace to hook to functions
=================================
.. Copyright 2017 VMware Inc.
.. Author: Steven Rostedt <srostedt@goodmis.org>
.. License: The GNU Free Documentation License, Version 1.2
.. (dual licensed under the GPL v2)
Written for: 4.14
Introduction
============
The ftrace infrastructure was originally created to attach callbacks to the
beginning of functions in order to record and trace the flow of the kernel.
But callbacks to the start of a function can have other use cases. Either
for live kernel patching, or for security monitoring. This document describes
how to use ftrace to implement your own function callbacks.
The ftrace context
==================
.. warning::
The ability to add a callback to almost any function within the
kernel comes with risks. A callback can be called from any context
(normal, softirq, irq, and NMI). Callbacks can also be called just before
going to idle, during CPU bring up and takedown, or going to user space.
This requires extra care to what can be done inside a callback. A callback
can be called outside the protective scope of RCU.
There are helper functions to help against recursion, and making sure
RCU is watching. These are explained below.
The ftrace_ops structure
========================
To register a function callback, a ftrace_ops is required. This structure
is used to tell ftrace what function should be called as the callback
as well as what protections the callback will perform and not require
ftrace to handle.
There is only one field that is needed to be set when registering
an ftrace_ops with ftrace:
.. code-block:: c
struct ftrace_ops ops = {
.func = my_callback_func,
.flags = MY_FTRACE_FLAGS
.private = any_private_data_structure,
};
Both .flags and .private are optional. Only .func is required.
To enable tracing call::
register_ftrace_function(&ops);
To disable tracing call::
unregister_ftrace_function(&ops);
The above is defined by including the header::
#include <linux/ftrace.h>
The registered callback will start being called some time after the
register_ftrace_function() is called and before it returns. The exact time
that callbacks start being called is dependent upon architecture and scheduling
of services. The callback itself will have to handle any synchronization if it
must begin at an exact moment.
The unregister_ftrace_function() will guarantee that the callback is
no longer being called by functions after the unregister_ftrace_function()
returns. Note that to perform this guarantee, the unregister_ftrace_function()
may take some time to finish.
The callback function
=====================
The prototype of the callback function is as follows (as of v4.14):
.. code-block:: c
void callback_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs);
@ip
This is the instruction pointer of the function that is being traced.
(where the fentry or mcount is within the function)
@parent_ip
This is the instruction pointer of the function that called the
the function being traced (where the call of the function occurred).
@op
This is a pointer to ftrace_ops that was used to register the callback.
This can be used to pass data to the callback via the private pointer.
@regs
If the FTRACE_OPS_FL_SAVE_REGS or FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED
flags are set in the ftrace_ops structure, then this will be pointing
to the pt_regs structure like it would be if an breakpoint was placed
at the start of the function where ftrace was tracing. Otherwise it
either contains garbage, or NULL.
Protect your callback
=====================
As functions can be called from anywhere, and it is possible that a function
called by a callback may also be traced, and call that same callback,
recursion protection must be used. There are two helper functions that
can help in this regard. If you start your code with:
.. code-block:: c
int bit;
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;
and end it with:
.. code-block:: c
ftrace_test_recursion_unlock(bit);
The code in between will be safe to use, even if it ends up calling a
function that the callback is tracing. Note, on success,
ftrace_test_recursion_trylock() will disable preemption, and the
ftrace_test_recursion_unlock() will enable it again (if it was previously
enabled). The instruction pointer (ip) and its parent (parent_ip) is passed to
ftrace_test_recursion_trylock() to record where the recursion happened
(if CONFIG_FTRACE_RECORD_RECURSION is set).
Alternatively, if the FTRACE_OPS_FL_RECURSION flag is set on the ftrace_ops
(as explained below), then a helper trampoline will be used to test
for recursion for the callback and no recursion test needs to be done.
But this is at the expense of a slightly more overhead from an extra
function call.
If your callback accesses any data or critical section that requires RCU
protection, it is best to make sure that RCU is "watching", otherwise
that data or critical section will not be protected as expected. In this
case add:
.. code-block:: c
if (!rcu_is_watching())
return;
Alternatively, if the FTRACE_OPS_FL_RCU flag is set on the ftrace_ops
(as explained below), then a helper trampoline will be used to test
for rcu_is_watching for the callback and no other test needs to be done.
But this is at the expense of a slightly more overhead from an extra
function call.
The ftrace FLAGS
================
The ftrace_ops flags are all defined and documented in include/linux/ftrace.h.
Some of the flags are used for internal infrastructure of ftrace, but the
ones that users should be aware of are the following:
FTRACE_OPS_FL_SAVE_REGS
If the callback requires reading or modifying the pt_regs
passed to the callback, then it must set this flag. Registering
a ftrace_ops with this flag set on an architecture that does not
support passing of pt_regs to the callback will fail.
FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED
Similar to SAVE_REGS but the registering of a
ftrace_ops on an architecture that does not support passing of regs
will not fail with this flag set. But the callback must check if
regs is NULL or not to determine if the architecture supports it.
FTRACE_OPS_FL_RECURSION
By default, it is expected that the callback can handle recursion.
But if the callback is not that worried about overhead, then
setting this bit will add the recursion protection around the
callback by calling a helper function that will do the recursion
protection and only call the callback if it did not recurse.
Note, if this flag is not set, and recursion does occur, it could
cause the system to crash, and possibly reboot via a triple fault.
Note, if this flag is set, then the callback will always be called
with preemption disabled. If it is not set, then it is possible
(but not guaranteed) that the callback will be called in
preemptible context.
FTRACE_OPS_FL_IPMODIFY
Requires FTRACE_OPS_FL_SAVE_REGS set. If the callback is to "hijack"
the traced function (have another function called instead of the
traced function), it requires setting this flag. This is what live
kernel patches uses. Without this flag the pt_regs->ip can not be
modified.
Note, only one ftrace_ops with FTRACE_OPS_FL_IPMODIFY set may be
registered to any given function at a time.
FTRACE_OPS_FL_RCU
If this is set, then the callback will only be called by functions
where RCU is "watching". This is required if the callback function
performs any rcu_read_lock() operation.
RCU stops watching when the system goes idle, the time when a CPU
is taken down and comes back online, and when entering from kernel
to user space and back to kernel space. During these transitions,
a callback may be executed and RCU synchronization will not protect
it.
FTRACE_OPS_FL_PERMANENT
If this is set on any ftrace ops, then the tracing cannot disabled by
writing 0 to the proc sysctl ftrace_enabled. Equally, a callback with
the flag set cannot be registered if ftrace_enabled is 0.
Livepatch uses it not to lose the function redirection, so the system
stays protected.
Filtering which functions to trace
==================================
If a callback is only to be called from specific functions, a filter must be
set up. The filters are added by name, or ip if it is known.
.. code-block:: c
int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset);
@ops
The ops to set the filter with
@buf
The string that holds the function filter text.
@len
The length of the string.
@reset
Non-zero to reset all filters before applying this filter.
Filters denote which functions should be enabled when tracing is enabled.
If @buf is NULL and reset is set, all functions will be enabled for tracing.
The @buf can also be a glob expression to enable all functions that
match a specific pattern.
See Filter Commands in Documentation/trace/ftrace.rst.
To just trace the schedule function:
.. code-block:: c
ret = ftrace_set_filter(&ops, "schedule", strlen("schedule"), 0);
To add more functions, call the ftrace_set_filter() more than once with the
@reset parameter set to zero. To remove the current filter set and replace it
with new functions defined by @buf, have @reset be non-zero.
To remove all the filtered functions and trace all functions:
.. code-block:: c
ret = ftrace_set_filter(&ops, NULL, 0, 1);
Sometimes more than one function has the same name. To trace just a specific
function in this case, ftrace_set_filter_ip() can be used.
.. code-block:: c
ret = ftrace_set_filter_ip(&ops, ip, 0, 0);
Although the ip must be the address where the call to fentry or mcount is
located in the function. This function is used by perf and kprobes that
gets the ip address from the user (usually using debug info from the kernel).
If a glob is used to set the filter, functions can be added to a "notrace"
list that will prevent those functions from calling the callback.
The "notrace" list takes precedence over the "filter" list. If the
two lists are non-empty and contain the same functions, the callback will not
be called by any function.
An empty "notrace" list means to allow all functions defined by the filter
to be traced.
.. code-block:: c
int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset);
This takes the same parameters as ftrace_set_filter() but will add the
functions it finds to not be traced. This is a separate list from the
filter list, and this function does not modify the filter list.
A non-zero @reset will clear the "notrace" list before adding functions
that match @buf to it.
Clearing the "notrace" list is the same as clearing the filter list
.. code-block:: c
ret = ftrace_set_notrace(&ops, NULL, 0, 1);
The filter and notrace lists may be changed at any time. If only a set of
functions should call the callback, it is best to set the filters before
registering the callback. But the changes may also happen after the callback
has been registered.
If a filter is in place, and the @reset is non-zero, and @buf contains a
matching glob to functions, the switch will happen during the time of
the ftrace_set_filter() call. At no time will all functions call the callback.
.. code-block:: c
ftrace_set_filter(&ops, "schedule", strlen("schedule"), 1);
register_ftrace_function(&ops);
msleep(10);
ftrace_set_filter(&ops, "try_to_wake_up", strlen("try_to_wake_up"), 1);
is not the same as:
.. code-block:: c
ftrace_set_filter(&ops, "schedule", strlen("schedule"), 1);
register_ftrace_function(&ops);
msleep(10);
ftrace_set_filter(&ops, NULL, 0, 1);
ftrace_set_filter(&ops, "try_to_wake_up", strlen("try_to_wake_up"), 0);
As the latter will have a short time where all functions will call
the callback, between the time of the reset, and the time of the
new setting of the filter.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
" Enable folding for ftrace function_graph traces.
"
" To use, :source this file while viewing a function_graph trace, or use vim's
" -S option to load from the command-line together with a trace. You can then
" use the usual vim fold commands, such as "za", to open and close nested
" functions. While closed, a fold will show the total time taken for a call,
" as would normally appear on the line with the closing brace. Folded
" functions will not include finish_task_switch(), so folding should remain
" relatively sane even through a context switch.
"
" Note that this will almost certainly only work well with a
" single-CPU trace (e.g. trace-cmd report --cpu 1).
function! FunctionGraphFoldExpr(lnum)
let line = getline(a:lnum)
if line[-1:] == '{'
if line =~ 'finish_task_switch() {$'
return '>1'
endif
return 'a1'
elseif line[-1:] == '}'
return 's1'
else
return '='
endif
endfunction
function! FunctionGraphFoldText()
let s = split(getline(v:foldstart), '|', 1)
if getline(v:foldend+1) =~ 'finish_task_switch() {$'
let s[2] = ' task switch '
else
let e = split(getline(v:foldend), '|', 1)
let s[2] = e[2]
endif
return join(s, '|')
endfunction
setlocal foldexpr=FunctionGraphFoldExpr(v:lnum)
setlocal foldtext=FunctionGraphFoldText()
setlocal foldcolumn=12
setlocal foldmethod=expr
@@ -0,0 +1,304 @@
.. SPDX-License-Identifier: GPL-2.0
======================================
HiSilicon PCIe Tune and Trace device
======================================
Introduction
============
HiSilicon PCIe tune and trace device (PTT) is a PCIe Root Complex
integrated Endpoint (RCiEP) device, providing the capability
to dynamically monitor and tune the PCIe link's events (tune),
and trace the TLP headers (trace). The two functions are independent,
but is recommended to use them together to analyze and enhance the
PCIe link's performance.
On Kunpeng 930 SoC, the PCIe Root Complex is composed of several
PCIe cores. Each PCIe core includes several Root Ports and a PTT
RCiEP, like below. The PTT device is capable of tuning and
tracing the links of the PCIe core.
::
+--------------Core 0-------+
| | [ PTT ] |
| | [Root Port]---[Endpoint]
| | [Root Port]---[Endpoint]
| | [Root Port]---[Endpoint]
Root Complex |------Core 1-------+
| | [ PTT ] |
| | [Root Port]---[ Switch ]---[Endpoint]
| | [Root Port]---[Endpoint] `-[Endpoint]
| | [Root Port]---[Endpoint]
+---------------------------+
The PTT device driver registers one PMU device for each PTT device.
The name of each PTT device is composed of 'hisi_ptt' prefix with
the id of the SICL and the Core where it locates. The Kunpeng 930
SoC encapsulates multiple CPU dies (SCCL, Super CPU Cluster) and
IO dies (SICL, Super I/O Cluster), where there's one PCIe Root
Complex for each SICL.
::
/sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>
Tune
====
PTT tune is designed for monitoring and adjusting PCIe link parameters (events).
Currently we support events in 2 classes. The scope of the events
covers the PCIe core to which the PTT device belongs.
Each event is presented as a file under $(PTT PMU dir)/tune, and
a simple open/read/write/close cycle will be used to tune the event.
::
$ cd /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune
$ ls
qos_tx_cpl qos_tx_np qos_tx_p
tx_path_rx_req_alloc_buf_level
tx_path_tx_req_alloc_buf_level
$ cat qos_tx_dp
1
$ echo 2 > qos_tx_dp
$ cat qos_tx_dp
2
Current value (numerical value) of the event can be simply read
from the file, and the desired value written to the file to tune.
1. Tx Path QoS Control
------------------------
The following files are provided to tune the QoS of the tx path of
the PCIe core.
- qos_tx_cpl: weight of Tx completion TLPs
- qos_tx_np: weight of Tx non-posted TLPs
- qos_tx_p: weight of Tx posted TLPs
The weight influences the proportion of certain packets on the PCIe link.
For example, for the storage scenario, increase the proportion
of the completion packets on the link to enhance the performance as
more completions are consumed.
The available tune data of these events is [0, 1, 2].
Writing a negative value will return an error, and out of range
values will be converted to 2. Note that the event value just
indicates a probable level, but is not precise.
2. Tx Path Buffer Control
-------------------------
Following files are provided to tune the buffer of tx path of the PCIe core.
- rx_alloc_buf_level: watermark of Rx requested
- tx_alloc_buf_level: watermark of Tx requested
These events influence the watermark of the buffer allocated for each
type. Rx means the inbound while Tx means outbound. The packets will
be stored in the buffer first and then transmitted either when the
watermark reached or when timed out. For a busy direction, you should
increase the related buffer watermark to avoid frequently posting and
thus enhance the performance. In most cases just keep the default value.
The available tune data of above events is [0, 1, 2].
Writing a negative value will return an error, and out of range
values will be converted to 2. Note that the event value just
indicates a probable level, but is not precise.
Trace
=====
PTT trace is designed for dumping the TLP headers to the memory, which
can be used to analyze the transactions and usage condition of the PCIe
Link. You can choose to filter the traced headers by either Requester ID,
or those downstream of a set of Root Ports on the same core of the PTT
device. It's also supported to trace the headers of certain type and of
certain direction.
You can use the perf command `perf record` to set the parameters, start
trace and get the data. It's also supported to decode the trace
data with `perf report`. The control parameters for trace is inputted
as event code for each events, which will be further illustrated later.
An example usage is like
::
$ perf record -e hisi_ptt0_2/filter=0x80001,type=1,direction=1,
format=1/ -- sleep 5
This will trace the TLP headers downstream root port 0000:00:10.1 (event
code for event 'filter' is 0x80001) with type of posted TLP requests,
direction of inbound and traced data format of 8DW.
1. Filter
---------
The TLP headers to trace can be filtered by the Root Ports or the Requester ID
of the Endpoint, which are located on the same core of the PTT device. You can
set the filter by specifying the `filter` parameter which is required to start
the trace. The parameter value is 20 bit. Bit 19 indicates the filter type.
1 for Root Port filter and 0 for Requester filter. Bit[15:0] indicates the
filter value. The value for a Root Port is a mask of the core port id which is
calculated from its PCI Slot ID as (slotid & 7) * 2. The value for a Requester
is the Requester ID (Device ID of the PCIe function). Bit[18:16] is currently
reserved for extension.
For example, if the desired filter is Endpoint function 0000:01:00.1 the filter
value will be 0x00101. If the desired filter is Root Port 0000:00:10.0 then
then filter value is calculated as 0x80001.
The driver also presents every supported Root Port and Requester filter through
sysfs. Each filter will be an individual file with name of its related PCIe
device name (domain:bus:device.function). The files of Root Port filters are
under $(PTT PMU dir)/root_port_filters and files of Requester filters
are under $(PTT PMU dir)/requester_filters.
Note that multiple Root Ports can be specified at one time, but only one
Endpoint function can be specified in one trace. Specifying both Root Port
and function at the same time is not supported. Driver maintains a list of
available filters and will check the invalid inputs.
The available filters will be dynamically updated, which means you will always
get correct filter information when hotplug events happen, or when you manually
remove/rescan the devices.
2. Type
-------
You can trace the TLP headers of certain types by specifying the `type`
parameter, which is required to start the trace. The parameter value is
8 bit. Current supported types and related values are shown below:
- 8'b00000001: posted requests (P)
- 8'b00000010: non-posted requests (NP)
- 8'b00000100: completions (CPL)
You can specify multiple types when tracing inbound TLP headers, but can only
specify one when tracing outbound TLP headers.
3. Direction
------------
You can trace the TLP headers from certain direction, which is relative
to the Root Port or the PCIe core, by specifying the `direction` parameter.
This is optional and the default parameter is inbound. The parameter value
is 4 bit. When the desired format is 4DW, directions and related values
supported are shown below:
- 4'b0000: inbound TLPs (P, NP, CPL)
- 4'b0001: outbound TLPs (P, NP, CPL)
- 4'b0010: outbound TLPs (P, NP, CPL) and inbound TLPs (P, NP, CPL B)
- 4'b0011: outbound TLPs (P, NP, CPL) and inbound TLPs (CPL A)
When the desired format is 8DW, directions and related values supported are
shown below:
- 4'b0000: reserved
- 4'b0001: outbound TLPs (P, NP, CPL)
- 4'b0010: inbound TLPs (P, NP, CPL B)
- 4'b0011: inbound TLPs (CPL A)
Inbound completions are classified into two types:
- completion A (CPL A): completion of CHI/DMA/Native non-posted requests, except for CPL B
- completion B (CPL B): completion of DMA remote2local and P2P non-posted requests
4. Format
--------------
You can change the format of the traced TLP headers by specifying the
`format` parameter. The default format is 4DW. The parameter value is 4 bit.
Current supported formats and related values are shown below:
- 4'b0000: 4DW length per TLP header
- 4'b0001: 8DW length per TLP header
The traced TLP header format is different from the PCIe standard.
When using the 8DW data format, the entire TLP header is logged
(Header DW0-3 shown below). For example, the TLP header for Memory
Reads with 64-bit addresses is shown in PCIe r5.0, Figure 2-17;
the header for Configuration Requests is shown in Figure 2.20, etc.
In addition, 8DW trace buffer entries contain a timestamp and
possibly a prefix for a PASID TLP prefix (see Figure 6-20, PCIe r5.0).
Otherwise this field will be all 0.
The bit[31:11] of DW0 is always 0x1fffff, which can be
used to distinguish the data format. 8DW format is like
::
bits [ 31:11 ][ 10:0 ]
|---------------------------------------|-------------------|
DW0 [ 0x1fffff ][ Reserved (0x7ff) ]
DW1 [ Prefix ]
DW2 [ Header DW0 ]
DW3 [ Header DW1 ]
DW4 [ Header DW2 ]
DW5 [ Header DW3 ]
DW6 [ Reserved (0x0) ]
DW7 [ Time ]
When using the 4DW data format, DW0 of the trace buffer entry
contains selected fields of DW0 of the TLP, together with a
timestamp. DW1-DW3 of the trace buffer entry contain DW1-DW3
directly from the TLP header.
4DW format is like
::
bits [31:30] [ 29:25 ][24][23][22][21][ 20:11 ][ 10:0 ]
|-----|---------|---|---|---|---|-------------|-------------|
DW0 [ Fmt ][ Type ][T9][T8][TH][SO][ Length ][ Time ]
DW1 [ Header DW1 ]
DW2 [ Header DW2 ]
DW3 [ Header DW3 ]
5. Memory Management
--------------------
The traced TLP headers will be written to the memory allocated
by the driver. The hardware accepts 4 DMA address with same size,
and writes the buffer sequentially like below. If DMA addr 3 is
finished and the trace is still on, it will return to addr 0.
::
+->[DMA addr 0]->[DMA addr 1]->[DMA addr 2]->[DMA addr 3]-+
+---------------------------------------------------------+
Driver will allocate each DMA buffer of 4MiB. The finished buffer
will be copied to the perf AUX buffer allocated by the perf core.
Once the AUX buffer is full while the trace is still on, driver
will commit the AUX buffer first and then apply for a new one with
the same size. The size of AUX buffer is default to 16MiB. User can
adjust the size by specifying the `-m` parameter of the perf command.
6. Decoding
-----------
You can decode the traced data with `perf report -D` command (currently
only support to dump the raw trace data). The traced data will be decoded
according to the format described previously (take 8DW as an example):
::
[...perf headers and other information]
. ... HISI PTT data: size 4194304 bytes
. 00000000: 00 00 00 00 Prefix
. 00000004: 01 00 00 60 Header DW0
. 00000008: 0f 1e 00 01 Header DW1
. 0000000c: 04 00 00 00 Header DW2
. 00000010: 40 00 81 02 Header DW3
. 00000014: 33 c0 04 00 Time
. 00000020: 00 00 00 00 Prefix
. 00000024: 01 00 00 60 Header DW0
. 00000028: 0f 1e 00 01 Header DW1
. 0000002c: 04 00 00 00 Header DW2
. 00000030: 40 00 81 02 Header DW3
. 00000034: 02 00 00 00 Time
. 00000040: 00 00 00 00 Prefix
. 00000044: 01 00 00 60 Header DW0
. 00000048: 0f 1e 00 01 Header DW1
. 0000004c: 04 00 00 00 Header DW2
. 00000050: 40 00 81 02 Header DW3
[...]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,88 @@
=========================
Hardware Latency Detector
=========================
Introduction
-------------
The tracer hwlat_detector is a special purpose tracer that is used to
detect large system latencies induced by the behavior of certain underlying
hardware or firmware, independent of Linux itself. The code was developed
originally to detect SMIs (System Management Interrupts) on x86 systems,
however there is nothing x86 specific about this patchset. It was
originally written for use by the "RT" patch since the Real Time
kernel is highly latency sensitive.
SMIs are not serviced by the Linux kernel, which means that it does not
even know that they are occurring. SMIs are instead set up by BIOS code
and are serviced by BIOS code, usually for "critical" events such as
management of thermal sensors and fans. Sometimes though, SMIs are used for
other tasks and those tasks can spend an inordinate amount of time in the
handler (sometimes measured in milliseconds). Obviously this is a problem if
you are trying to keep event service latencies down in the microsecond range.
The hardware latency detector works by hogging one of the cpus for configurable
amounts of time (with interrupts disabled), polling the CPU Time Stamp Counter
for some period, then looking for gaps in the TSC data. Any gap indicates a
time when the polling was interrupted and since the interrupts are disabled,
the only thing that could do that would be an SMI or other hardware hiccup
(or an NMI, but those can be tracked).
Note that the hwlat detector should *NEVER* be used in a production environment.
It is intended to be run manually to determine if the hardware platform has a
problem with long system firmware service routines.
Usage
------
Write the ASCII text "hwlat" into the current_tracer file of the tracing system
(mounted at /sys/kernel/tracing or /sys/kernel/tracing). It is possible to
redefine the threshold in microseconds (us) above which latency spikes will
be taken into account.
Example::
# echo hwlat > /sys/kernel/tracing/current_tracer
# echo 100 > /sys/kernel/tracing/tracing_thresh
The /sys/kernel/tracing/hwlat_detector interface contains the following files:
- width - time period to sample with CPUs held (usecs)
must be less than the total window size (enforced)
- window - total period of sampling, width being inside (usecs)
By default the width is set to 500,000 and window to 1,000,000, meaning that
for every 1,000,000 usecs (1s) the hwlat detector will spin for 500,000 usecs
(0.5s). If tracing_thresh contains zero when hwlat tracer is enabled, it will
change to a default of 10 usecs. If any latencies that exceed the threshold is
observed then the data will be written to the tracing ring buffer.
The minimum sleep time between periods is 1 millisecond. Even if width
is less than 1 millisecond apart from window, to allow the system to not
be totally starved.
If tracing_thresh was zero when hwlat detector was started, it will be set
back to zero if another tracer is loaded. Note, the last value in
tracing_thresh that hwlat detector had will be saved and this value will
be restored in tracing_thresh if it is still zero when hwlat detector is
started again.
The following tracing directory files are used by the hwlat_detector:
in /sys/kernel/tracing:
- tracing_threshold - minimum latency value to be considered (usecs)
- tracing_max_latency - maximum hardware latency actually observed (usecs)
- tracing_cpumask - the CPUs to move the hwlat thread across
- hwlat_detector/width - specified amount of time to spin within window (usecs)
- hwlat_detector/window - amount of time between (width) runs (usecs)
- hwlat_detector/mode - the thread mode
By default, one hwlat detector's kernel thread will migrate across each CPU
specified in cpumask at the beginning of a new window, in a round-robin
fashion. This behavior can be changed by changing the thread mode,
the available options are:
- none: do not force migration
- round-robin: migrate across each CPU specified in cpumask [default]
- per-cpu: create one thread for each cpu in tracing_cpumask
+110
View File
@@ -0,0 +1,110 @@
================================
Linux Tracing Technologies Guide
================================
Tracing in the Linux kernel is a powerful mechanism that allows
developers and system administrators to analyze and debug system
behavior. This guide provides documentation on various tracing
frameworks and tools available in the Linux kernel.
Introduction to Tracing
-----------------------
This section provides an overview of Linux tracing mechanisms
and debugging approaches.
.. toctree::
:maxdepth: 1
debugging
tracepoints
tracepoint-analysis
ring-buffer-map
Core Tracing Frameworks
-----------------------
The following are the primary tracing frameworks integrated into
the Linux kernel.
.. toctree::
:maxdepth: 1
ftrace
ftrace-design
ftrace-uses
kprobes
kprobetrace
fprobetrace
eprobetrace
fprobe
ring-buffer-design
Event Tracing and Analysis
--------------------------
A detailed explanation of event tracing mechanisms and their
applications.
.. toctree::
:maxdepth: 1
events
events-kmem
events-power
events-nmi
events-msr
events-pci
events-pci-controller
boottime-trace
histogram
histogram-design
Hardware and Performance Tracing
--------------------------------
This section covers tracing features that monitor hardware
interactions and system performance.
.. toctree::
:maxdepth: 1
intel_th
stm
sys-t
coresight/index
rv/index
hisi-ptt
mmiotrace
hwlat_detector
osnoise-tracer
timerlat-tracer
User-Space Tracing
------------------
These tools allow tracing user-space applications and
interactions.
.. toctree::
:maxdepth: 1
user_events
uprobetracer
Remote Tracing
--------------
This section covers the framework to read compatible ring-buffers, written by
entities outside of the kernel (most likely firmware or hypervisor)
.. toctree::
:maxdepth: 1
remotes
Additional Resources
--------------------
For more details, refer to the respective documentation of each
tracing tool and framework.
@@ -0,0 +1,150 @@
.. SPDX-License-Identifier: GPL-2.0
=======================
Intel(R) Trace Hub (TH)
=======================
Overview
--------
Intel(R) Trace Hub (TH) is a set of hardware blocks that produce,
switch and output trace data from multiple hardware and software
sources over several types of trace output ports encoded in System
Trace Protocol (MIPI STPv2) and is intended to perform full system
debugging. For more information on the hardware, see Intel(R) Trace
Hub developer's manual [1].
It consists of trace sources, trace destinations (outputs) and a
switch (Global Trace Hub, GTH). These devices are placed on a bus of
their own ("intel_th"), where they can be discovered and configured
via sysfs attributes.
Currently, the following Intel TH subdevices (blocks) are supported:
- Software Trace Hub (STH), trace source, which is a System Trace
Module (STM) device,
- Memory Storage Unit (MSU), trace output, which allows storing
trace hub output in system memory,
- Parallel Trace Interface output (PTI), trace output to an external
debug host via a PTI port,
- Global Trace Hub (GTH), which is a switch and a central component
of Intel(R) Trace Hub architecture.
Common attributes for output devices are described in
Documentation/ABI/testing/sysfs-bus-intel_th-output-devices, the most
notable of them is "active", which enables or disables trace output
into that particular output device.
GTH allows directing different STP masters into different output ports
via its "masters" attribute group. More detailed GTH interface
description is at Documentation/ABI/testing/sysfs-bus-intel_th-devices-gth.
STH registers an stm class device, through which it provides interface
to userspace and kernelspace software trace sources. See
Documentation/trace/stm.rst for more information on that.
MSU can be configured to collect trace data into a system memory
buffer, which can later on be read from its device nodes via read() or
mmap() interface and directed to a "software sink" driver that will
consume the data and/or relay it further.
On the whole, Intel(R) Trace Hub does not require any special
userspace software to function; everything can be configured, started
and collected via sysfs attributes, and device nodes.
[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
Bus and Subdevices
------------------
For each Intel TH device in the system a bus of its own is
created and assigned an id number that reflects the order in which TH
devices were enumerated. All TH subdevices (devices on intel_th bus)
begin with this id: 0-gth, 0-msc0, 0-msc1, 0-pti, 0-sth, which is
followed by device's name and an optional index.
Output devices also get a device node in /dev/intel_thN, where N is
the Intel TH device id. For example, MSU's memory buffers, when
allocated, are accessible via /dev/intel_th0/msc{0,1}.
Quick example
-------------
# figure out which GTH port is the first memory controller::
$ cat /sys/bus/intel_th/devices/0-msc0/port
0
# looks like it's port 0, configure master 33 to send data to port 0::
$ echo 0 > /sys/bus/intel_th/devices/0-gth/masters/33
# allocate a 2-windowed multiblock buffer on the first memory
# controller, each with 64 pages::
$ echo multi > /sys/bus/intel_th/devices/0-msc0/mode
$ echo 64,64 > /sys/bus/intel_th/devices/0-msc0/nr_pages
# enable wrapping for this controller, too::
$ echo 1 > /sys/bus/intel_th/devices/0-msc0/wrap
# and enable tracing into this port::
$ echo 1 > /sys/bus/intel_th/devices/0-msc0/active
# .. send data to master 33, see stm.txt for more details ..
# .. wait for traces to pile up ..
# .. and stop the trace::
$ echo 0 > /sys/bus/intel_th/devices/0-msc0/active
# and now you can collect the trace from the device node::
$ cat /dev/intel_th0/msc0 > my_stp_trace
Host Debugger Mode
------------------
It is possible to configure the Trace Hub and control its trace
capture from a remote debug host, which should be connected via one of
the hardware debugging interfaces, which will then be used to both
control Intel Trace Hub and transfer its trace data to the debug host.
The driver needs to be told that such an arrangement is taking place
so that it does not touch any capture/port configuration and avoids
conflicting with the debug host's configuration accesses. The only
activity that the driver will perform in this mode is collecting
software traces to the Software Trace Hub (an stm class device). The
user is still responsible for setting up adequate master/channel
mappings that the decoder on the receiving end would recognize.
In order to enable the host mode, set the 'host_mode' parameter of the
'intel_th' kernel module to 'y'. None of the virtual output devices
will show up on the intel_th bus. Also, trace configuration and
capture controlling attribute groups of the 'gth' device will not be
exposed. The 'sth' device will operate as usual.
Software Sinks
--------------
The Memory Storage Unit (MSU) driver provides an in-kernel API for
drivers to register themselves as software sinks for the trace data.
Such drivers can further export the data via other devices, such as
USB device controllers or network cards.
The API has two main parts::
- notifying the software sink that a particular window is full, and
"locking" that window, that is, making it unavailable for the trace
collection; when this happens, the MSU driver will automatically
switch to the next window in the buffer if it is unlocked, or stop
the trace capture if it's not;
- tracking the "locked" state of windows and providing a way for the
software sink driver to notify the MSU driver when a window is
unlocked and can be used again to collect trace data.
An example sink driver, msu-sink illustrates the implementation of a
software sink. Functionally, it simply unlocks windows as soon as they
are full, keeping the MSU running in a circular buffer mode. Unlike the
"multi" mode, it will fill out all the windows in the buffer as opposed
to just the first one. It can be enabled by writing "sink" to the "mode"
file (assuming msu-sink.ko is loaded).
+789
View File
@@ -0,0 +1,789 @@
=======================
Kernel Probes (Kprobes)
=======================
:Author: Jim Keniston <jkenisto@us.ibm.com>
:Author: Prasanna S Panchamukhi <prasanna.panchamukhi@gmail.com>
:Author: Masami Hiramatsu <mhiramat@kernel.org>
.. CONTENTS
1. Concepts: Kprobes, and Return Probes
2. Architectures Supported
3. Configuring Kprobes
4. API Reference
5. Kprobes Features and Limitations
6. Probe Overhead
7. TODO
8. Kprobes Example
9. Kretprobes Example
10. Deprecated Features
Appendix A: The kprobes debugfs interface
Appendix B: The kprobes sysctl interface
Appendix C: References
Concepts: Kprobes and Return Probes
=========================================
Kprobes enables you to dynamically break into any kernel routine and
collect debugging and performance information non-disruptively. You
can trap at almost any kernel code address [1]_, specifying a handler
routine to be invoked when the breakpoint is hit.
.. [1] some parts of the kernel code can not be trapped, see
:ref:`kprobes_blacklist`)
There are currently two types of probes: kprobes, and kretprobes
(also called return probes). A kprobe can be inserted on virtually
any instruction in the kernel. A return probe fires when a specified
function returns.
In the typical case, Kprobes-based instrumentation is packaged as
a kernel module. The module's init function installs ("registers")
one or more probes, and the exit function unregisters them. A
registration function such as register_kprobe() specifies where
the probe is to be inserted and what handler is to be called when
the probe is hit.
There are also ``register_/unregister_*probes()`` functions for batch
registration/unregistration of a group of ``*probes``. These functions
can speed up unregistration process when you have to unregister
a lot of probes at once.
The next four subsections explain how the different types of
probes work and how jump optimization works. They explain certain
things that you'll need to know in order to make the best use of
Kprobes -- e.g., the difference between a pre_handler and
a post_handler, and how to use the maxactive and nmissed fields of
a kretprobe. But if you're in a hurry to start using Kprobes, you
can skip ahead to :ref:`kprobes_archs_supported`.
How Does a Kprobe Work?
-----------------------
When a kprobe is registered, Kprobes makes a copy of the probed
instruction and replaces the first byte(s) of the probed instruction
with a breakpoint instruction (e.g., int3 on i386 and x86_64).
When a CPU hits the breakpoint instruction, a trap occurs, the CPU's
registers are saved, and control passes to Kprobes via the
notifier_call_chain mechanism. Kprobes executes the "pre_handler"
associated with the kprobe, passing the handler the addresses of the
kprobe struct and the saved registers.
Next, Kprobes single-steps its copy of the probed instruction.
(It would be simpler to single-step the actual instruction in place,
but then Kprobes would have to temporarily remove the breakpoint
instruction. This would open a small time window when another CPU
could sail right past the probepoint.)
After the instruction is single-stepped, Kprobes executes the
"post_handler," if any, that is associated with the kprobe.
Execution then continues with the instruction following the probepoint.
Changing Execution Path
-----------------------
Since kprobes can probe into a running kernel code, it can change the
register set, including instruction pointer. This operation requires
maximum care, such as keeping the stack frame, recovering the execution
path etc. Since it operates on a running kernel and needs deep knowledge
of computer architecture and concurrent computing, you can easily shoot
your foot.
If you change the instruction pointer (and set up other related
registers) in pre_handler, you must return !0 so that kprobes stops
single stepping and just returns to the given address.
This also means post_handler should not be called anymore.
Note that this operation may be harder on some architectures which use
TOC (Table of Contents) for function call, since you have to setup a new
TOC for your function in your module, and recover the old one after
returning from it.
Return Probes
-------------
How Does a Return Probe Work?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When you call register_kretprobe(), Kprobes establishes a kprobe at
the entry to the function. When the probed function is called and this
probe is hit, Kprobes saves a copy of the return address, and replaces
the return address with the address of a "trampoline." The trampoline
is an arbitrary piece of code -- typically just a nop instruction.
At boot time, Kprobes registers a kprobe at the trampoline.
When the probed function executes its return instruction, control
passes to the trampoline and that probe is hit. Kprobes' trampoline
handler calls the user-specified return handler associated with the
kretprobe, then sets the saved instruction pointer to the saved return
address, and that's where execution resumes upon return from the trap.
While the probed function is executing, its return address is
stored in an object of type kretprobe_instance. Before calling
register_kretprobe(), the user sets the maxactive field of the
kretprobe struct to specify how many instances of the specified
function can be probed simultaneously. register_kretprobe()
pre-allocates the indicated number of kretprobe_instance objects.
For example, if the function is non-recursive and is called with a
spinlock held, maxactive = 1 should be enough. If the function is
non-recursive and can never relinquish the CPU (e.g., via a semaphore
or preemption), NR_CPUS should be enough. If maxactive <= 0, it is
set to a default value: max(10, 2*NR_CPUS).
It's not a disaster if you set maxactive too low; you'll just miss
some probes. In the kretprobe struct, the nmissed field is set to
zero when the return probe is registered, and is incremented every
time the probed function is entered but there is no kretprobe_instance
object available for establishing the return probe.
Kretprobe entry-handler
^^^^^^^^^^^^^^^^^^^^^^^
Kretprobes also provides an optional user-specified handler which runs
on function entry. This handler is specified by setting the entry_handler
field of the kretprobe struct. Whenever the kprobe placed by kretprobe at the
function entry is hit, the user-defined entry_handler, if any, is invoked.
If the entry_handler returns 0 (success) then a corresponding return handler
is guaranteed to be called upon function return. If the entry_handler
returns a non-zero error then Kprobes leaves the return address as is, and
the kretprobe has no further effect for that particular function instance.
Multiple entry and return handler invocations are matched using the unique
kretprobe_instance object associated with them. Additionally, a user
may also specify per return-instance private data to be part of each
kretprobe_instance object. This is especially useful when sharing private
data between corresponding user entry and return handlers. The size of each
private data object can be specified at kretprobe registration time by
setting the data_size field of the kretprobe struct. This data can be
accessed through the data field of each kretprobe_instance object.
In case probed function is entered but there is no kretprobe_instance
object available, then in addition to incrementing the nmissed count,
the user entry_handler invocation is also skipped.
.. _kprobes_jump_optimization:
How Does Jump Optimization Work?
--------------------------------
If your kernel is built with CONFIG_OPTPROBES=y (currently this flag
is automatically set 'y' on x86/x86-64, non-preemptive kernel) and
the "debug.kprobes_optimization" kernel parameter is set to 1 (see
sysctl(8)), Kprobes tries to reduce probe-hit overhead by using a jump
instruction instead of a breakpoint instruction at each probepoint.
Init a Kprobe
^^^^^^^^^^^^^
When a probe is registered, before attempting this optimization,
Kprobes inserts an ordinary, breakpoint-based kprobe at the specified
address. So, even if it's not possible to optimize this particular
probepoint, there'll be a probe there.
Safety Check
^^^^^^^^^^^^
Before optimizing a probe, Kprobes performs the following safety checks:
- Kprobes verifies that the region that will be replaced by the jump
instruction (the "optimized region") lies entirely within one function.
(A jump instruction is multiple bytes, and so may overlay multiple
instructions.)
- Kprobes analyzes the entire function and verifies that there is no
jump into the optimized region. Specifically:
- the function contains no indirect jump;
- the function contains no instruction that causes an exception (since
the fixup code triggered by the exception could jump back into the
optimized region -- Kprobes checks the exception tables to verify this);
- there is no near jump to the optimized region (other than to the first
byte).
- For each instruction in the optimized region, Kprobes verifies that
the instruction can be executed out of line.
Preparing Detour Buffer
^^^^^^^^^^^^^^^^^^^^^^^
Next, Kprobes prepares a "detour" buffer, which contains the following
instruction sequence:
- code to push the CPU's registers (emulating a breakpoint trap)
- a call to the trampoline code which calls user's probe handlers.
- code to restore registers
- the instructions from the optimized region
- a jump back to the original execution path.
Pre-optimization
^^^^^^^^^^^^^^^^
After preparing the detour buffer, Kprobes verifies that none of the
following situations exist:
- The probe has a post_handler.
- Other instructions in the optimized region are probed.
- The probe is disabled.
In any of the above cases, Kprobes won't start optimizing the probe.
Since these are temporary situations, Kprobes tries to start
optimizing it again if the situation is changed.
If the kprobe can be optimized, Kprobes enqueues the kprobe to an
optimizing list, and kicks the kprobe-optimizer workqueue to optimize
it. If the to-be-optimized probepoint is hit before being optimized,
Kprobes returns control to the original instruction path by setting
the CPU's instruction pointer to the copied code in the detour buffer
-- thus at least avoiding the single-step.
Optimization
^^^^^^^^^^^^
The Kprobe-optimizer doesn't insert the jump instruction immediately;
rather, it calls synchronize_rcu() for safety first, because it's
possible for a CPU to be interrupted in the middle of executing the
optimized region [3]_. As you know, synchronize_rcu() can ensure
that all interruptions that were active when synchronize_rcu()
was called are done, but only if CONFIG_PREEMPT=n. So, this version
of kprobe optimization supports only kernels with CONFIG_PREEMPT=n [4]_.
After that, the Kprobe-optimizer calls stop_machine() to replace
the optimized region with a jump instruction to the detour buffer,
using text_poke_smp().
Unoptimization
^^^^^^^^^^^^^^
When an optimized kprobe is unregistered, disabled, or blocked by
another kprobe, it will be unoptimized. If this happens before
the optimization is complete, the kprobe is just dequeued from the
optimized list. If the optimization has been done, the jump is
replaced with the original code (except for an int3 breakpoint in
the first byte) by using text_poke_smp().
.. [3] Please imagine that the 2nd instruction is interrupted and then
the optimizer replaces the 2nd instruction with the jump *address*
while the interrupt handler is running. When the interrupt
returns to original address, there is no valid instruction,
and it causes an unexpected result.
.. [4] This optimization-safety checking may be replaced with the
stop-machine method that ksplice uses for supporting a CONFIG_PREEMPT=y
kernel.
NOTE for geeks:
The jump optimization changes the kprobe's pre_handler behavior.
Without optimization, the pre_handler can change the kernel's execution
path by changing regs->ip and returning 1. However, when the probe
is optimized, that modification is ignored. Thus, if you want to
tweak the kernel's execution path, you need to suppress optimization,
using one of the following techniques:
- Specify an empty function for the kprobe's post_handler.
or
- Execute 'sysctl -w debug.kprobes_optimization=n'
.. _kprobes_blacklist:
Blacklist
---------
Kprobes can probe most of the kernel except itself. This means
that there are some functions where kprobes cannot probe. Probing
(trapping) such functions can cause a recursive trap (e.g. double
fault) or the nested probe handler may never be called.
Kprobes manages such functions as a blacklist.
If you want to add a function into the blacklist, you just need
to (1) include linux/kprobes.h and (2) use NOKPROBE_SYMBOL() macro
to specify a blacklisted function.
Kprobes checks the given probe address against the blacklist and
rejects registering it, if the given address is in the blacklist.
.. _kprobes_archs_supported:
Architectures Supported
=======================
Kprobes and return probes are implemented on the following
architectures:
- i386 (Supports jump optimization)
- x86_64 (AMD-64, EM64T) (Supports jump optimization)
- ppc64
- sparc64 (Return probes not yet implemented.)
- arm
- ppc
- mips
- s390
- parisc
- loongarch
- riscv
Configuring Kprobes
===================
When configuring the kernel using make menuconfig/xconfig/oldconfig,
ensure that CONFIG_KPROBES is set to "y", look for "Kprobes" under
"General architecture-dependent options".
So that you can load and unload Kprobes-based instrumentation modules,
make sure "Loadable module support" (CONFIG_MODULES) and "Module
unloading" (CONFIG_MODULE_UNLOAD) are set to "y".
Also make sure that CONFIG_KALLSYMS and perhaps even CONFIG_KALLSYMS_ALL
are set to "y", since kallsyms_lookup_name() is used by the in-kernel
kprobe address resolution code.
If you need to insert a probe in the middle of a function, you may find
it useful to "Compile the kernel with debug info" (CONFIG_DEBUG_INFO),
so you can use "objdump -d -l vmlinux" to see the source-to-object
code mapping.
API Reference
=============
The Kprobes API includes a "register" function and an "unregister"
function for each type of probe. The API also includes "register_*probes"
and "unregister_*probes" functions for (un)registering arrays of probes.
Here are terse, mini-man-page specifications for these functions and
the associated probe handlers that you'll write. See the files in the
samples/kprobes/ sub-directory for examples.
register_kprobe
---------------
::
#include <linux/kprobes.h>
int register_kprobe(struct kprobe *kp);
Sets a breakpoint at the address kp->addr. When the breakpoint is hit, Kprobes
calls kp->pre_handler. After the probed instruction is single-stepped, Kprobe
calls kp->post_handler. Any or all handlers can be NULL. If kp->flags is set
KPROBE_FLAG_DISABLED, that kp will be registered but disabled, so, its handlers
aren't hit until calling enable_kprobe(kp).
.. note::
1. With the introduction of the "symbol_name" field to struct kprobe,
the probepoint address resolution will now be taken care of by the kernel.
The following will now work::
kp.symbol_name = "symbol_name";
(64-bit powerpc intricacies such as function descriptors are handled
transparently)
2. Use the "offset" field of struct kprobe if the offset into the symbol
to install a probepoint is known. This field is used to calculate the
probepoint.
3. Specify either the kprobe "symbol_name" OR the "addr". If both are
specified, kprobe registration will fail with -EINVAL.
4. With CISC architectures (such as i386 and x86_64), the kprobes code
does not validate if the kprobe.addr is at an instruction boundary.
Use "offset" with caution.
register_kprobe() returns 0 on success, or a negative errno otherwise.
User's pre-handler (kp->pre_handler)::
#include <linux/kprobes.h>
#include <linux/ptrace.h>
int pre_handler(struct kprobe *p, struct pt_regs *regs);
Called with p pointing to the kprobe associated with the breakpoint,
and regs pointing to the struct containing the registers saved when
the breakpoint was hit. Return 0 here unless you're a Kprobes geek.
User's post-handler (kp->post_handler)::
#include <linux/kprobes.h>
#include <linux/ptrace.h>
void post_handler(struct kprobe *p, struct pt_regs *regs,
unsigned long flags);
p and regs are as described for the pre_handler. flags always seems
to be zero.
register_kretprobe
------------------
::
#include <linux/kprobes.h>
int register_kretprobe(struct kretprobe *rp);
Establishes a return probe for the function whose address is
rp->kp.addr. When that function returns, Kprobes calls rp->handler.
You must set rp->maxactive appropriately before you call
register_kretprobe(); see "How Does a Return Probe Work?" for details.
register_kretprobe() returns 0 on success, or a negative errno
otherwise.
User's return-probe handler (rp->handler)::
#include <linux/kprobes.h>
#include <linux/ptrace.h>
int kretprobe_handler(struct kretprobe_instance *ri,
struct pt_regs *regs);
regs is as described for kprobe.pre_handler. ri points to the
kretprobe_instance object, of which the following fields may be
of interest:
- ret_addr: the return address
- rp: points to the corresponding kretprobe object
- task: points to the corresponding task struct
- data: points to per return-instance private data; see "Kretprobe
entry-handler" for details.
The regs_return_value(regs) macro provides a simple abstraction to
extract the return value from the appropriate register as defined by
the architecture's ABI.
The handler's return value is currently ignored.
unregister_*probe
------------------
::
#include <linux/kprobes.h>
void unregister_kprobe(struct kprobe *kp);
void unregister_kretprobe(struct kretprobe *rp);
Removes the specified probe. The unregister function can be called
at any time after the probe has been registered.
.. note::
If the functions find an incorrect probe (ex. an unregistered probe),
they clear the addr field of the probe.
register_*probes
----------------
::
#include <linux/kprobes.h>
int register_kprobes(struct kprobe **kps, int num);
int register_kretprobes(struct kretprobe **rps, int num);
Registers each of the num probes in the specified array. If any
error occurs during registration, all probes in the array, up to
the bad probe, are safely unregistered before the register_*probes
function returns.
- kps/rps: an array of pointers to ``*probe`` data structures
- num: the number of the array entries.
.. note::
You have to allocate(or define) an array of pointers and set all
of the array entries before using these functions.
unregister_*probes
------------------
::
#include <linux/kprobes.h>
void unregister_kprobes(struct kprobe **kps, int num);
void unregister_kretprobes(struct kretprobe **rps, int num);
Removes each of the num probes in the specified array at once.
.. note::
If the functions find some incorrect probes (ex. unregistered
probes) in the specified array, they clear the addr field of those
incorrect probes. However, other probes in the array are
unregistered correctly.
disable_*probe
--------------
::
#include <linux/kprobes.h>
int disable_kprobe(struct kprobe *kp);
int disable_kretprobe(struct kretprobe *rp);
Temporarily disables the specified ``*probe``. You can enable it again by using
enable_*probe(). You must specify the probe which has been registered.
enable_*probe
-------------
::
#include <linux/kprobes.h>
int enable_kprobe(struct kprobe *kp);
int enable_kretprobe(struct kretprobe *rp);
Enables ``*probe`` which has been disabled by disable_*probe(). You must specify
the probe which has been registered.
Kprobes Features and Limitations
================================
Kprobes allows multiple probes at the same address. Also,
a probepoint for which there is a post_handler cannot be optimized.
So if you install a kprobe with a post_handler, at an optimized
probepoint, the probepoint will be unoptimized automatically.
In general, you can install a probe anywhere in the kernel.
In particular, you can probe interrupt handlers. Known exceptions
are discussed in this section.
The register_*probe functions will return -EINVAL if you attempt
to install a probe in the code that implements Kprobes (mostly
kernel/kprobes.c and ``arch/*/kernel/kprobes.c``, but also functions such
as do_page_fault and notifier_call_chain).
If you install a probe in an inline-able function, Kprobes makes
no attempt to chase down all inline instances of the function and
install probes there. gcc may inline a function without being asked,
so keep this in mind if you're not seeing the probe hits you expect.
A probe handler can modify the environment of the probed function
-- e.g., by modifying kernel data structures, or by modifying the
contents of the pt_regs struct (which are restored to the registers
upon return from the breakpoint). So Kprobes can be used, for example,
to install a bug fix or to inject faults for testing. Kprobes, of
course, has no way to distinguish the deliberately injected faults
from the accidental ones. Don't drink and probe.
Kprobes makes no attempt to prevent probe handlers from stepping on
each other -- e.g., probing printk() and then calling printk() from a
probe handler. If a probe handler hits a probe, that second probe's
handlers won't be run in that instance, and the kprobe.nmissed member
of the second probe will be incremented.
As of Linux v2.6.15-rc1, multiple handlers (or multiple instances of
the same handler) may run concurrently on different CPUs.
Kprobes does not use mutexes or allocate memory except during
registration and unregistration.
Probe handlers are run with preemption disabled or interrupt disabled,
which depends on the architecture and optimization state. (e.g.,
kretprobe handlers and optimized kprobe handlers run without interrupt
disabled on x86/x86-64). In any case, your handler should not yield
the CPU (e.g., by attempting to acquire a semaphore, or waiting I/O).
Since a return probe is implemented by replacing the return
address with the trampoline's address, stack backtraces and calls
to __builtin_return_address() will typically yield the trampoline's
address instead of the real return address for kretprobed functions.
(As far as we can tell, __builtin_return_address() is used only
for instrumentation and error reporting.)
If the number of times a function is called does not match the number
of times it returns, registering a return probe on that function may
produce undesirable results. In such a case, a line:
kretprobe BUG!: Processing kretprobe d000000000041aa8 @ c00000000004f48c
gets printed. With this information, one will be able to correlate the
exact instance of the kretprobe that caused the problem. We have the
do_exit() case covered. do_execve() and do_fork() are not an issue.
We're unaware of other specific cases where this could be a problem.
If, upon entry to or exit from a function, the CPU is running on
a stack other than that of the current task, registering a return
probe on that function may produce undesirable results. For this
reason, Kprobes doesn't support return probes (or kprobes)
on the x86_64 version of __switch_to(); the registration functions
return -EINVAL.
On x86/x86-64, since the Jump Optimization of Kprobes modifies
instructions widely, there are some limitations to optimization. To
explain it, we introduce some terminology. Imagine a 3-instruction
sequence consisting of a two 2-byte instructions and one 3-byte
instruction.
::
IA
|
[-2][-1][0][1][2][3][4][5][6][7]
[ins1][ins2][ ins3 ]
[<- DCR ->]
[<- JTPR ->]
ins1: 1st Instruction
ins2: 2nd Instruction
ins3: 3rd Instruction
IA: Insertion Address
JTPR: Jump Target Prohibition Region
DCR: Detoured Code Region
The instructions in DCR are copied to the out-of-line buffer
of the kprobe, because the bytes in DCR are replaced by
a 5-byte jump instruction. So there are several limitations.
a) The instructions in DCR must be relocatable.
b) The instructions in DCR must not include a call instruction.
c) JTPR must not be targeted by any jump or call instruction.
d) DCR must not straddle the border between functions.
Anyway, these limitations are checked by the in-kernel instruction
decoder, so you don't need to worry about that.
Probe Overhead
==============
On a typical CPU in use in 2005, a kprobe hit takes 0.5 to 1.0
microseconds to process. Specifically, a benchmark that hits the same
probepoint repeatedly, firing a simple handler each time, reports 1-2
million hits per second, depending on the architecture. A return-probe
hit typically takes 50-75% longer than a kprobe hit.
When you have a return probe set on a function, adding a kprobe at
the entry to that function adds essentially no overhead.
Here are sample overhead figures (in usec) for different architectures::
k = kprobe; r = return probe; kr = kprobe + return probe
on same function
i386: Intel Pentium M, 1495 MHz, 2957.31 bogomips
k = 0.57 usec; r = 0.92; kr = 0.99
x86_64: AMD Opteron 246, 1994 MHz, 3971.48 bogomips
k = 0.49 usec; r = 0.80; kr = 0.82
ppc64: POWER5 (gr), 1656 MHz (SMT disabled, 1 virtual CPU per physical CPU)
k = 0.77 usec; r = 1.26; kr = 1.45
Optimized Probe Overhead
------------------------
Typically, an optimized kprobe hit takes 0.07 to 0.1 microseconds to
process. Here are sample overhead figures (in usec) for x86 architectures::
k = unoptimized kprobe, b = boosted (single-step skipped), o = optimized kprobe,
r = unoptimized kretprobe, rb = boosted kretprobe, ro = optimized kretprobe.
i386: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
k = 0.80 usec; b = 0.33; o = 0.05; r = 1.10; rb = 0.61; ro = 0.33
x86-64: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
k = 0.99 usec; b = 0.43; o = 0.06; r = 1.24; rb = 0.68; ro = 0.30
TODO
====
a. SystemTap (http://sourceware.org/systemtap): Provides a simplified
programming interface for probe-based instrumentation. Try it out.
b. Kernel return probes for sparc64.
c. Support for other architectures.
d. User-space probes.
e. Watchpoint probes (which fire on data references).
Kprobes Example
===============
See samples/kprobes/kprobe_example.c
Kretprobes Example
==================
See samples/kprobes/kretprobe_example.c
Deprecated Features
===================
Jprobes is now a deprecated feature. People who are depending on it should
migrate to other tracing features or use older kernels. Please consider to
migrate your tool to one of the following options:
- Use trace-event to trace target function with arguments.
trace-event is a low-overhead (and almost no visible overhead if it
is off) statically defined event interface. You can define new events
and trace it via ftrace or any other tracing tools.
See the following urls:
- https://lwn.net/Articles/379903/
- https://lwn.net/Articles/381064/
- https://lwn.net/Articles/383362/
- Use ftrace dynamic events (kprobe event) with perf-probe.
If you build your kernel with debug info (CONFIG_DEBUG_INFO=y), you can
find which register/stack is assigned to which local variable or arguments
by using perf-probe and set up new event to trace it.
See following documents:
- Documentation/trace/kprobetrace.rst
- Documentation/trace/events.rst
- tools/perf/Documentation/perf-probe.txt
The kprobes debugfs interface
=============================
With recent kernels (> 2.6.20) the list of registered kprobes is visible
under the /sys/kernel/debug/kprobes/ directory (assuming debugfs is mounted at //sys/kernel/debug).
/sys/kernel/debug/kprobes/list: Lists all registered probes on the system::
c015d71a k vfs_read+0x0
c03dedc5 r tcp_v4_rcv+0x0
The first column provides the kernel address where the probe is inserted.
The second column identifies the type of probe (k - kprobe and r - kretprobe)
while the third column specifies the symbol+offset of the probe.
If the probed function belongs to a module, the module name is also
specified. Following columns show probe status. If the probe is on
a virtual address that is no longer valid (module init sections, module
virtual addresses that correspond to modules that've been unloaded),
such probes are marked with [GONE]. If the probe is temporarily disabled,
such probes are marked with [DISABLED]. If the probe is optimized, it is
marked with [OPTIMIZED]. If the probe is ftrace-based, it is marked with
[FTRACE].
/sys/kernel/debug/kprobes/enabled: Turn kprobes ON/OFF forcibly.
Provides a knob to globally and forcibly turn registered kprobes ON or OFF.
By default, all kprobes are enabled. By echoing "0" to this file, all
registered probes will be disarmed, till such time a "1" is echoed to this
file. Note that this knob just disarms and arms all kprobes and doesn't
change each probe's disabling state. This means that disabled kprobes (marked
[DISABLED]) will be not enabled if you turn ON all kprobes by this knob.
The kprobes sysctl interface
============================
/proc/sys/debug/kprobes-optimization: Turn kprobes optimization ON/OFF.
When CONFIG_OPTPROBES=y, this sysctl interface appears and it provides
a knob to globally and forcibly turn jump optimization (see section
:ref:`kprobes_jump_optimization`) ON or OFF. By default, jump optimization
is allowed (ON). If you echo "0" to this file or set
"debug.kprobes_optimization" to 0 via sysctl, all optimized probes will be
unoptimized, and any new probes registered after that will not be optimized.
Note that this knob *changes* the optimized state. This means that optimized
probes (marked [OPTIMIZED]) will be unoptimized ([OPTIMIZED] tag will be
removed). If the knob is turned on, they will be optimized again.
References
==========
For additional information on Kprobes, refer to the following URLs:
- https://lwn.net/Articles/132196/
- https://www.kernel.org/doc/ols/2006/ols2006v2-pages-109-124.pdf
@@ -0,0 +1,285 @@
==========================
Kprobe-based Event Tracing
==========================
:Author: Masami Hiramatsu
Overview
--------
These events are similar to tracepoint-based events. Instead of tracepoints,
this is based on kprobes (kprobe and kretprobe). So it can probe wherever
kprobes can probe (this means, all functions except those with
__kprobes/nokprobe_inline annotation and those marked NOKPROBE_SYMBOL).
Unlike the tracepoint-based event, this can be added and removed
dynamically, on the fly.
To enable this feature, build your kernel with CONFIG_KPROBE_EVENTS=y.
Similar to the event tracer, this doesn't need to be activated via
current_tracer. Instead of that, add probe points via
/sys/kernel/tracing/kprobe_events, and enable it via
/sys/kernel/tracing/events/kprobes/<EVENT>/enable.
You can also use /sys/kernel/tracing/dynamic_events instead of
kprobe_events. That interface will provide unified access to other
dynamic events too.
Synopsis of kprobe_events
-------------------------
::
p[:[GRP/][EVENT]] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
r[MAXACTIVE][:[GRP/][EVENT]] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
p[:[GRP/][EVENT]] [MOD:]SYM[+0]%return [FETCHARGS] : Set a return probe
-:[GRP/][EVENT] : Clear a probe
GRP : Group name. If omitted, use "kprobes" for it.
EVENT : Event name. If omitted, the event name is generated
based on SYM+offs or MEMADDR.
MOD : Module name which has given SYM.
SYM[+offs] : Symbol+offset where the probe is inserted.
SYM%return : Return address of the symbol
MEMADDR : Address where the probe is inserted.
MAXACTIVE : Maximum number of instances of the specified function that
can be probed simultaneously, or 0 for the default value
as defined in Documentation/trace/kprobes.rst section 1.3.1.
FETCHARGS : Arguments. Each probe can have up to 128 args.
%REG : Fetch register REG
@ADDR : Fetch memory at ADDR (ADDR should be in kernel)
@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
$stackN : Fetch Nth entry of stack (N >= 0)
$stack : Fetch stack address.
$argN : Fetch the Nth function argument. (N >= 1) (\*1)
$retval : Fetch return value.(\*2)
$comm : Fetch current task comm.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
(x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
"string", "ustring", "symbol", "symstr" and bitfield are
supported.
(\*1) only for the probe on function entry (offs == 0). Note, this argument access
is best effort, because depending on the argument type, it may be passed on
the stack. But this only support the arguments via registers.
(\*2) only for return probe. Note that this is also best effort. Depending on the
return value type, it might be passed via a pair of registers. But this only
accesses one register.
(\*3) this is useful for fetching a field of data structures.
(\*4) "u" means user-space dereference. See :ref:`user_mem_access`.
Function arguments at kretprobe
-------------------------------
Function arguments can be accessed at kretprobe using $arg<N> fetcharg. This
is useful to record the function parameter and return value at once, and
trace the difference of structure fields (for debugging a function whether it
correctly updates the given data structure or not).
See the :ref:`sample<fprobetrace_exit_args_sample>` in fprobe event for how
it works.
.. _kprobetrace_types:
Types
-----
Several types are supported for fetchargs. Kprobe tracer will access memory
by given type. Prefix 's' and 'u' means those types are signed and unsigned
respectively. 'x' prefix implies it is unsigned. Traced arguments are shown
in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32'
or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and
x86-64 uses x64).
These value types can be an array. To record array data, you can add '[N]'
(where N is a fixed number, less than 64) to the base type.
E.g. 'x16[4]' means an array of x16 (2-byte hex) with 4 elements.
Note that the array can be applied to memory type fetchargs, you can not
apply it to registers/stack-entries etc. (for example, '$stack1:x8[8]' is
wrong, but '+8($stack):x8[8]' is OK.)
Char type can be used to show the character value of traced arguments.
String type is a special type, which fetches a "null-terminated" string from
kernel space. This means it will fail and store NULL if the string container
has been paged out. "ustring" type is an alternative of string for user-space.
See :ref:`user_mem_access` for more info.
The string array type is a bit different from other types. For other base
types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same
as +0(%di):x32.) But string[1] is not equal to string. The string type itself
represents "char array", but string array type represents "char * array".
So, for example, +0(%di):string[1] is equal to +0(+0(%di)):string.
Bitfield is another special type, which takes 3 parameters, bit-width, bit-
offset, and container-size (usually 32). The syntax is::
b<bit-width>@<bit-offset>/<container-size>
Symbol type('symbol') is an alias of u32 or u64 type (depends on BITS_PER_LONG)
which shows given pointer in "symbol+offset" style.
On the other hand, symbol-string type ('symstr') converts the given address to
"symbol+offset/symbolsize" style and stores it as a null-terminated string.
With 'symstr' type, you can filter the event with wildcard pattern of the
symbols, and you don't need to solve symbol name by yourself.
For $comm, the default type is "string"; any other type is invalid.
VFS layer common type(%pd/%pD) is a special type, which fetches dentry's or
file's name from struct dentry's address or struct file's address.
.. _user_mem_access:
User Memory Access
------------------
Kprobe events supports user-space memory access. For that purpose, you can use
either user-space dereference syntax or 'ustring' type.
The user-space dereference syntax allows you to access a field of a data
structure in user-space. This is done by adding the "u" prefix to the
dereference syntax. For example, +u4(%si) means it will read memory from the
address in the register %si offset by 4, and the memory is expected to be in
user-space. You can use this for strings too, e.g. +u0(%si):string will read
a string from the address in the register %si that is expected to be in user-
space. 'ustring' is a shortcut way of performing the same task. That is,
+0(%si):ustring is equivalent to +u0(%si):string.
Note that kprobe-event provides the user-memory access syntax but it doesn't
use it transparently. This means if you use normal dereference or string type
for user memory, it might fail, and may always fail on some architectures. The
user has to carefully check if the target data is in kernel or user space.
Per-Probe Event Filtering
-------------------------
Per-probe event filtering feature allows you to set different filter on each
probe and gives you what arguments will be shown in trace buffer. If an event
name is specified right after 'p:' or 'r:' in kprobe_events, it adds an event
under tracing/events/kprobes/<EVENT>, at the directory you can see 'id',
'enable', 'format', 'filter' and 'trigger'.
enable:
You can enable/disable the probe by writing 1 or 0 on it.
format:
This shows the format of this probe event.
filter:
You can write filtering rules of this event.
id:
This shows the id of this probe event.
trigger:
This allows to install trigger commands which are executed when the event is
hit (for details, see Documentation/trace/events.rst, section 6).
Event Profiling
---------------
You can check the total number of probe hits and probe miss-hits via
/sys/kernel/tracing/kprobe_profile.
The first column is event name, the second is the number of probe hits,
the third is the number of probe miss-hits.
Kernel Boot Parameter
---------------------
You can add and enable new kprobe events when booting up the kernel by
"kprobe_event=" parameter. The parameter accepts a semicolon-delimited
kprobe events, which format is similar to the kprobe_events.
The difference is that the probe definition parameters are comma-delimited
instead of space. For example, adding myprobe event on do_sys_open like below::
p:myprobe do_sys_open dfd=%ax filename=%dx flags=%cx mode=+4($stack)
should be below for kernel boot parameter (just replace spaces with comma)::
p:myprobe,do_sys_open,dfd=%ax,filename=%dx,flags=%cx,mode=+4($stack)
Usage examples
--------------
To add a probe as a new event, write a new definition to kprobe_events
as below::
echo 'p:myprobe do_sys_open dfd=%ax filename=%dx flags=%cx mode=+4($stack)' > /sys/kernel/tracing/kprobe_events
This sets a kprobe on the top of do_sys_open() function with recording
1st to 4th arguments as "myprobe" event. Note, which register/stack entry is
assigned to each function argument depends on arch-specific ABI. If you unsure
the ABI, please try to use probe subcommand of perf-tools (you can find it
under tools/perf/).
As this example shows, users can choose more familiar names for each arguments.
::
echo 'r:myretprobe do_sys_open $retval' >> /sys/kernel/tracing/kprobe_events
This sets a kretprobe on the return point of do_sys_open() function with
recording return value as "myretprobe" event.
You can see the format of these events via
/sys/kernel/tracing/events/kprobes/<EVENT>/format.
::
cat /sys/kernel/tracing/events/kprobes/myprobe/format
name: myprobe
ID: 780
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1;signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:12; size:4; signed:0;
field:int __probe_nargs; offset:16; size:4; signed:1;
field:unsigned long dfd; offset:20; size:4; signed:0;
field:unsigned long filename; offset:24; size:4; signed:0;
field:unsigned long flags; offset:28; size:4; signed:0;
field:unsigned long mode; offset:32; size:4; signed:0;
print fmt: "(%lx) dfd=%lx filename=%lx flags=%lx mode=%lx", REC->__probe_ip,
REC->dfd, REC->filename, REC->flags, REC->mode
You can see that the event has 4 arguments as in the expressions you specified.
::
echo > /sys/kernel/tracing/kprobe_events
This clears all probe points.
Or,
::
echo -:myprobe >> kprobe_events
This clears probe points selectively.
Right after definition, each event is disabled by default. For tracing these
events, you need to enable it.
::
echo 1 > /sys/kernel/tracing/events/kprobes/myprobe/enable
echo 1 > /sys/kernel/tracing/events/kprobes/myretprobe/enable
Use the following command to start tracing in an interval.
::
# echo 1 > tracing_on
Open something...
# echo 0 > tracing_on
And you can see the traced information via /sys/kernel/tracing/trace.
::
cat /sys/kernel/tracing/trace
# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
<...>-1447 [001] 1038282.286875: myprobe: (do_sys_open+0x0/0xd6) dfd=3 filename=7fffd1ec4440 flags=8000 mode=0
<...>-1447 [001] 1038282.286878: myretprobe: (sys_openat+0xc/0xe <- do_sys_open) $retval=fffffffffffffffe
<...>-1447 [001] 1038282.286885: myprobe: (do_sys_open+0x0/0xd6) dfd=ffffff9c filename=40413c flags=8000 mode=1b6
<...>-1447 [001] 1038282.286915: myretprobe: (sys_open+0x1b/0x1d <- do_sys_open) $retval=3
<...>-1447 [001] 1038282.286969: myprobe: (do_sys_open+0x0/0xd6) dfd=ffffff9c filename=4041c6 flags=98800 mode=10
<...>-1447 [001] 1038282.286976: myretprobe: (sys_open+0x1b/0x1d <- do_sys_open) $retval=3
Each line shows when the kernel hits an event, and <- SYMBOL means kernel
returns from SYMBOL(e.g. "sys_open+0x1b/0x1d <- do_sys_open" means kernel
returns from do_sys_open to sys_open+0x1b).
@@ -0,0 +1,184 @@
===================================
In-kernel memory-mapped I/O tracing
===================================
Home page and links to optional user space tools:
https://nouveau.freedesktop.org/wiki/MmioTrace
MMIO tracing was originally developed by Intel around 2003 for their Fault
Injection Test Harness. In Dec 2006 - Jan 2007, using the code from Intel,
Jeff Muizelaar created a tool for tracing MMIO accesses with the Nouveau
project in mind. Since then many people have contributed.
Mmiotrace was built for reverse engineering any memory-mapped IO device with
the Nouveau project as the first real user. Only x86 and x86_64 architectures
are supported.
Out-of-tree mmiotrace was originally modified for mainline inclusion and
ftrace framework by Pekka Paalanen <pq@iki.fi>.
Preparation
-----------
Mmiotrace feature is compiled in by the CONFIG_MMIOTRACE option. Tracing is
disabled by default, so it is safe to have this set to yes. SMP systems are
supported, but tracing is unreliable and may miss events if more than one CPU
is on-line, therefore mmiotrace takes all but one CPU off-line during run-time
activation. You can re-enable CPUs by hand, but you have been warned, there
is no way to automatically detect if you are losing events due to CPUs racing.
Usage Quick Reference
---------------------
::
$ mount -t debugfs debugfs /sys/kernel/debug
$ echo mmiotrace > /sys/kernel/tracing/current_tracer
$ cat /sys/kernel/tracing/trace_pipe > mydump.txt &
Start X or whatever.
$ echo "X is up" > /sys/kernel/tracing/trace_marker
$ echo nop > /sys/kernel/tracing/current_tracer
Check for lost events.
Usage
-----
Make sure debugfs is mounted to /sys/kernel/debug.
If not (requires root privileges)::
$ mount -t debugfs debugfs /sys/kernel/debug
Check that the driver you are about to trace is not loaded.
Activate mmiotrace (requires root privileges)::
$ echo mmiotrace > /sys/kernel/tracing/current_tracer
Start storing the trace::
$ cat /sys/kernel/tracing/trace_pipe > mydump.txt &
The 'cat' process should stay running (sleeping) in the background.
Load the driver you want to trace and use it. Mmiotrace will only catch MMIO
accesses to areas that are ioremapped while mmiotrace is active.
During tracing you can place comments (markers) into the trace by
$ echo "X is up" > /sys/kernel/tracing/trace_marker
This makes it easier to see which part of the (huge) trace corresponds to
which action. It is recommended to place descriptive markers about what you
do.
Shut down mmiotrace (requires root privileges)::
$ echo nop > /sys/kernel/tracing/current_tracer
The 'cat' process exits. If it does not, kill it by issuing 'fg' command and
pressing ctrl+c.
Check that mmiotrace did not lose events due to a buffer filling up. Either::
$ grep -i lost mydump.txt
which tells you exactly how many events were lost, or use::
$ dmesg
to view your kernel log and look for "mmiotrace has lost events" warning. If
events were lost, the trace is incomplete. You should enlarge the buffers and
try again. Buffers are enlarged by first seeing how large the current buffers
are::
$ cat /sys/kernel/tracing/buffer_size_kb
gives you a number. Approximately double this number and write it back, for
instance::
$ echo 128000 > /sys/kernel/tracing/buffer_size_kb
Then start again from the top.
If you are doing a trace for a driver project, e.g. Nouveau, you should also
do the following before sending your results::
$ lspci -vvv > lspci.txt
$ dmesg > dmesg.txt
$ tar zcf pciid-nick-mmiotrace.tar.gz mydump.txt lspci.txt dmesg.txt
and then send the .tar.gz file. The trace compresses considerably. Replace
"pciid" and "nick" with the PCI ID or model name of your piece of hardware
under investigation and your nickname.
How Mmiotrace Works
-------------------
Access to hardware IO-memory is gained by mapping addresses from PCI bus by
calling one of the ioremap_*() functions. Mmiotrace is hooked into the
__ioremap() function and gets called whenever a mapping is created. Mapping is
an event that is recorded into the trace log. Note that ISA range mappings
are not caught, since the mapping always exists and is returned directly.
MMIO accesses are recorded via page faults. Just before __ioremap() returns,
the mapped pages are marked as not present. Any access to the pages causes a
fault. The page fault handler calls mmiotrace to handle the fault. Mmiotrace
marks the page present, sets TF flag to achieve single stepping and exits the
fault handler. The instruction that faulted is executed and debug trap is
entered. Here mmiotrace again marks the page as not present. The instruction
is decoded to get the type of operation (read/write), data width and the value
read or written. These are stored to the trace log.
Setting the page present in the page fault handler has a race condition on SMP
machines. During the single stepping other CPUs may run freely on that page
and events can be missed without a notice. Re-enabling other CPUs during
tracing is discouraged.
Trace Log Format
----------------
The raw log is text and easily filtered with e.g. grep and awk. One record is
one line in the log. A record starts with a keyword, followed by keyword-
dependent arguments. Arguments are separated by a space, or continue until the
end of line. The format for version 20070824 is as follows:
Explanation Keyword Space-separated arguments
---------------------------------------------------------------------------
read event R width, timestamp, map id, physical, value, PC, PID
write event W width, timestamp, map id, physical, value, PC, PID
ioremap event MAP timestamp, map id, physical, virtual, length, PC, PID
iounmap event UNMAP timestamp, map id, PC, PID
marker MARK timestamp, text
version VERSION the string "20070824"
info for reader LSPCI one line from lspci -v
PCI address map PCIDEV space-separated /proc/bus/pci/devices data
unk. opcode UNKNOWN timestamp, map id, physical, data, PC, PID
Timestamp is in seconds with decimals. Physical is a PCI bus address, virtual
is a kernel virtual address. Width is the data width in bytes and value is the
data value. Map id is an arbitrary id number identifying the mapping that was
used in an operation. PC is the program counter and PID is process id. PC is
zero if it is not recorded. PID is always zero as tracing MMIO accesses
originating in user space memory is not yet supported.
For instance, the following awk filter will pass all 32-bit writes that target
physical addresses in the range [0xfb73ce40, 0xfb800000]
::
$ awk '/W 4 / { adr=strtonum($5); if (adr >= 0xfb73ce40 &&
adr < 0xfb800000) print; }'
Tools for Developers
--------------------
The user space tools include utilities for:
- replacing numeric addresses and values with hardware register names
- replaying MMIO logs, i.e., re-executing the recorded writes
@@ -0,0 +1,180 @@
==============
OSNOISE Tracer
==============
In the context of high-performance computing (HPC), the Operating System
Noise (*osnoise*) refers to the interference experienced by an application
due to activities inside the operating system. In the context of Linux,
NMIs, IRQs, SoftIRQs, and any other system thread can cause noise to the
system. Moreover, hardware-related jobs can also cause noise, for example,
via SMIs.
hwlat_detector is one of the tools used to identify the most complex
source of noise: *hardware noise*.
In a nutshell, the hwlat_detector creates a thread that runs
periodically for a given period. At the beginning of a period, the thread
disables interrupt and starts sampling. While running, the hwlatd
thread reads the time in a loop. As interrupts are disabled, threads,
IRQs, and SoftIRQs cannot interfere with the hwlatd thread. Hence, the
cause of any gap between two different reads of the time roots either on
NMI or in the hardware itself. At the end of the period, hwlatd enables
interrupts and reports the max observed gap between the reads. It also
prints a NMI occurrence counter. If the output does not report NMI
executions, the user can conclude that the hardware is the culprit for
the latency. The hwlat detects the NMI execution by observing
the entry and exit of a NMI.
The osnoise tracer leverages the hwlat_detector by running a
similar loop with preemption, SoftIRQs and IRQs enabled, thus allowing
all the sources of *osnoise* during its execution. Using the same approach
of hwlat, osnoise takes note of the entry and exit point of any
source of interferences, increasing a per-cpu interference counter. The
osnoise tracer also saves an interference counter for each source of
interference. The interference counter for NMI, IRQs, SoftIRQs, and
threads is increased anytime the tool observes these interferences' entry
events. When a noise happens without any interference from the operating
system level, the hardware noise counter increases, pointing to a
hardware-related noise. In this way, osnoise can account for any
source of interference. At the end of the period, the osnoise tracer
prints the sum of all noise, the max single noise, the percentage of CPU
available for the thread, and the counters for the noise sources.
Usage
-----
Write the ASCII text "osnoise" into the current_tracer file of the
tracing system (generally mounted at /sys/kernel/tracing).
For example::
[root@f32 ~]# cd /sys/kernel/tracing/
[root@f32 tracing]# echo osnoise > current_tracer
It is possible to follow the trace by reading the trace file::
[root@f32 tracing]# cat trace
# tracer: osnoise
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth MAX
# || / SINGLE Interference counters:
# |||| RUNTIME NOISE % OF CPU NOISE +-----------------------------+
# TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD
# | | | |||| | | | | | | | | | |
<...>-859 [000] .... 81.637220: 1000000 190 99.98100 9 18 0 1007 18 1
<...>-860 [001] .... 81.638154: 1000000 656 99.93440 74 23 0 1006 16 3
<...>-861 [002] .... 81.638193: 1000000 5675 99.43250 202 6 0 1013 25 21
<...>-862 [003] .... 81.638242: 1000000 125 99.98750 45 1 0 1011 23 0
<...>-863 [004] .... 81.638260: 1000000 1721 99.82790 168 7 0 1002 49 41
<...>-864 [005] .... 81.638286: 1000000 263 99.97370 57 6 0 1006 26 2
<...>-865 [006] .... 81.638302: 1000000 109 99.98910 21 3 0 1006 18 1
<...>-866 [007] .... 81.638326: 1000000 7816 99.21840 107 8 0 1016 39 19
In addition to the regular trace fields (from TASK-PID to TIMESTAMP), the
tracer prints a message at the end of each period for each CPU that is
running an osnoise/ thread. The osnoise specific fields report:
- The RUNTIME IN US reports the amount of time in microseconds that
the osnoise thread kept looping reading the time.
- The NOISE IN US reports the sum of noise in microseconds observed
by the osnoise tracer during the associated runtime.
- The % OF CPU AVAILABLE reports the percentage of CPU available for
the osnoise thread during the runtime window.
- The MAX SINGLE NOISE IN US reports the maximum single noise observed
during the runtime window.
- The Interference counters display how many each of the respective
interference happened during the runtime window.
Note that the example above shows a high number of HW noise samples.
The reason being is that this sample was taken on a virtual machine,
and the host interference is detected as a hardware interference.
Tracer Configuration
--------------------
The tracer has a set of options inside the osnoise directory, they are:
- osnoise/cpus: CPUs at which a osnoise thread will execute.
- osnoise/period_us: the period of the osnoise thread.
- osnoise/runtime_us: how long an osnoise thread will look for noise.
- osnoise/stop_tracing_us: stop the system tracing if a single noise
higher than the configured value happens. Writing 0 disables this
option.
- osnoise/stop_tracing_total_us: stop the system tracing if total noise
higher than the configured value happens. Writing 0 disables this
option.
- tracing_threshold: the minimum delta between two time() reads to be
considered as noise, in us. When set to 0, the default value will
be used, which is currently 1 us.
- osnoise/options: a set of on/off options that can be enabled by
writing the option name to the file or disabled by writing the option
name preceded with the 'NO\_' prefix. For example, writing
NO_OSNOISE_WORKLOAD disables the OSNOISE_WORKLOAD option. The
special DEAFAULTS option resets all options to the default value.
Tracer Options
--------------
The osnoise/options file exposes a set of on/off configuration options for
the osnoise tracer. These options are:
- DEFAULTS: reset the options to the default value.
- OSNOISE_WORKLOAD: do not dispatch osnoise workload (see dedicated
section below).
- PANIC_ON_STOP: call panic() if the tracer stops. This option serves to
capture a vmcore.
- OSNOISE_PREEMPT_DISABLE: disable preemption while running the osnoise
workload, allowing only IRQ and hardware-related noise.
- OSNOISE_IRQ_DISABLE: disable IRQs while running the osnoise workload,
allowing only NMIs and hardware-related noise, like hwlat tracer.
Additional Tracing
------------------
In addition to the tracer, a set of tracepoints were added to
facilitate the identification of the osnoise source.
- osnoise:sample_threshold: printed anytime a noise is higher than
the configurable tolerance_ns.
- osnoise:nmi_noise: noise from NMI, including the duration.
- osnoise:irq_noise: noise from an IRQ, including the duration.
- osnoise:softirq_noise: noise from a SoftIRQ, including the
duration.
- osnoise:thread_noise: noise from a thread, including the duration.
Note that all the values are *net values*. For example, if while osnoise
is running, another thread preempts the osnoise thread, it will start a
thread_noise duration at the start. Then, an IRQ takes place, preempting
the thread_noise, starting a irq_noise. When the IRQ ends its execution,
it will compute its duration, and this duration will be subtracted from
the thread_noise, in such a way as to avoid the double accounting of the
IRQ execution. This logic is valid for all sources of noise.
Here is one example of the usage of these tracepoints::
osnoise/8-961 [008] d.h. 5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns
osnoise/8-961 [008] dNh. 5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns
migration/8-54 [008] d... 5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns
osnoise/8-961 [008] .... 5789.858413: sample_threshold: start 5789.858404555 duration 8812 ns interferences 2
In this example, a noise sample of 8 microseconds was reported in the last
line, pointing to two interferences. Looking backward in the trace, the
two previous entries were about the migration thread running after a
timer IRQ execution. The first event is not part of the noise because
it took place one millisecond before.
It is worth noticing that the sum of the duration reported in the
tracepoints is smaller than eight us reported in the sample_threshold.
The reason roots in the overhead of the entry and exit code that happens
before and after any interference execution. This justifies the dual
approach: measuring thread and tracing.
Running osnoise tracer without workload
---------------------------------------
By enabling the osnoise tracer with the NO_OSNOISE_WORKLOAD option set,
the osnoise: tracepoints serve to measure the execution time of
any type of Linux task, free from the interference of other tasks.
@@ -0,0 +1,37 @@
#!/usr/bin/env python
# add symbolic names to read_msr / write_msr in trace
# decode_msr msr-index.h < trace
import sys
import re
msrs = dict()
with open(sys.argv[1] if len(sys.argv) > 1 else "msr-index.h", "r") as f:
for j in f:
m = re.match(r'#define (MSR_\w+)\s+(0x[0-9a-fA-F]+)', j)
if m:
msrs[int(m.group(2), 16)] = m.group(1)
extra_ranges = (
( "MSR_LASTBRANCH_%d_FROM_IP", 0x680, 0x69F ),
( "MSR_LASTBRANCH_%d_TO_IP", 0x6C0, 0x6DF ),
( "LBR_INFO_%d", 0xdc0, 0xddf ),
)
for j in sys.stdin:
m = re.search(r'(read|write)_msr:\s+([0-9a-f]+)', j)
if m:
r = None
num = int(m.group(2), 16)
if num in msrs:
r = msrs[num]
else:
for er in extra_ranges:
if er[1] <= num <= er[2]:
r = er[0] % (num - er[1],)
break
if r:
j = j.replace(" " + m.group(2), " " + r + "(" + m.group(2) + ")")
print(j)
@@ -0,0 +1,418 @@
#!/usr/bin/env perl
# This is a POC (proof of concept or piece of crap, take your pick) for reading the
# text representation of trace output related to page allocation. It makes an attempt
# to extract some high-level information on what is going on. The accuracy of the parser
# may vary considerably
#
# Example usage: trace-pagealloc-postprocess.pl < /sys/kernel/tracing/trace_pipe
# other options
# --prepend-parent Report on the parent proc and PID
# --read-procstat If the trace lacks process info, get it from /proc
# --ignore-pid Aggregate processes of the same name together
#
# Copyright (c) IBM Corporation 2009
# Author: Mel Gorman <mel@csn.ul.ie>
use strict;
use Getopt::Long;
# Tracepoint events
use constant MM_PAGE_ALLOC => 1;
use constant MM_PAGE_FREE => 2;
use constant MM_PAGE_FREE_BATCHED => 3;
use constant MM_PAGE_PCPU_DRAIN => 4;
use constant MM_PAGE_ALLOC_ZONE_LOCKED => 5;
use constant MM_PAGE_ALLOC_EXTFRAG => 6;
use constant EVENT_UNKNOWN => 7;
# Constants used to track state
use constant STATE_PCPU_PAGES_DRAINED => 8;
use constant STATE_PCPU_PAGES_REFILLED => 9;
# High-level events extrapolated from tracepoints
use constant HIGH_PCPU_DRAINS => 10;
use constant HIGH_PCPU_REFILLS => 11;
use constant HIGH_EXT_FRAGMENT => 12;
use constant HIGH_EXT_FRAGMENT_SEVERE => 13;
use constant HIGH_EXT_FRAGMENT_MODERATE => 14;
use constant HIGH_EXT_FRAGMENT_CHANGED => 15;
my %perprocesspid;
my %perprocess;
my $opt_ignorepid;
my $opt_read_procstat;
my $opt_prepend_parent;
# Catch sigint and exit on request
my $sigint_report = 0;
my $sigint_exit = 0;
my $sigint_pending = 0;
my $sigint_received = 0;
sub sigint_handler {
my $current_time = time;
if ($current_time - 2 > $sigint_received) {
print "SIGINT received, report pending. Hit ctrl-c again to exit\n";
$sigint_report = 1;
} else {
if (!$sigint_exit) {
print "Second SIGINT received quickly, exiting\n";
}
$sigint_exit++;
}
if ($sigint_exit > 3) {
print "Many SIGINTs received, exiting now without report\n";
exit;
}
$sigint_received = $current_time;
$sigint_pending = 1;
}
$SIG{INT} = "sigint_handler";
# Parse command line options
GetOptions(
'ignore-pid' => \$opt_ignorepid,
'read-procstat' => \$opt_read_procstat,
'prepend-parent' => \$opt_prepend_parent,
);
# Defaults for dynamically discovered regex's
my $regex_fragdetails_default = 'page=([0-9a-f]*) pfn=([0-9]*) alloc_order=([-0-9]*) fallback_order=([-0-9]*) pageblock_order=([-0-9]*) alloc_migratetype=([-0-9]*) fallback_migratetype=([-0-9]*) fragmenting=([-0-9]) change_ownership=([-0-9])';
# Dyanically discovered regex
my $regex_fragdetails;
# Static regex used. Specified like this for readability and for use with /o
# (process_pid) (cpus ) ( time ) (tpoint ) (details)
my $regex_traceevent = '\s*([a-zA-Z0-9-]*)\s*(\[[0-9]*\])\s*([0-9.]*):\s*([a-zA-Z_]*):\s*(.*)';
my $regex_statname = '[-0-9]*\s\((.*)\).*';
my $regex_statppid = '[-0-9]*\s\(.*\)\s[A-Za-z]\s([0-9]*).*';
sub generate_traceevent_regex {
my $event = shift;
my $default = shift;
my $regex;
# Read the event format or use the default
if (!open (FORMAT, "/sys/kernel/tracing/events/$event/format")) {
$regex = $default;
} else {
my $line;
while (!eof(FORMAT)) {
$line = <FORMAT>;
if ($line =~ /^print fmt:\s"(.*)",.*/) {
$regex = $1;
$regex =~ s/%p/\([0-9a-f]*\)/g;
$regex =~ s/%d/\([-0-9]*\)/g;
$regex =~ s/%lu/\([0-9]*\)/g;
}
}
}
# Verify fields are in the right order
my $tuple;
foreach $tuple (split /\s/, $regex) {
my ($key, $value) = split(/=/, $tuple);
my $expected = shift;
if ($key ne $expected) {
print("WARNING: Format not as expected '$key' != '$expected'");
$regex =~ s/$key=\((.*)\)/$key=$1/;
}
}
if (defined shift) {
die("Fewer fields than expected in format");
}
return $regex;
}
$regex_fragdetails = generate_traceevent_regex("kmem/mm_page_alloc_extfrag",
$regex_fragdetails_default,
"page", "pfn",
"alloc_order", "fallback_order", "pageblock_order",
"alloc_migratetype", "fallback_migratetype",
"fragmenting", "change_ownership");
sub read_statline($) {
my $pid = $_[0];
my $statline;
if (open(STAT, "/proc/$pid/stat")) {
$statline = <STAT>;
close(STAT);
}
if ($statline eq '') {
$statline = "-1 (UNKNOWN_PROCESS_NAME) R 0";
}
return $statline;
}
sub guess_process_pid($$) {
my $pid = $_[0];
my $statline = $_[1];
if ($pid == 0) {
return "swapper-0";
}
if ($statline !~ /$regex_statname/o) {
die("Failed to math stat line for process name :: $statline");
}
return "$1-$pid";
}
sub parent_info($$) {
my $pid = $_[0];
my $statline = $_[1];
my $ppid;
if ($pid == 0) {
return "NOPARENT-0";
}
if ($statline !~ /$regex_statppid/o) {
die("Failed to match stat line process ppid:: $statline");
}
# Read the ppid stat line
$ppid = $1;
return guess_process_pid($ppid, read_statline($ppid));
}
sub process_events {
my $traceevent;
my $process_pid;
my $cpus;
my $timestamp;
my $tracepoint;
my $details;
my $statline;
# Read each line of the event log
EVENT_PROCESS:
while ($traceevent = <STDIN>) {
if ($traceevent =~ /$regex_traceevent/o) {
$process_pid = $1;
$tracepoint = $4;
if ($opt_read_procstat || $opt_prepend_parent) {
$process_pid =~ /(.*)-([0-9]*)$/;
my $process = $1;
my $pid = $2;
$statline = read_statline($pid);
if ($opt_read_procstat && $process eq '') {
$process_pid = guess_process_pid($pid, $statline);
}
if ($opt_prepend_parent) {
$process_pid = parent_info($pid, $statline) . " :: $process_pid";
}
}
# Unnecessary in this script. Uncomment if required
# $cpus = $2;
# $timestamp = $3;
} else {
next;
}
# Perl Switch() sucks majorly
if ($tracepoint eq "mm_page_alloc") {
$perprocesspid{$process_pid}->{MM_PAGE_ALLOC}++;
} elsif ($tracepoint eq "mm_page_free") {
$perprocesspid{$process_pid}->{MM_PAGE_FREE}++
} elsif ($tracepoint eq "mm_page_free_batched") {
$perprocesspid{$process_pid}->{MM_PAGE_FREE_BATCHED}++;
} elsif ($tracepoint eq "mm_page_pcpu_drain") {
$perprocesspid{$process_pid}->{MM_PAGE_PCPU_DRAIN}++;
$perprocesspid{$process_pid}->{STATE_PCPU_PAGES_DRAINED}++;
} elsif ($tracepoint eq "mm_page_alloc_zone_locked") {
$perprocesspid{$process_pid}->{MM_PAGE_ALLOC_ZONE_LOCKED}++;
$perprocesspid{$process_pid}->{STATE_PCPU_PAGES_REFILLED}++;
} elsif ($tracepoint eq "mm_page_alloc_extfrag") {
# Extract the details of the event now
$details = $5;
my ($page, $pfn);
my ($alloc_order, $fallback_order, $pageblock_order);
my ($alloc_migratetype, $fallback_migratetype);
my ($fragmenting, $change_ownership);
if ($details !~ /$regex_fragdetails/o) {
print "WARNING: Failed to parse mm_page_alloc_extfrag as expected\n";
next;
}
$perprocesspid{$process_pid}->{MM_PAGE_ALLOC_EXTFRAG}++;
$page = $1;
$pfn = $2;
$alloc_order = $3;
$fallback_order = $4;
$pageblock_order = $5;
$alloc_migratetype = $6;
$fallback_migratetype = $7;
$fragmenting = $8;
$change_ownership = $9;
if ($fragmenting) {
$perprocesspid{$process_pid}->{HIGH_EXT_FRAG}++;
if ($fallback_order <= 3) {
$perprocesspid{$process_pid}->{HIGH_EXT_FRAGMENT_SEVERE}++;
} else {
$perprocesspid{$process_pid}->{HIGH_EXT_FRAGMENT_MODERATE}++;
}
}
if ($change_ownership) {
$perprocesspid{$process_pid}->{HIGH_EXT_FRAGMENT_CHANGED}++;
}
} else {
$perprocesspid{$process_pid}->{EVENT_UNKNOWN}++;
}
# Catch a full pcpu drain event
if ($perprocesspid{$process_pid}->{STATE_PCPU_PAGES_DRAINED} &&
$tracepoint ne "mm_page_pcpu_drain") {
$perprocesspid{$process_pid}->{HIGH_PCPU_DRAINS}++;
$perprocesspid{$process_pid}->{STATE_PCPU_PAGES_DRAINED} = 0;
}
# Catch a full pcpu refill event
if ($perprocesspid{$process_pid}->{STATE_PCPU_PAGES_REFILLED} &&
$tracepoint ne "mm_page_alloc_zone_locked") {
$perprocesspid{$process_pid}->{HIGH_PCPU_REFILLS}++;
$perprocesspid{$process_pid}->{STATE_PCPU_PAGES_REFILLED} = 0;
}
if ($sigint_pending) {
last EVENT_PROCESS;
}
}
}
sub dump_stats {
my $hashref = shift;
my %stats = %$hashref;
# Dump per-process stats
my $process_pid;
my $max_strlen = 0;
# Get the maximum process name
foreach $process_pid (keys %perprocesspid) {
my $len = length($process_pid);
if ($len > $max_strlen) {
$max_strlen = $len;
}
}
$max_strlen += 2;
printf("\n");
printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s\n",
"Process", "Pages", "Pages", "Pages", "Pages", "PCPU", "PCPU", "PCPU", "Fragment", "Fragment", "MigType", "Fragment", "Fragment", "Unknown");
printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s\n",
"details", "allocd", "allocd", "freed", "freed", "pages", "drains", "refills", "Fallback", "Causing", "Changed", "Severe", "Moderate", "");
printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s\n",
"", "", "under lock", "direct", "pagevec", "drain", "", "", "", "", "", "", "", "");
foreach $process_pid (keys %stats) {
# Dump final aggregates
if ($stats{$process_pid}->{STATE_PCPU_PAGES_DRAINED}) {
$stats{$process_pid}->{HIGH_PCPU_DRAINS}++;
$stats{$process_pid}->{STATE_PCPU_PAGES_DRAINED} = 0;
}
if ($stats{$process_pid}->{STATE_PCPU_PAGES_REFILLED}) {
$stats{$process_pid}->{HIGH_PCPU_REFILLS}++;
$stats{$process_pid}->{STATE_PCPU_PAGES_REFILLED} = 0;
}
printf("%-" . $max_strlen . "s %8d %10d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d\n",
$process_pid,
$stats{$process_pid}->{MM_PAGE_ALLOC},
$stats{$process_pid}->{MM_PAGE_ALLOC_ZONE_LOCKED},
$stats{$process_pid}->{MM_PAGE_FREE},
$stats{$process_pid}->{MM_PAGE_FREE_BATCHED},
$stats{$process_pid}->{MM_PAGE_PCPU_DRAIN},
$stats{$process_pid}->{HIGH_PCPU_DRAINS},
$stats{$process_pid}->{HIGH_PCPU_REFILLS},
$stats{$process_pid}->{MM_PAGE_ALLOC_EXTFRAG},
$stats{$process_pid}->{HIGH_EXT_FRAG},
$stats{$process_pid}->{HIGH_EXT_FRAGMENT_CHANGED},
$stats{$process_pid}->{HIGH_EXT_FRAGMENT_SEVERE},
$stats{$process_pid}->{HIGH_EXT_FRAGMENT_MODERATE},
$stats{$process_pid}->{EVENT_UNKNOWN});
}
}
sub aggregate_perprocesspid() {
my $process_pid;
my $process;
undef %perprocess;
foreach $process_pid (keys %perprocesspid) {
$process = $process_pid;
$process =~ s/-([0-9])*$//;
if ($process eq '') {
$process = "NO_PROCESS_NAME";
}
$perprocess{$process}->{MM_PAGE_ALLOC} += $perprocesspid{$process_pid}->{MM_PAGE_ALLOC};
$perprocess{$process}->{MM_PAGE_ALLOC_ZONE_LOCKED} += $perprocesspid{$process_pid}->{MM_PAGE_ALLOC_ZONE_LOCKED};
$perprocess{$process}->{MM_PAGE_FREE} += $perprocesspid{$process_pid}->{MM_PAGE_FREE};
$perprocess{$process}->{MM_PAGE_FREE_BATCHED} += $perprocesspid{$process_pid}->{MM_PAGE_FREE_BATCHED};
$perprocess{$process}->{MM_PAGE_PCPU_DRAIN} += $perprocesspid{$process_pid}->{MM_PAGE_PCPU_DRAIN};
$perprocess{$process}->{HIGH_PCPU_DRAINS} += $perprocesspid{$process_pid}->{HIGH_PCPU_DRAINS};
$perprocess{$process}->{HIGH_PCPU_REFILLS} += $perprocesspid{$process_pid}->{HIGH_PCPU_REFILLS};
$perprocess{$process}->{MM_PAGE_ALLOC_EXTFRAG} += $perprocesspid{$process_pid}->{MM_PAGE_ALLOC_EXTFRAG};
$perprocess{$process}->{HIGH_EXT_FRAG} += $perprocesspid{$process_pid}->{HIGH_EXT_FRAG};
$perprocess{$process}->{HIGH_EXT_FRAGMENT_CHANGED} += $perprocesspid{$process_pid}->{HIGH_EXT_FRAGMENT_CHANGED};
$perprocess{$process}->{HIGH_EXT_FRAGMENT_SEVERE} += $perprocesspid{$process_pid}->{HIGH_EXT_FRAGMENT_SEVERE};
$perprocess{$process}->{HIGH_EXT_FRAGMENT_MODERATE} += $perprocesspid{$process_pid}->{HIGH_EXT_FRAGMENT_MODERATE};
$perprocess{$process}->{EVENT_UNKNOWN} += $perprocesspid{$process_pid}->{EVENT_UNKNOWN};
}
}
sub report() {
if (!$opt_ignorepid) {
dump_stats(\%perprocesspid);
} else {
aggregate_perprocesspid();
dump_stats(\%perprocess);
}
}
# Process events or signals until neither is available
sub signal_loop() {
my $sigint_processed;
do {
$sigint_processed = 0;
process_events();
# Handle pending signals if any
if ($sigint_pending) {
my $current_time = time;
if ($sigint_exit) {
print "Received exit signal\n";
$sigint_pending = 0;
}
if ($sigint_report) {
if ($current_time >= $sigint_received + 2) {
report();
$sigint_report = 0;
$sigint_pending = 0;
$sigint_processed = 1;
}
}
}
} while ($sigint_pending || $sigint_processed);
}
signal_loop();
report();
@@ -0,0 +1,752 @@
#!/usr/bin/env perl
# This is a POC for reading the text representation of trace output related to
# page reclaim. It makes an attempt to extract some high-level information on
# what is going on. The accuracy of the parser may vary
#
# Example usage: trace-vmscan-postprocess.pl < /sys/kernel/tracing/trace_pipe
# other options
# --read-procstat If the trace lacks process info, get it from /proc
# --ignore-pid Aggregate processes of the same name together
#
# Copyright (c) IBM Corporation 2009
# Author: Mel Gorman <mel@csn.ul.ie>
use strict;
use Getopt::Long;
# Tracepoint events
use constant MM_VMSCAN_DIRECT_RECLAIM_BEGIN => 1;
use constant MM_VMSCAN_DIRECT_RECLAIM_END => 2;
use constant MM_VMSCAN_KSWAPD_WAKE => 3;
use constant MM_VMSCAN_KSWAPD_SLEEP => 4;
use constant MM_VMSCAN_LRU_SHRINK_ACTIVE => 5;
use constant MM_VMSCAN_LRU_SHRINK_INACTIVE => 6;
use constant MM_VMSCAN_LRU_ISOLATE => 7;
use constant MM_VMSCAN_WRITEPAGE_FILE_SYNC => 8;
use constant MM_VMSCAN_WRITEPAGE_ANON_SYNC => 9;
use constant MM_VMSCAN_WRITEPAGE_FILE_ASYNC => 10;
use constant MM_VMSCAN_WRITEPAGE_ANON_ASYNC => 11;
use constant MM_VMSCAN_WRITEPAGE_ASYNC => 12;
use constant EVENT_UNKNOWN => 13;
# Per-order events
use constant MM_VMSCAN_DIRECT_RECLAIM_BEGIN_PERORDER => 11;
use constant MM_VMSCAN_WAKEUP_KSWAPD_PERORDER => 12;
use constant MM_VMSCAN_KSWAPD_WAKE_PERORDER => 13;
use constant HIGH_KSWAPD_REWAKEUP_PERORDER => 14;
# Constants used to track state
use constant STATE_DIRECT_BEGIN => 15;
use constant STATE_DIRECT_ORDER => 16;
use constant STATE_KSWAPD_BEGIN => 17;
use constant STATE_KSWAPD_ORDER => 18;
# High-level events extrapolated from tracepoints
use constant HIGH_DIRECT_RECLAIM_LATENCY => 19;
use constant HIGH_KSWAPD_LATENCY => 20;
use constant HIGH_KSWAPD_REWAKEUP => 21;
use constant HIGH_NR_SCANNED => 22;
use constant HIGH_NR_TAKEN => 23;
use constant HIGH_NR_RECLAIMED => 24;
use constant HIGH_NR_FILE_SCANNED => 25;
use constant HIGH_NR_ANON_SCANNED => 26;
use constant HIGH_NR_FILE_RECLAIMED => 27;
use constant HIGH_NR_ANON_RECLAIMED => 28;
my %perprocesspid;
my %perprocess;
my %last_procmap;
my $opt_ignorepid;
my $opt_read_procstat;
my $total_wakeup_kswapd;
my ($total_direct_reclaim, $total_direct_nr_scanned);
my ($total_direct_nr_file_scanned, $total_direct_nr_anon_scanned);
my ($total_direct_latency, $total_kswapd_latency);
my ($total_direct_nr_reclaimed);
my ($total_direct_nr_file_reclaimed, $total_direct_nr_anon_reclaimed);
my ($total_direct_writepage_file_sync, $total_direct_writepage_file_async);
my ($total_direct_writepage_anon_sync, $total_direct_writepage_anon_async);
my ($total_kswapd_nr_scanned, $total_kswapd_wake);
my ($total_kswapd_nr_file_scanned, $total_kswapd_nr_anon_scanned);
my ($total_kswapd_writepage_file_sync, $total_kswapd_writepage_file_async);
my ($total_kswapd_writepage_anon_sync, $total_kswapd_writepage_anon_async);
my ($total_kswapd_nr_reclaimed);
my ($total_kswapd_nr_file_reclaimed, $total_kswapd_nr_anon_reclaimed);
# Catch sigint and exit on request
my $sigint_report = 0;
my $sigint_exit = 0;
my $sigint_pending = 0;
my $sigint_received = 0;
sub sigint_handler {
my $current_time = time;
if ($current_time - 2 > $sigint_received) {
print "SIGINT received, report pending. Hit ctrl-c again to exit\n";
$sigint_report = 1;
} else {
if (!$sigint_exit) {
print "Second SIGINT received quickly, exiting\n";
}
$sigint_exit++;
}
if ($sigint_exit > 3) {
print "Many SIGINTs received, exiting now without report\n";
exit;
}
$sigint_received = $current_time;
$sigint_pending = 1;
}
$SIG{INT} = "sigint_handler";
# Parse command line options
GetOptions(
'ignore-pid' => \$opt_ignorepid,
'read-procstat' => \$opt_read_procstat,
);
# Defaults for dynamically discovered regex's
my $regex_direct_begin_default = 'order=([0-9]*) gfp_flags=([A-Z_|]*)';
my $regex_direct_end_default = 'nr_reclaimed=([0-9]*)';
my $regex_kswapd_wake_default = 'nid=([0-9]*) order=([0-9]*)';
my $regex_kswapd_sleep_default = 'nid=([0-9]*)';
my $regex_wakeup_kswapd_default = 'nid=([0-9]*) order=([0-9]*) gfp_flags=([A-Z_|]*)';
my $regex_lru_isolate_default = 'classzone=([0-9]*) order=([0-9]*) nr_requested=([0-9]*) nr_scanned=([0-9]*) nr_skipped=([0-9]*) nr_taken=([0-9]*) lru=([a-z_]*)';
my $regex_lru_shrink_inactive_default = 'nid=([0-9]*) nr_scanned=([0-9]*) nr_reclaimed=([0-9]*) nr_dirty=([0-9]*) nr_writeback=([0-9]*) nr_congested=([0-9]*) nr_immediate=([0-9]*) nr_activate_anon=([0-9]*) nr_activate_file=([0-9]*) nr_ref_keep=([0-9]*) nr_unmap_fail=([0-9]*) priority=([0-9]*) flags=([A-Z_|]*)';
my $regex_lru_shrink_active_default = 'lru=([A-Z_]*) nr_taken=([0-9]*) nr_active=([0-9]*) nr_deactivated=([0-9]*) nr_referenced=([0-9]*) priority=([0-9]*) flags=([A-Z_|]*)' ;
my $regex_writepage_default = 'page=([0-9a-f]*) pfn=([0-9]*) flags=([A-Z_|]*)';
# Dyanically discovered regex
my $regex_direct_begin;
my $regex_direct_end;
my $regex_kswapd_wake;
my $regex_kswapd_sleep;
my $regex_wakeup_kswapd;
my $regex_lru_isolate;
my $regex_lru_shrink_inactive;
my $regex_lru_shrink_active;
my $regex_writepage;
# Static regex used. Specified like this for readability and for use with /o
# (process_pid) (cpus ) ( time ) (tpoint ) (details)
my $regex_traceevent = '\s*([a-zA-Z0-9-]*)\s*(\[[0-9]*\])(\s*[dX.][Nnp.][Hhs.][0-9a-fA-F.]*|)\s*([0-9.]*):\s*([a-zA-Z_]*):\s*(.*)';
my $regex_statname = '[-0-9]*\s\((.*)\).*';
my $regex_statppid = '[-0-9]*\s\(.*\)\s[A-Za-z]\s([0-9]*).*';
sub generate_traceevent_regex {
my $event = shift;
my $default = shift;
my $regex;
# Read the event format or use the default
if (!open (FORMAT, "/sys/kernel/tracing/events/$event/format")) {
print("WARNING: Event $event format string not found\n");
return $default;
} else {
my $line;
while (!eof(FORMAT)) {
$line = <FORMAT>;
$line =~ s/, REC->.*//;
if ($line =~ /^print fmt:\s"(.*)".*/) {
$regex = $1;
$regex =~ s/%s/\([0-9a-zA-Z|_]*\)/g;
$regex =~ s/%p/\([0-9a-f]*\)/g;
$regex =~ s/%d/\([-0-9]*\)/g;
$regex =~ s/%ld/\([-0-9]*\)/g;
$regex =~ s/%lu/\([0-9]*\)/g;
}
}
}
# Can't handle the print_flags stuff but in the context of this
# script, it really doesn't matter
$regex =~ s/\(REC.*\) \? __print_flags.*//;
# Verify fields are in the right order
my $tuple;
foreach $tuple (split /\s/, $regex) {
my ($key, $value) = split(/=/, $tuple);
my $expected = shift;
if ($key ne $expected) {
print("WARNING: Format not as expected for event $event '$key' != '$expected'\n");
$regex =~ s/$key=\((.*)\)/$key=$1/;
}
}
if (defined shift) {
die("Fewer fields than expected in format");
}
return $regex;
}
$regex_direct_begin = generate_traceevent_regex(
"vmscan/mm_vmscan_direct_reclaim_begin",
$regex_direct_begin_default,
"order", "gfp_flags");
$regex_direct_end = generate_traceevent_regex(
"vmscan/mm_vmscan_direct_reclaim_end",
$regex_direct_end_default,
"nr_reclaimed");
$regex_kswapd_wake = generate_traceevent_regex(
"vmscan/mm_vmscan_kswapd_wake",
$regex_kswapd_wake_default,
"nid", "order");
$regex_kswapd_sleep = generate_traceevent_regex(
"vmscan/mm_vmscan_kswapd_sleep",
$regex_kswapd_sleep_default,
"nid");
$regex_wakeup_kswapd = generate_traceevent_regex(
"vmscan/mm_vmscan_wakeup_kswapd",
$regex_wakeup_kswapd_default,
"nid", "order", "gfp_flags");
$regex_lru_isolate = generate_traceevent_regex(
"vmscan/mm_vmscan_lru_isolate",
$regex_lru_isolate_default,
"classzone", "order",
"nr_requested", "nr_scanned", "nr_skipped", "nr_taken",
"lru");
$regex_lru_shrink_inactive = generate_traceevent_regex(
"vmscan/mm_vmscan_lru_shrink_inactive",
$regex_lru_shrink_inactive_default,
"nid", "nr_scanned", "nr_reclaimed", "nr_dirty", "nr_writeback",
"nr_congested", "nr_immediate", "nr_activate_anon",
"nr_activate_file", "nr_ref_keep",
"nr_unmap_fail", "priority", "flags");
$regex_lru_shrink_active = generate_traceevent_regex(
"vmscan/mm_vmscan_lru_shrink_active",
$regex_lru_shrink_active_default,
"nid", "nr_taken", "nr_active", "nr_deactivated", "nr_referenced",
"priority", "flags");
$regex_writepage = generate_traceevent_regex(
"vmscan/mm_vmscan_write_folio",
$regex_writepage_default,
"page", "pfn", "flags");
sub read_statline($) {
my $pid = $_[0];
my $statline;
if (open(STAT, "/proc/$pid/stat")) {
$statline = <STAT>;
close(STAT);
}
if ($statline eq '') {
$statline = "-1 (UNKNOWN_PROCESS_NAME) R 0";
}
return $statline;
}
sub guess_process_pid($$) {
my $pid = $_[0];
my $statline = $_[1];
if ($pid == 0) {
return "swapper-0";
}
if ($statline !~ /$regex_statname/o) {
die("Failed to math stat line for process name :: $statline");
}
return "$1-$pid";
}
# Convert sec.usec timestamp format
sub timestamp_to_ms($) {
my $timestamp = $_[0];
my ($sec, $usec) = split (/\./, $timestamp);
return ($sec * 1000) + ($usec / 1000);
}
sub process_events {
my $traceevent;
my $process_pid;
my $cpus;
my $timestamp;
my $tracepoint;
my $details;
my $statline;
# Read each line of the event log
EVENT_PROCESS:
while ($traceevent = <STDIN>) {
if ($traceevent =~ /$regex_traceevent/o) {
$process_pid = $1;
$timestamp = $4;
$tracepoint = $5;
$process_pid =~ /(.*)-([0-9]*)$/;
my $process = $1;
my $pid = $2;
if ($process eq "") {
$process = $last_procmap{$pid};
$process_pid = "$process-$pid";
}
$last_procmap{$pid} = $process;
if ($opt_read_procstat) {
$statline = read_statline($pid);
if ($opt_read_procstat && $process eq '') {
$process_pid = guess_process_pid($pid, $statline);
}
}
} else {
next;
}
# Perl Switch() sucks majorly
if ($tracepoint eq "mm_vmscan_direct_reclaim_begin") {
$timestamp = timestamp_to_ms($timestamp);
$perprocesspid{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN}++;
$perprocesspid{$process_pid}->{STATE_DIRECT_BEGIN} = $timestamp;
$details = $6;
if ($details !~ /$regex_direct_begin/o) {
print "WARNING: Failed to parse mm_vmscan_direct_reclaim_begin as expected\n";
print " $details\n";
print " $regex_direct_begin\n";
next;
}
my $order = $1;
$perprocesspid{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN_PERORDER}[$order]++;
$perprocesspid{$process_pid}->{STATE_DIRECT_ORDER} = $order;
} elsif ($tracepoint eq "mm_vmscan_direct_reclaim_end") {
# Count the event itself
my $index = $perprocesspid{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_END};
$perprocesspid{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_END}++;
# Record how long direct reclaim took this time
if (defined $perprocesspid{$process_pid}->{STATE_DIRECT_BEGIN}) {
$timestamp = timestamp_to_ms($timestamp);
my $order = $perprocesspid{$process_pid}->{STATE_DIRECT_ORDER};
my $latency = ($timestamp - $perprocesspid{$process_pid}->{STATE_DIRECT_BEGIN});
$perprocesspid{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$index] = "$order-$latency";
}
} elsif ($tracepoint eq "mm_vmscan_kswapd_wake") {
$details = $6;
if ($details !~ /$regex_kswapd_wake/o) {
print "WARNING: Failed to parse mm_vmscan_kswapd_wake as expected\n";
print " $details\n";
print " $regex_kswapd_wake\n";
next;
}
my $order = $2;
$perprocesspid{$process_pid}->{STATE_KSWAPD_ORDER} = $order;
if (!$perprocesspid{$process_pid}->{STATE_KSWAPD_BEGIN}) {
$timestamp = timestamp_to_ms($timestamp);
$perprocesspid{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE}++;
$perprocesspid{$process_pid}->{STATE_KSWAPD_BEGIN} = $timestamp;
$perprocesspid{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE_PERORDER}[$order]++;
} else {
$perprocesspid{$process_pid}->{HIGH_KSWAPD_REWAKEUP}++;
$perprocesspid{$process_pid}->{HIGH_KSWAPD_REWAKEUP_PERORDER}[$order]++;
}
} elsif ($tracepoint eq "mm_vmscan_kswapd_sleep") {
# Count the event itself
my $index = $perprocesspid{$process_pid}->{MM_VMSCAN_KSWAPD_SLEEP};
$perprocesspid{$process_pid}->{MM_VMSCAN_KSWAPD_SLEEP}++;
# Record how long kswapd was awake
$timestamp = timestamp_to_ms($timestamp);
my $order = $perprocesspid{$process_pid}->{STATE_KSWAPD_ORDER};
my $latency = ($timestamp - $perprocesspid{$process_pid}->{STATE_KSWAPD_BEGIN});
$perprocesspid{$process_pid}->{HIGH_KSWAPD_LATENCY}[$index] = "$order-$latency";
$perprocesspid{$process_pid}->{STATE_KSWAPD_BEGIN} = 0;
} elsif ($tracepoint eq "mm_vmscan_wakeup_kswapd") {
$perprocesspid{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD}++;
$details = $6;
if ($details !~ /$regex_wakeup_kswapd/o) {
print "WARNING: Failed to parse mm_vmscan_wakeup_kswapd as expected\n";
print " $details\n";
print " $regex_wakeup_kswapd\n";
next;
}
my $order = $2;
$perprocesspid{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD_PERORDER}[$order]++;
} elsif ($tracepoint eq "mm_vmscan_lru_isolate") {
$details = $6;
if ($details !~ /$regex_lru_isolate/o) {
print "WARNING: Failed to parse mm_vmscan_lru_isolate as expected\n";
print " $details\n";
print " $regex_lru_isolate/o\n";
next;
}
my $nr_scanned = $4;
my $lru = $7;
# To closer match vmstat scanning statistics, only count
# inactive lru as scanning
if ($lru =~ /inactive_/) {
$perprocesspid{$process_pid}->{HIGH_NR_SCANNED} += $nr_scanned;
if ($lru =~ /_file/) {
$perprocesspid{$process_pid}->{HIGH_NR_FILE_SCANNED} += $nr_scanned;
} else {
$perprocesspid{$process_pid}->{HIGH_NR_ANON_SCANNED} += $nr_scanned;
}
}
} elsif ($tracepoint eq "mm_vmscan_lru_shrink_inactive") {
$details = $6;
if ($details !~ /$regex_lru_shrink_inactive/o) {
print "WARNING: Failed to parse mm_vmscan_lru_shrink_inactive as expected\n";
print " $details\n";
print " $regex_lru_shrink_inactive/o\n";
next;
}
my $nr_reclaimed = $3;
my $flags = $13;
my $file = 0;
if ($flags =~ /RECLAIM_WB_FILE/) {
$file = 1;
}
$perprocesspid{$process_pid}->{HIGH_NR_RECLAIMED} += $nr_reclaimed;
if ($file) {
$perprocesspid{$process_pid}->{HIGH_NR_FILE_RECLAIMED} += $nr_reclaimed;
} else {
$perprocesspid{$process_pid}->{HIGH_NR_ANON_RECLAIMED} += $nr_reclaimed;
}
} elsif ($tracepoint eq "mm_vmscan_writepage") {
$details = $6;
if ($details !~ /$regex_writepage/o) {
print "WARNING: Failed to parse mm_vmscan_writepage as expected\n";
print " $details\n";
print " $regex_writepage\n";
next;
}
my $flags = $3;
my $file = 0;
my $sync_io = 0;
if ($flags =~ /RECLAIM_WB_FILE/) {
$file = 1;
}
if ($flags =~ /RECLAIM_WB_SYNC/) {
$sync_io = 1;
}
if ($sync_io) {
if ($file) {
$perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC}++;
} else {
$perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC}++;
}
} else {
if ($file) {
$perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC}++;
} else {
$perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC}++;
}
}
} else {
$perprocesspid{$process_pid}->{EVENT_UNKNOWN}++;
}
if ($sigint_pending) {
last EVENT_PROCESS;
}
}
}
sub dump_stats {
my $hashref = shift;
my %stats = %$hashref;
# Dump per-process stats
my $process_pid;
my $max_strlen = 0;
# Get the maximum process name
foreach $process_pid (keys %perprocesspid) {
my $len = length($process_pid);
if ($len > $max_strlen) {
$max_strlen = $len;
}
}
$max_strlen += 2;
# Work out latencies
printf("\n") if !$opt_ignorepid;
printf("Reclaim latencies expressed as order-latency_in_ms\n") if !$opt_ignorepid;
foreach $process_pid (keys %stats) {
if (!$stats{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[0] &&
!$stats{$process_pid}->{HIGH_KSWAPD_LATENCY}[0]) {
next;
}
printf "%-" . $max_strlen . "s ", $process_pid if !$opt_ignorepid;
my $index = 0;
while (defined $stats{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$index] ||
defined $stats{$process_pid}->{HIGH_KSWAPD_LATENCY}[$index]) {
if ($stats{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$index]) {
printf("%s ", $stats{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$index]) if !$opt_ignorepid;
my ($dummy, $latency) = split(/-/, $stats{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$index]);
$total_direct_latency += $latency;
} else {
printf("%s ", $stats{$process_pid}->{HIGH_KSWAPD_LATENCY}[$index]) if !$opt_ignorepid;
my ($dummy, $latency) = split(/-/, $stats{$process_pid}->{HIGH_KSWAPD_LATENCY}[$index]);
$total_kswapd_latency += $latency;
}
$index++;
}
print "\n" if !$opt_ignorepid;
}
# Print out process activity
printf("\n");
printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s %8s\n", "Process", "Direct", "Wokeup", "Pages", "Pages", "Pages", "Pages", "Time");
printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s %8s\n", "details", "Rclms", "Kswapd", "Scanned", "Rclmed", "Sync-IO", "ASync-IO", "Stalled");
foreach $process_pid (keys %stats) {
if (!$stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN}) {
next;
}
$total_direct_reclaim += $stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN};
$total_wakeup_kswapd += $stats{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD};
$total_direct_nr_scanned += $stats{$process_pid}->{HIGH_NR_SCANNED};
$total_direct_nr_file_scanned += $stats{$process_pid}->{HIGH_NR_FILE_SCANNED};
$total_direct_nr_anon_scanned += $stats{$process_pid}->{HIGH_NR_ANON_SCANNED};
$total_direct_nr_reclaimed += $stats{$process_pid}->{HIGH_NR_RECLAIMED};
$total_direct_nr_file_reclaimed += $stats{$process_pid}->{HIGH_NR_FILE_RECLAIMED};
$total_direct_nr_anon_reclaimed += $stats{$process_pid}->{HIGH_NR_ANON_RECLAIMED};
$total_direct_writepage_file_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC};
$total_direct_writepage_anon_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC};
$total_direct_writepage_file_async += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC};
$total_direct_writepage_anon_async += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC};
my $index = 0;
my $this_reclaim_delay = 0;
while (defined $stats{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$index]) {
my ($dummy, $latency) = split(/-/, $stats{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$index]);
$this_reclaim_delay += $latency;
$index++;
}
printf("%-" . $max_strlen . "s %8d %10d %8u %8u %8u %8u %8.3f",
$process_pid,
$stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN},
$stats{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD},
$stats{$process_pid}->{HIGH_NR_SCANNED},
$stats{$process_pid}->{HIGH_NR_FILE_SCANNED},
$stats{$process_pid}->{HIGH_NR_ANON_SCANNED},
$stats{$process_pid}->{HIGH_NR_RECLAIMED},
$stats{$process_pid}->{HIGH_NR_FILE_RECLAIMED},
$stats{$process_pid}->{HIGH_NR_ANON_RECLAIMED},
$stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC},
$stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC},
$this_reclaim_delay / 1000);
if ($stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN}) {
print " ";
for (my $order = 0; $order < 20; $order++) {
my $count = $stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN_PERORDER}[$order];
if ($count != 0) {
print "direct-$order=$count ";
}
}
}
if ($stats{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD}) {
print " ";
for (my $order = 0; $order < 20; $order++) {
my $count = $stats{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD_PERORDER}[$order];
if ($count != 0) {
print "wakeup-$order=$count ";
}
}
}
print "\n";
}
# Print out kswapd activity
printf("\n");
printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s\n", "Kswapd", "Kswapd", "Order", "Pages", "Pages", "Pages", "Pages");
printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s\n", "Instance", "Wakeups", "Re-wakeup", "Scanned", "Rclmed", "Sync-IO", "ASync-IO");
foreach $process_pid (keys %stats) {
if (!$stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE}) {
next;
}
$total_kswapd_wake += $stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE};
$total_kswapd_nr_scanned += $stats{$process_pid}->{HIGH_NR_SCANNED};
$total_kswapd_nr_file_scanned += $stats{$process_pid}->{HIGH_NR_FILE_SCANNED};
$total_kswapd_nr_anon_scanned += $stats{$process_pid}->{HIGH_NR_ANON_SCANNED};
$total_kswapd_nr_reclaimed += $stats{$process_pid}->{HIGH_NR_RECLAIMED};
$total_kswapd_nr_file_reclaimed += $stats{$process_pid}->{HIGH_NR_FILE_RECLAIMED};
$total_kswapd_nr_anon_reclaimed += $stats{$process_pid}->{HIGH_NR_ANON_RECLAIMED};
$total_kswapd_writepage_file_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC};
$total_kswapd_writepage_anon_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC};
$total_kswapd_writepage_file_async += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC};
$total_kswapd_writepage_anon_async += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC};
printf("%-" . $max_strlen . "s %8d %10d %8u %8u %8i %8u",
$process_pid,
$stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE},
$stats{$process_pid}->{HIGH_KSWAPD_REWAKEUP},
$stats{$process_pid}->{HIGH_NR_SCANNED},
$stats{$process_pid}->{HIGH_NR_FILE_SCANNED},
$stats{$process_pid}->{HIGH_NR_ANON_SCANNED},
$stats{$process_pid}->{HIGH_NR_RECLAIMED},
$stats{$process_pid}->{HIGH_NR_FILE_RECLAIMED},
$stats{$process_pid}->{HIGH_NR_ANON_RECLAIMED},
$stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC},
$stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC});
if ($stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE}) {
print " ";
for (my $order = 0; $order < 20; $order++) {
my $count = $stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE_PERORDER}[$order];
if ($count != 0) {
print "wake-$order=$count ";
}
}
}
if ($stats{$process_pid}->{HIGH_KSWAPD_REWAKEUP}) {
print " ";
for (my $order = 0; $order < 20; $order++) {
my $count = $stats{$process_pid}->{HIGH_KSWAPD_REWAKEUP_PERORDER}[$order];
if ($count != 0) {
print "rewake-$order=$count ";
}
}
}
printf("\n");
}
# Print out summaries
$total_direct_latency /= 1000;
$total_kswapd_latency /= 1000;
print "\nSummary\n";
print "Direct reclaims: $total_direct_reclaim\n";
print "Direct reclaim pages scanned: $total_direct_nr_scanned\n";
print "Direct reclaim file pages scanned: $total_direct_nr_file_scanned\n";
print "Direct reclaim anon pages scanned: $total_direct_nr_anon_scanned\n";
print "Direct reclaim pages reclaimed: $total_direct_nr_reclaimed\n";
print "Direct reclaim file pages reclaimed: $total_direct_nr_file_reclaimed\n";
print "Direct reclaim anon pages reclaimed: $total_direct_nr_anon_reclaimed\n";
print "Direct reclaim write file sync I/O: $total_direct_writepage_file_sync\n";
print "Direct reclaim write anon sync I/O: $total_direct_writepage_anon_sync\n";
print "Direct reclaim write file async I/O: $total_direct_writepage_file_async\n";
print "Direct reclaim write anon async I/O: $total_direct_writepage_anon_async\n";
print "Wake kswapd requests: $total_wakeup_kswapd\n";
printf "Time stalled direct reclaim: %-1.2f seconds\n", $total_direct_latency;
print "\n";
print "Kswapd wakeups: $total_kswapd_wake\n";
print "Kswapd pages scanned: $total_kswapd_nr_scanned\n";
print "Kswapd file pages scanned: $total_kswapd_nr_file_scanned\n";
print "Kswapd anon pages scanned: $total_kswapd_nr_anon_scanned\n";
print "Kswapd pages reclaimed: $total_kswapd_nr_reclaimed\n";
print "Kswapd file pages reclaimed: $total_kswapd_nr_file_reclaimed\n";
print "Kswapd anon pages reclaimed: $total_kswapd_nr_anon_reclaimed\n";
print "Kswapd reclaim write file sync I/O: $total_kswapd_writepage_file_sync\n";
print "Kswapd reclaim write anon sync I/O: $total_kswapd_writepage_anon_sync\n";
print "Kswapd reclaim write file async I/O: $total_kswapd_writepage_file_async\n";
print "Kswapd reclaim write anon async I/O: $total_kswapd_writepage_anon_async\n";
printf "Time kswapd awake: %-1.2f seconds\n", $total_kswapd_latency;
}
sub aggregate_perprocesspid() {
my $process_pid;
my $process;
undef %perprocess;
foreach $process_pid (keys %perprocesspid) {
$process = $process_pid;
$process =~ s/-([0-9])*$//;
if ($process eq '') {
$process = "NO_PROCESS_NAME";
}
$perprocess{$process}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN} += $perprocesspid{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN};
$perprocess{$process}->{MM_VMSCAN_KSWAPD_WAKE} += $perprocesspid{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE};
$perprocess{$process}->{MM_VMSCAN_WAKEUP_KSWAPD} += $perprocesspid{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD};
$perprocess{$process}->{HIGH_KSWAPD_REWAKEUP} += $perprocesspid{$process_pid}->{HIGH_KSWAPD_REWAKEUP};
$perprocess{$process}->{HIGH_NR_SCANNED} += $perprocesspid{$process_pid}->{HIGH_NR_SCANNED};
$perprocess{$process}->{HIGH_NR_FILE_SCANNED} += $perprocesspid{$process_pid}->{HIGH_NR_FILE_SCANNED};
$perprocess{$process}->{HIGH_NR_ANON_SCANNED} += $perprocesspid{$process_pid}->{HIGH_NR_ANON_SCANNED};
$perprocess{$process}->{HIGH_NR_RECLAIMED} += $perprocesspid{$process_pid}->{HIGH_NR_RECLAIMED};
$perprocess{$process}->{HIGH_NR_FILE_RECLAIMED} += $perprocesspid{$process_pid}->{HIGH_NR_FILE_RECLAIMED};
$perprocess{$process}->{HIGH_NR_ANON_RECLAIMED} += $perprocesspid{$process_pid}->{HIGH_NR_ANON_RECLAIMED};
$perprocess{$process}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC} += $perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC};
$perprocess{$process}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC} += $perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC};
$perprocess{$process}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC} += $perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC};
$perprocess{$process}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC} += $perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC};
for (my $order = 0; $order < 20; $order++) {
$perprocess{$process}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN_PERORDER}[$order] += $perprocesspid{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN_PERORDER}[$order];
$perprocess{$process}->{MM_VMSCAN_WAKEUP_KSWAPD_PERORDER}[$order] += $perprocesspid{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD_PERORDER}[$order];
$perprocess{$process}->{MM_VMSCAN_KSWAPD_WAKE_PERORDER}[$order] += $perprocesspid{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE_PERORDER}[$order];
}
# Aggregate direct reclaim latencies
my $wr_index = $perprocess{$process}->{MM_VMSCAN_DIRECT_RECLAIM_END};
my $rd_index = 0;
while (defined $perprocesspid{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$rd_index]) {
$perprocess{$process}->{HIGH_DIRECT_RECLAIM_LATENCY}[$wr_index] = $perprocesspid{$process_pid}->{HIGH_DIRECT_RECLAIM_LATENCY}[$rd_index];
$rd_index++;
$wr_index++;
}
$perprocess{$process}->{MM_VMSCAN_DIRECT_RECLAIM_END} = $wr_index;
# Aggregate kswapd latencies
my $wr_index = $perprocess{$process}->{MM_VMSCAN_KSWAPD_SLEEP};
my $rd_index = 0;
while (defined $perprocesspid{$process_pid}->{HIGH_KSWAPD_LATENCY}[$rd_index]) {
$perprocess{$process}->{HIGH_KSWAPD_LATENCY}[$wr_index] = $perprocesspid{$process_pid}->{HIGH_KSWAPD_LATENCY}[$rd_index];
$rd_index++;
$wr_index++;
}
$perprocess{$process}->{MM_VMSCAN_DIRECT_RECLAIM_END} = $wr_index;
}
}
sub report() {
if (!$opt_ignorepid) {
dump_stats(\%perprocesspid);
} else {
aggregate_perprocesspid();
dump_stats(\%perprocess);
}
}
# Process events or signals until neither is available
sub signal_loop() {
my $sigint_processed;
do {
$sigint_processed = 0;
process_events();
# Handle pending signals if any
if ($sigint_pending) {
my $current_time = time;
if ($sigint_exit) {
print "Received exit signal\n";
$sigint_pending = 0;
}
if ($sigint_report) {
if ($current_time >= $sigint_received + 2) {
report();
$sigint_report = 0;
$sigint_pending = 0;
$sigint_processed = 1;
}
}
}
} while ($sigint_pending || $sigint_processed);
}
signal_loop();
report();
@@ -0,0 +1,66 @@
.. SPDX-License-Identifier: GPL-2.0
===============
Tracing Remotes
===============
:Author: Vincent Donnefort <vdonnefort@google.com>
Overview
========
Firmware and hypervisors are black boxes to the kernel. Having a way to see what
they are doing can be useful to debug both. This is where remote tracing buffers
come in. A remote tracing buffer is a ring buffer executed by the firmware or
hypervisor into memory that is memory mapped to the host kernel. This is similar
to how user space memory maps the kernel ring buffer but in this case the kernel
is acting like user space and the firmware or hypervisor is the "kernel" side.
With a trace remote ring buffer, the firmware and hypervisor can record events
for which the host kernel can see and expose to user space.
Register a remote
=================
A remote must provide a set of callbacks `struct trace_remote_callbacks` whom
description can be found below. Those callbacks allows Tracefs to enable and
disable tracing and events, to load and unload a tracing buffer (a set of
ring-buffers) and to swap a reader page with the head page, which enables
consuming reading.
.. kernel-doc:: include/linux/trace_remote.h
Once registered, an instance will appear for this remote in the Tracefs
directory **remotes/**. Buffers can then be read using the usual Tracefs files
**trace_pipe** and **trace**.
Declare a remote event
======================
Macros are provided to ease the declaration of remote events, in a similar
fashion to in-kernel events. A declaration must provide an ID, a description of
the event arguments and how to print the event:
.. code-block:: c
REMOTE_EVENT(foo, EVENT_FOO_ID,
RE_STRUCT(
re_field(u64, bar)
),
RE_PRINTK("bar=%lld", __entry->bar)
);
Then those events must be declared in a C file with the following:
.. code-block:: c
#define REMOTE_EVENT_INCLUDE_FILE foo_events.h
#include <trace/define_remote_events.h>
This will provide a `struct remote_event remote_event_foo` that can be given to
`trace_remote_register`.
Registered events appear in the remote directory under **events/**.
Simple ring-buffer
==================
A simple implementation for a ring-buffer writer can be found in
kernel/trace/simple_ring_buffer.c.
.. kernel-doc:: include/linux/simple_ring_buffer.h
@@ -0,0 +1,983 @@
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.2-no-invariants-only
===========================
Lockless Ring Buffer Design
===========================
Copyright 2009 Red Hat Inc.
:Author: Steven Rostedt <srostedt@redhat.com>
:License: The GNU Free Documentation License, Version 1.2
(dual licensed under the GPL v2)
:Reviewers: Mathieu Desnoyers, Huang Ying, Hidetoshi Seto,
and Frederic Weisbecker.
Written for: 2.6.31
Terminology used in this Document
---------------------------------
tail
- where new writes happen in the ring buffer.
head
- where new reads happen in the ring buffer.
producer
- the task that writes into the ring buffer (same as writer)
writer
- same as producer
consumer
- the task that reads from the buffer (same as reader)
reader
- same as consumer.
reader_page
- A page outside the ring buffer used solely (for the most part)
by the reader.
head_page
- a pointer to the page that the reader will use next
tail_page
- a pointer to the page that will be written to next
commit_page
- a pointer to the page with the last finished non-nested write.
cmpxchg
- hardware-assisted atomic transaction that performs the following::
A = B if previous A == C
R = cmpxchg(A, C, B) is saying that we replace A with B if and only
if current A is equal to C, and we put the old (current)
A into R
R gets the previous A regardless if A is updated with B or not.
To see if the update was successful a compare of ``R == C``
may be used.
The Generic Ring Buffer
-----------------------
The ring buffer can be used in either an overwrite mode or in
producer/consumer mode.
Producer/consumer mode is where if the producer were to fill up the
buffer before the consumer could free up anything, the producer
will stop writing to the buffer. This will lose most recent events.
Overwrite mode is where if the producer were to fill up the buffer
before the consumer could free up anything, the producer will
overwrite the older data. This will lose the oldest events.
No two writers can write at the same time (on the same per-cpu buffer),
but a writer may interrupt another writer, but it must finish writing
before the previous writer may continue. This is very important to the
algorithm. The writers act like a "stack". The way interrupts works
enforces this behavior::
writer1 start
<preempted> writer2 start
<preempted> writer3 start
writer3 finishes
writer2 finishes
writer1 finishes
This is very much like a writer being preempted by an interrupt and
the interrupt doing a write as well.
Readers can happen at any time. But no two readers may run at the
same time, nor can a reader preempt/interrupt another reader. A reader
cannot preempt/interrupt a writer, but it may read/consume from the
buffer at the same time as a writer is writing, but the reader must be
on another processor to do so. A reader may read on its own processor
and can be preempted by a writer.
A writer can preempt a reader, but a reader cannot preempt a writer.
But a reader can read the buffer at the same time (on another processor)
as a writer.
The ring buffer is made up of a list of pages held together by a linked list.
At initialization a reader page is allocated for the reader that is not
part of the ring buffer.
The head_page, tail_page and commit_page are all initialized to point
to the same page.
The reader page is initialized to have its next pointer pointing to
the head page, and its previous pointer pointing to a page before
the head page.
The reader has its own page to use. At start up time, this page is
allocated but is not attached to the list. When the reader wants
to read from the buffer, if its page is empty (like it is on start-up),
it will swap its page with the head_page. The old reader page will
become part of the ring buffer and the head_page will be removed.
The page after the inserted page (old reader_page) will become the
new head page.
Once the new page is given to the reader, the reader could do what
it wants with it, as long as a writer has left that page.
A sample of how the reader page is swapped: Note this does not
show the head page in the buffer, it is for demonstrating a swap
only.
::
+------+
|reader| RING BUFFER
|page |
+------+
+---+ +---+ +---+
| |-->| |-->| |
| |<--| |<--| |
+---+ +---+ +---+
^ | ^ |
| +-------------+ |
+-----------------+
+------+
|reader| RING BUFFER
|page |-------------------+
+------+ v
| +---+ +---+ +---+
| | |-->| |-->| |
| | |<--| |<--| |<-+
| +---+ +---+ +---+ |
| ^ | ^ | |
| | +-------------+ | |
| +-----------------+ |
+------------------------------------+
+------+
|reader| RING BUFFER
|page |-------------------+
+------+ <---------------+ v
| ^ +---+ +---+ +---+
| | | |-->| |-->| |
| | | | | |<--| |<-+
| | +---+ +---+ +---+ |
| | | ^ | |
| | +-------------+ | |
| +-----------------------------+ |
+------------------------------------+
+------+
|buffer| RING BUFFER
|page |-------------------+
+------+ <---------------+ v
| ^ +---+ +---+ +---+
| | | | | |-->| |
| | New | | | |<--| |<-+
| | Reader +---+ +---+ +---+ |
| | page ----^ | |
| | | |
| +-----------------------------+ |
+------------------------------------+
It is possible that the page swapped is the commit page and the tail page,
if what is in the ring buffer is less than what is held in a buffer page.
::
reader page commit page tail page
| | |
v | |
+---+ | |
| |<----------+ |
| |<------------------------+
| |------+
+---+ |
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
This case is still valid for this algorithm.
When the writer leaves the page, it simply goes into the ring buffer
since the reader page still points to the next location in the ring
buffer.
The main pointers:
reader page
- The page used solely by the reader and is not part
of the ring buffer (may be swapped in)
head page
- the next page in the ring buffer that will be swapped
with the reader page.
tail page
- the page where the next write will take place.
commit page
- the page that last finished a write.
The commit page only is updated by the outermost writer in the
writer stack. A writer that preempts another writer will not move the
commit page.
When data is written into the ring buffer, a position is reserved
in the ring buffer and passed back to the writer. When the writer
is finished writing data into that position, it commits the write.
Another write (or a read) may take place at anytime during this
transaction. If another write happens it must finish before continuing
with the previous write.
Write reserve::
Buffer page
+---------+
|written |
+---------+ <--- given back to writer (current commit)
|reserved |
+---------+ <--- tail pointer
| empty |
+---------+
Write commit::
Buffer page
+---------+
|written |
+---------+
|written |
+---------+ <--- next position for write (current commit)
| empty |
+---------+
If a write happens after the first reserve::
Buffer page
+---------+
|written |
+---------+ <-- current commit
|reserved |
+---------+ <--- given back to second writer
|reserved |
+---------+ <--- tail pointer
After second writer commits::
Buffer page
+---------+
|written |
+---------+ <--(last full commit)
|reserved |
+---------+
|pending |
|commit |
+---------+ <--- tail pointer
When the first writer commits::
Buffer page
+---------+
|written |
+---------+
|written |
+---------+
|written |
+---------+ <--(last full commit and tail pointer)
The commit pointer points to the last write location that was
committed without preempting another write. When a write that
preempted another write is committed, it only becomes a pending commit
and will not be a full commit until all writes have been committed.
The commit page points to the page that has the last full commit.
The tail page points to the page with the last write (before
committing).
The tail page is always equal to or after the commit page. It may
be several pages ahead. If the tail page catches up to the commit
page then no more writes may take place (regardless of the mode
of the ring buffer: overwrite and produce/consumer).
The order of pages is::
head page
commit page
tail page
Possible scenario::
tail page
head page commit page |
| | |
v v v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
There is a special case that the head page is after either the commit page
and possibly the tail page. That is when the commit (and tail) page has been
swapped with the reader page. This is because the head page is always
part of the ring buffer, but the reader page is not. Whenever there
has been less than a full page that has been committed inside the ring buffer,
and a reader swaps out a page, it will be swapping out the commit page.
::
reader page commit page tail page
| | |
v | |
+---+ | |
| |<----------+ |
| |<------------------------+
| |------+
+---+ |
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
^
|
head page
In this case, the head page will not move when the tail and commit
move back into the ring buffer.
The reader cannot swap a page into the ring buffer if the commit page
is still on that page. If the read meets the last commit (real commit
not pending or reserved), then there is nothing more to read.
The buffer is considered empty until another full commit finishes.
When the tail meets the head page, if the buffer is in overwrite mode,
the head page will be pushed ahead one. If the buffer is in producer/consumer
mode, the write will fail.
Overwrite mode::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
^
|
head page
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
^
|
head page
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
^
|
head page
Note, the reader page will still point to the previous head page.
But when a swap takes place, it will use the most recent head page.
Making the Ring Buffer Lockless:
--------------------------------
The main idea behind the lockless algorithm is to combine the moving
of the head_page pointer with the swapping of pages with the reader.
State flags are placed inside the pointer to the page. To do this,
each page must be aligned in memory by 4 bytes. This will allow the 2
least significant bits of the address to be used as flags, since
they will always be zero for the address. To get the address,
simply mask out the flags::
MASK = ~3
address & MASK
Two flags will be kept by these two bits:
HEADER
- the page being pointed to is a head page
UPDATE
- the page being pointed to is being updated by a writer
and was or is about to be a head page.
::
reader page
|
v
+---+
| |------+
+---+ |
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-H->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The above pointer "-H->" would have the HEADER flag set. That is
the next page is the next page to be swapped out by the reader.
This pointer means the next page is the head page.
When the tail page meets the head pointer, it will use cmpxchg to
change the pointer to the UPDATE state::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-H->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
"-U->" represents a pointer in the UPDATE state.
Any access to the reader will need to take some sort of lock to serialize
the readers. But the writers will never take a lock to write to the
ring buffer. This means we only need to worry about a single reader,
and writes only preempt in "stack" formation.
When the reader tries to swap the page with the ring buffer, it
will also use cmpxchg. If the flag bit in the pointer to the
head page does not have the HEADER flag set, the compare will fail
and the reader will need to look for the new head page and try again.
Note, the flags UPDATE and HEADER are never set at the same time.
The reader swaps the reader page as follows::
+------+
|reader| RING BUFFER
|page |
+------+
+---+ +---+ +---+
| |--->| |--->| |
| |<---| |<---| |
+---+ +---+ +---+
^ | ^ |
| +---------------+ |
+-----H-------------+
The reader sets the reader page next pointer as HEADER to the page after
the head page::
+------+
|reader| RING BUFFER
|page |-------H-----------+
+------+ v
| +---+ +---+ +---+
| | |--->| |--->| |
| | |<---| |<---| |<-+
| +---+ +---+ +---+ |
| ^ | ^ | |
| | +---------------+ | |
| +-----H-------------+ |
+--------------------------------------+
It does a cmpxchg with the pointer to the previous head page to make it
point to the reader page. Note that the new pointer does not have the HEADER
flag set. This action atomically moves the head page forward::
+------+
|reader| RING BUFFER
|page |-------H-----------+
+------+ v
| ^ +---+ +---+ +---+
| | | |-->| |-->| |
| | | |<--| |<--| |<-+
| | +---+ +---+ +---+ |
| | | ^ | |
| | +-------------+ | |
| +-----------------------------+ |
+------------------------------------+
After the new head page is set, the previous pointer of the head page is
updated to the reader page::
+------+
|reader| RING BUFFER
|page |-------H-----------+
+------+ <---------------+ v
| ^ +---+ +---+ +---+
| | | |-->| |-->| |
| | | | | |<--| |<-+
| | +---+ +---+ +---+ |
| | | ^ | |
| | +-------------+ | |
| +-----------------------------+ |
+------------------------------------+
+------+
|buffer| RING BUFFER
|page |-------H-----------+ <--- New head page
+------+ <---------------+ v
| ^ +---+ +---+ +---+
| | | | | |-->| |
| | New | | | |<--| |<-+
| | Reader +---+ +---+ +---+ |
| | page ----^ | |
| | | |
| +-----------------------------+ |
+------------------------------------+
Another important point: The page that the reader page points back to
by its previous pointer (the one that now points to the new head page)
never points back to the reader page. That is because the reader page is
not part of the ring buffer. Traversing the ring buffer via the next pointers
will always stay in the ring buffer. Traversing the ring buffer via the
prev pointers may not.
Note, the way to determine a reader page is simply by examining the previous
pointer of the page. If the next pointer of the previous page does not
point back to the original page, then the original page is a reader page::
+--------+
| reader | next +----+
| page |-------->| |<====== (buffer page)
+--------+ +----+
| | ^
| v | next
prev | +----+
+------------->| |
+----+
The way the head page moves forward:
When the tail page meets the head page and the buffer is in overwrite mode
and more writes take place, the head page must be moved forward before the
writer may move the tail page. The way this is done is that the writer
performs a cmpxchg to convert the pointer to the head page from the HEADER
flag to have the UPDATE flag set. Once this is done, the reader will
not be able to swap the head page from the buffer, nor will it be able to
move the head page, until the writer is finished with the move.
This eliminates any races that the reader can have on the writer. The reader
must spin, and this is why the reader cannot preempt the writer::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-H->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The following page will be made into the new head page::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
After the new head page has been set, we can set the old head page
pointer back to NORMAL::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
After the head page has been moved, the tail page may now move forward::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The above are the trivial updates. Now for the more complex scenarios.
As stated before, if enough writes preempt the first write, the
tail page may make it all the way around the buffer and meet the commit
page. At this time, we must start dropping writes (usually with some kind
of warning to the user). But what happens if the commit was still on the
reader page? The commit page is not part of the ring buffer. The tail page
must account for this::
reader page commit page
| |
v |
+---+ |
| |<----------+
| |
| |------+
+---+ |
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-H->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
^
|
tail page
If the tail page were to simply push the head page forward, the commit when
leaving the reader page would not be pointing to the correct page.
The solution to this is to test if the commit page is on the reader page
before pushing the head page. If it is, then it can be assumed that the
tail page wrapped the buffer, and we must drop new writes.
This is not a race condition, because the commit page can only be moved
by the outermost writer (the writer that was preempted).
This means that the commit will not move while a writer is moving the
tail page. The reader cannot swap the reader page if it is also being
used as the commit page. The reader can simply check that the commit
is off the reader page. Once the commit page leaves the reader page
it will never go back on it unless a reader does another swap with the
buffer page that is also the commit page.
Nested writes
-------------
In the pushing forward of the tail page we must first push forward
the head page if the head page is the next page. If the head page
is not the next page, the tail page is simply updated with a cmpxchg.
Only writers move the tail page. This must be done atomically to protect
against nested writers::
temp_page = tail_page
next_page = temp_page->next
cmpxchg(tail_page, temp_page, next_page)
The above will update the tail page if it is still pointing to the expected
page. If this fails, a nested write pushed it forward, the current write
does not need to push it::
temp page
|
v
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
Nested write comes in and moves the tail page forward::
tail page (moved by nested writer)
temp page |
| |
v v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The above would fail the cmpxchg, but since the tail page has already
been moved forward, the writer will just try again to reserve storage
on the new tail page.
But the moving of the head page is a bit more complex::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-H->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The write converts the head page pointer to UPDATE::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
But if a nested writer preempts here, it will see that the next
page is a head page, but it is also nested. It will detect that
it is nested and will save that information. The detection is the
fact that it sees the UPDATE flag instead of a HEADER or NORMAL
pointer.
The nested writer will set the new head page pointer::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
But it will not reset the update back to normal. Only the writer
that converted a pointer from HEAD to UPDATE will convert it back
to NORMAL::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
After the nested writer finishes, the outermost writer will convert
the UPDATE pointer to NORMAL::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
It can be even more complex if several nested writes came in and moved
the tail page ahead several pages::
(first writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-H->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The write converts the head page pointer to UPDATE::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
Next writer comes in, and sees the update and sets up the new
head page::
(second writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The nested writer moves the tail page forward. But does not set the old
update page to NORMAL because it is not the outermost writer::
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-H->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
Another writer preempts and sees the page after the tail page is a head page.
It changes it from HEAD to UPDATE::
(third writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-U->| |--->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The writer will move the head page forward::
(third writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-U->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
But now that the third writer did change the HEAD flag to UPDATE it
will convert it to normal::
(third writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
Then it will move the tail page, and return back to the second writer::
(second writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The second writer will fail to move the tail page because it was already
moved, so it will try again and add its data to the new tail page.
It will return to the first writer::
(first writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
The first writer cannot know atomically if the tail page moved
while it updates the HEAD page. It will then update the head page to
what it thinks is the new head page::
(first writer)
tail page
|
v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-H->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
Since the cmpxchg returns the old value of the pointer the first writer
will see it succeeded in updating the pointer from NORMAL to HEAD.
But as we can see, this is not good enough. It must also check to see
if the tail page is either where it use to be or on the next page::
(first writer)
A B tail page
| | |
v v v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |-H->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
If tail page != A and tail page != B, then it must reset the pointer
back to NORMAL. The fact that it only needs to worry about nested
writers means that it only needs to check this after setting the HEAD page::
(first writer)
A B tail page
| | |
v v v
+---+ +---+ +---+ +---+
<---| |--->| |-U->| |--->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
Now the writer can update the head page. This is also why the head page must
remain in UPDATE and only reset by the outermost writer. This prevents
the reader from seeing the incorrect head page::
(first writer)
A B tail page
| | |
v v v
+---+ +---+ +---+ +---+
<---| |--->| |--->| |--->| |-H->
--->| |<---| |<---| |<---| |<---
+---+ +---+ +---+ +---+
@@ -0,0 +1,106 @@
.. SPDX-License-Identifier: GPL-2.0
==================================
Tracefs ring-buffer memory mapping
==================================
:Author: Vincent Donnefort <vdonnefort@google.com>
Overview
========
Tracefs ring-buffer memory map provides an efficient method to stream data
as no memory copy is necessary. The application mapping the ring-buffer becomes
then a consumer for that ring-buffer, in a similar fashion to trace_pipe.
Memory mapping setup
====================
The mapping works with a mmap() of the trace_pipe_raw interface.
The first system page of the mapping contains ring-buffer statistics and
description. It is referred to as the meta-page. One of the most important
fields of the meta-page is the reader. It contains the sub-buffer ID which can
be safely read by the mapper (see ring-buffer-design.rst).
The meta-page is followed by all the sub-buffers, ordered by ascending ID. It is
therefore effortless to know where the reader starts in the mapping:
.. code-block:: c
reader_id = meta->reader->id;
reader_offset = meta->meta_page_size + reader_id * meta->subbuf_size;
When the application is done with the current reader, it can get a new one using
the trace_pipe_raw ioctl() TRACE_MMAP_IOCTL_GET_READER. This ioctl also updates
the meta-page fields.
Limitations
===========
When a mapping is in place on a Tracefs ring-buffer, it is not possible to
either resize it (either by increasing the entire size of the ring-buffer or
each subbuf). It is also not possible to use snapshot and causes splice to copy
the ring buffer data instead of using the copyless swap from the ring buffer.
Concurrent readers (either another application mapping that ring-buffer or the
kernel with trace_pipe) are allowed but not recommended. They will compete for
the ring-buffer and the output is unpredictable, just like concurrent readers on
trace_pipe would be.
Example
=======
.. code-block:: c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/trace_mmap.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#define TRACE_PIPE_RAW "/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw"
int main(void)
{
int page_size = getpagesize(), fd, reader_id;
unsigned long meta_len, data_len;
struct trace_buffer_meta *meta;
void *map, *reader, *data;
fd = open(TRACE_PIPE_RAW, O_RDONLY | O_NONBLOCK);
if (fd < 0)
exit(EXIT_FAILURE);
map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
if (map == MAP_FAILED)
exit(EXIT_FAILURE);
meta = (struct trace_buffer_meta *)map;
meta_len = meta->meta_page_size;
printf("entries: %llu\n", meta->entries);
printf("overrun: %llu\n", meta->overrun);
printf("read: %llu\n", meta->read);
printf("nr_subbufs: %u\n", meta->nr_subbufs);
data_len = meta->subbuf_size * meta->nr_subbufs;
data = mmap(NULL, data_len, PROT_READ, MAP_SHARED, fd, meta_len);
if (data == MAP_FAILED)
exit(EXIT_FAILURE);
if (ioctl(fd, TRACE_MMAP_IOCTL_GET_READER) < 0)
exit(EXIT_FAILURE);
reader_id = meta->reader.id;
reader = data + meta->subbuf_size * reader_id;
printf("Current reader address: %p\n", reader);
munmap(data, data_len);
munmap(meta, meta_len);
close (fd);
return 0;
}
@@ -0,0 +1,171 @@
Deterministic Automata Instrumentation
======================================
The RV monitor file created by dot2k, with the name "$MODEL_NAME.c"
includes a section dedicated to instrumentation.
In the example of the wip.dot monitor created on [1], it will look like::
/*
* This is the instrumentation part of the monitor.
*
* This is the section where manual work is required. Here the kernel events
* are translated into model's event.
*
*/
static void handle_preempt_disable(void *data, /* XXX: fill header */)
{
da_handle_event_wip(preempt_disable_wip);
}
static void handle_preempt_enable(void *data, /* XXX: fill header */)
{
da_handle_event_wip(preempt_enable_wip);
}
static void handle_sched_waking(void *data, /* XXX: fill header */)
{
da_handle_event_wip(sched_waking_wip);
}
static int enable_wip(void)
{
int retval;
retval = da_monitor_init_wip();
if (retval)
return retval;
rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_disable);
rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_enable);
rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_sched_waking);
return 0;
}
The comment at the top of the section explains the general idea: the
instrumentation section translates *kernel events* into the *model's
event*.
Tracing callback functions
--------------------------
The first three functions are the starting point of the callback *handler
functions* for each of the three events from the wip model. The developer
does not necessarily need to use them: they are just starting points.
Using the example of::
void handle_preempt_disable(void *data, /* XXX: fill header */)
{
da_handle_event_wip(preempt_disable_wip);
}
The preempt_disable event from the model connects directly to the
preemptirq:preempt_disable. The preemptirq:preempt_disable event
has the following signature, from include/trace/events/preemptirq.h::
TP_PROTO(unsigned long ip, unsigned long parent_ip)
Hence, the handle_preempt_disable() function will look like::
void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
In this case, the kernel event translates one to one with the automata
event, and indeed, no other change is required for this function.
The next handler function, handle_preempt_enable() has the same argument
list from the handle_preempt_disable(). The difference is that the
preempt_enable event will be used to synchronize the system to the model.
Initially, the *model* is placed in the initial state. However, the *system*
might or might not be in the initial state. The monitor cannot start
processing events until it knows that the system has reached the initial state.
Otherwise, the monitor and the system could be out-of-sync.
Looking at the automata definition, it is possible to see that the system
and the model are expected to return to the initial state after the
preempt_enable execution. Hence, it can be used to synchronize the
system and the model at the initialization of the monitoring section.
The start is informed via a special handle function, the
"da_handle_start_event_$(MONITOR_NAME)(event)", in this case::
da_handle_start_event_wip(preempt_enable_wip);
So, the callback function will look like::
void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_start_event_wip(preempt_enable_wip);
}
Finally, the "handle_sched_waking()" will look like::
void handle_sched_waking(void *data, struct task_struct *task)
{
da_handle_event_wip(sched_waking_wip);
}
And the explanation is left for the reader as an exercise.
enable and disable functions
----------------------------
dot2k automatically creates two special functions::
enable_$(MONITOR_NAME)()
disable_$(MONITOR_NAME)()
These functions are called when the monitor is enabled and disabled,
respectively.
They should be used to *attach* and *detach* the instrumentation to the running
system. The developer must add to the relative function all that is needed to
*attach* and *detach* its monitor to the system.
For the wip case, these functions were named::
enable_wip()
disable_wip()
But no change was required because: by default, these functions *attach* and
*detach* the tracepoints_to_attach, which was enough for this case.
Instrumentation helpers
-----------------------
To complete the instrumentation, the *handler functions* need to be attached to a
kernel event, at the monitoring enable phase.
The RV interface also facilitates this step. For example, the macro "rv_attach_trace_probe()"
is used to connect the wip model events to the relative kernel event. dot2k automatically
adds "rv_attach_trace_probe()" function call for each model event in the enable phase, as
a suggestion.
For example, from the wip sample model::
static int enable_wip(void)
{
int retval;
retval = da_monitor_init_wip();
if (retval)
return retval;
rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_enable);
rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_sched_waking);
rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_disable);
return 0;
}
The probes then need to be detached at the disable phase.
[1] The wip model is presented in:
Documentation/trace/rv/deterministic_automata.rst
The wip monitor is presented in:
Documentation/trace/rv/monitor_synthesis.rst
@@ -0,0 +1,184 @@
Deterministic Automata
======================
Formally, a deterministic automaton, denoted by G, is defined as a quintuple:
*G* = { *X*, *E*, *f*, x\ :subscript:`0`, X\ :subscript:`m` }
where:
- *X* is the set of states;
- *E* is the finite set of events;
- x\ :subscript:`0` is the initial state;
- X\ :subscript:`m` (subset of *X*) is the set of marked (or final) states.
- *f* : *X* x *E* -> *X* is the transition function. It defines the state
transition in the occurrence of an event from *E* in the state *X*. In the
special case of deterministic automata, the occurrence of the event in *E*
in a state in *X* has a deterministic next state from *X*.
For example, a given automaton named 'wip' (wakeup in preemptive) can
be defined as:
- *X* = { ``preemptive``, ``non_preemptive``}
- *E* = { ``preempt_enable``, ``preempt_disable``, ``sched_waking``}
- x\ :subscript:`0` = ``preemptive``
- X\ :subscript:`m` = {``preemptive``}
- *f* =
- *f*\ (``preemptive``, ``preempt_disable``) = ``non_preemptive``
- *f*\ (``non_preemptive``, ``sched_waking``) = ``non_preemptive``
- *f*\ (``non_preemptive``, ``preempt_enable``) = ``preemptive``
One of the benefits of this formal definition is that it can be presented
in multiple formats. For example, using a *graphical representation*, using
vertices (nodes) and edges, which is very intuitive for *operating system*
practitioners, without any loss.
The previous 'wip' automaton can also be represented as::
preempt_enable
+---------------------------------+
v |
#============# preempt_disable +------------------+
--> H preemptive H -----------------> | non_preemptive |
#============# +------------------+
^ |
| sched_waking |
+--------------+
Deterministic Automaton in C
----------------------------
In the paper "Efficient formal verification for the Linux kernel",
the authors present a simple way to represent an automaton in C that can
be used as regular code in the Linux kernel.
For example, the 'wip' automata can be presented as (augmented with comments)::
/* enum representation of X (set of states) to be used as index */
enum states {
preemptive = 0,
non_preemptive,
state_max
};
#define INVALID_STATE state_max
/* enum representation of E (set of events) to be used as index */
enum events {
preempt_disable = 0,
preempt_enable,
sched_waking,
event_max
};
struct automaton {
char *state_names[state_max]; // X: the set of states
char *event_names[event_max]; // E: the finite set of events
unsigned char function[state_max][event_max]; // f: transition function
unsigned char initial_state; // x_0: the initial state
bool final_states[state_max]; // X_m: the set of marked states
};
struct automaton aut = {
.state_names = {
"preemptive",
"non_preemptive"
},
.event_names = {
"preempt_disable",
"preempt_enable",
"sched_waking"
},
.function = {
{ non_preemptive, INVALID_STATE, INVALID_STATE },
{ INVALID_STATE, preemptive, non_preemptive },
},
.initial_state = preemptive,
.final_states = { 1, 0 },
};
The *transition function* is represented as a matrix of states (lines) and
events (columns), and so the function *f* : *X* x *E* -> *X* can be solved
in O(1). For example::
next_state = automaton_wip.function[curr_state][event];
Graphviz .dot format
--------------------
The Graphviz open-source tool can produce the graphical representation
of an automaton using the (textual) DOT language as the source code.
The DOT format is widely used and can be converted to many other formats.
For example, this is the 'wip' model in DOT::
digraph state_automaton {
{node [shape = circle] "non_preemptive"};
{node [shape = plaintext, style=invis, label=""] "__init_preemptive"};
{node [shape = doublecircle] "preemptive"};
{node [shape = circle] "preemptive"};
"__init_preemptive" -> "preemptive";
"non_preemptive" [label = "non_preemptive"];
"non_preemptive" -> "non_preemptive" [ label = "sched_waking" ];
"non_preemptive" -> "preemptive" [ label = "preempt_enable" ];
"preemptive" [label = "preemptive"];
"preemptive" -> "non_preemptive" [ label = "preempt_disable" ];
{ rank = min ;
"__init_preemptive";
"preemptive";
}
}
This DOT format can be transformed into a bitmap or vectorial image
using the dot utility, or into an ASCII art using graph-easy. For
instance::
$ dot -Tsvg -o wip.svg wip.dot
$ graph-easy wip.dot > wip.txt
dot2c
-----
dot2c is a utility that can parse a .dot file containing an automaton as
in the example above and automatically convert it to the C representation
presented in [3].
For example, having the previous 'wip' model into a file named 'wip.dot',
the following command will transform the .dot file into the C
representation (previously shown) in the 'wip.h' file::
$ dot2c wip.dot > wip.h
The 'wip.h' content is the code sample in section 'Deterministic Automaton
in C'.
Remarks
-------
The automata formalism allows modeling discrete event systems (DES) in
multiple formats, suitable for different applications/users.
For example, the formal description using set theory is better suitable
for automata operations, while the graphical format for human interpretation;
and computer languages for machine execution.
References
----------
Many textbooks cover automata formalism. For a brief introduction see::
O'Regan, Gerard. Concise guide to software engineering. Springer,
Cham, 2017.
For a detailed description, including operations, and application on Discrete
Event Systems (DES), see::
Cassandras, Christos G., and Stephane Lafortune, eds. Introduction to discrete
event systems. Boston, MA: Springer US, 2008.
For the C representation in kernel, see::
De Oliveira, Daniel Bristot; Cucinotta, Tommaso; De Oliveira, Romulo
Silva. Efficient formal verification for the Linux kernel. In:
International Conference on Software Engineering and Formal Methods.
Springer, Cham, 2019. p. 315-332.
@@ -0,0 +1,341 @@
Hybrid Automata
===============
Hybrid automata are an extension of deterministic automata, there are several
definitions of hybrid automata in the literature. The adaptation implemented
here is formally denoted by G and defined as a 7-tuple:
*G* = { *X*, *E*, *V*, *f*, x\ :subscript:`0`, X\ :subscript:`m`, *i* }
- *X* is the set of states;
- *E* is the finite set of events;
- *V* is the finite set of environment variables;
- x\ :subscript:`0` is the initial state;
- X\ :subscript:`m` (subset of *X*) is the set of marked (or final) states.
- *f* : *X* x *E* x *C(V)* -> *X* is the transition function.
It defines the state transition in the occurrence of an event from *E* in the
state *X*. Unlike deterministic automata, the transition function also
includes guards from the set of all possible constraints (defined as *C(V)*).
Guards can be true or false with the valuation of *V* when the event occurs,
and the transition is possible only when constraints are true. Similarly to
deterministic automata, the occurrence of the event in *E* in a state in *X*
has a deterministic next state from *X*, if the guard is true.
- *i* : *X* -> *C'(V)* is the invariant assignment function, this is a
constraint assigned to each state in *X*, every state in *X* must be left
before the invariant turns to false. We can omit the representation of
invariants whose value is true regardless of the valuation of *V*.
The set of all possible constraints *C(V)* is defined according to the
following grammar:
g = v < c | v > c | v <= c | v >= c | v == c | v != c | g && g | true
With v a variable in *V* and c a numerical value.
We define the special case of hybrid automata whose variables grow with uniform
rates as timed automata. In this case, the variables are called clocks.
As the name implies, timed automata can be used to describe real time.
Additionally, clocks support another type of guard which always evaluates to true:
reset(v)
The reset constraint is used to set the value of a clock to 0.
The set of invariant constraints *C'(V)* is a subset of *C(V)* including only
constraint of the form:
g = v < c | true
This simplifies the implementation as a clock expiration is a necessary and
sufficient condition for the violation of invariants while still allowing more
complex constraints to be specified as guards.
It is important to note that any hybrid automaton is a valid deterministic
automaton with additional guards and invariants. Those can only further
constrain what transitions are valid but it is not possible to define
transition functions starting from the same state in *X* and the same event in
*E* but ending up in different states in *X* based on the valuation of *V*.
Examples
--------
Wip as hybrid automaton
~~~~~~~~~~~~~~~~~~~~~~~
The 'wip' (wakeup in preemptive) example introduced as a deterministic automaton
can also be described as:
- *X* = { ``any_thread_running`` }
- *E* = { ``sched_waking`` }
- *V* = { ``preemptive`` }
- x\ :subscript:`0` = ``any_thread_running``
- X\ :subscript:`m` = {``any_thread_running``}
- *f* =
- *f*\ (``any_thread_running``, ``sched_waking``, ``preemptive==0``) = ``any_thread_running``
- *i* =
- *i*\ (``any_thread_running``) = ``true``
Which can be represented graphically as::
|
|
v
#====================# sched_waking;preemptive==0
H H ------------------------------+
H any_thread_running H |
H H <-----------------------------+
#====================#
In this example, by using the preemptive state of the system as an environment
variable, we can assert this constraint on ``sched_waking`` without requiring
preemption events (as we would in a deterministic automaton), which can be
useful in case those events are not available or not reliable on the system.
Since all the invariants in *i* are true, we can omit them from the representation.
Stall model with guards (iteration 1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As a sample timed automaton we can define 'stall' as:
- *X* = { ``dequeued``, ``enqueued``, ``running``}
- *E* = { ``enqueue``, ``dequeue``, ``switch_in``}
- *V* = { ``clk`` }
- x\ :subscript:`0` = ``dequeue``
- X\ :subscript:`m` = {``dequeue``}
- *f* =
- *f*\ (``enqueued``, ``switch_in``, ``clk < threshold``) = ``running``
- *f*\ (``running``, ``dequeue``) = ``dequeued``
- *f*\ (``dequeued``, ``enqueue``, ``reset(clk)``) = ``enqueued``
- *i* = *omitted as all true*
Graphically represented as::
|
|
v
#============================#
H dequeued H <+
#============================# |
| |
| enqueue; reset(clk) |
v |
+----------------------------+ |
| enqueued | | dequeue
+----------------------------+ |
| |
| switch_in; clk < threshold |
v |
+----------------------------+ |
| running | -+
+----------------------------+
This model imposes that the time between when a task is enqueued (it becomes
runnable) and when the task gets to run must be lower than a certain threshold.
A failure in this model means that the task is starving.
One problem in using guards on the edges in this case is that the model will
not report a failure until the ``switch_in`` event occurs. This means that,
according to the model, it is valid for the task never to run.
Stall model with invariants (iteration 2)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The first iteration isn't exactly what was intended, we can change the model as:
- *X* = { ``dequeued``, ``enqueued``, ``running``}
- *E* = { ``enqueue``, ``dequeue``, ``switch_in``}
- *V* = { ``clk`` }
- x\ :subscript:`0` = ``dequeue``
- X\ :subscript:`m` = {``dequeue``}
- *f* =
- *f*\ (``enqueued``, ``switch_in``) = ``running``
- *f*\ (``running``, ``dequeue``) = ``dequeued``
- *f*\ (``dequeued``, ``enqueue``, ``reset(clk)``) = ``enqueued``
- *i* =
- *i*\ (``enqueued``) = ``clk < threshold``
Graphically::
|
|
v
#=========================#
H dequeued H <+
#=========================# |
| |
| enqueue; reset(clk) |
v |
+-------------------------+ |
| enqueued | |
| clk < threshold | | dequeue
+-------------------------+ |
| |
| switch_in |
v |
+-------------------------+ |
| running | -+
+-------------------------+
In this case, we moved the guard as an invariant to the ``enqueued`` state,
this means we not only forbid the occurrence of ``switch_in`` when ``clk`` is
past the threshold but also mark as invalid in case we are *still* in
``enqueued`` after the threshold. This model is effectively in an invalid state
as soon as a task is starving, rather than when the starving task finally runs.
Hybrid Automaton in C
---------------------
The definition of hybrid automata in C is heavily based on the deterministic
automata one. Specifically, we add the set of environment variables and the
constraints (both guards on transitions and invariants on states) as follows.
This is a combination of both iterations of the stall example::
/* enum representation of X (set of states) to be used as index */
enum states {
dequeued,
enqueued,
running,
state_max,
};
#define INVALID_STATE state_max
/* enum representation of E (set of events) to be used as index */
enum events {
dequeue,
enqueue,
switch_in,
event_max,
};
/* enum representation of V (set of environment variables) to be used as index */
enum envs {
clk,
env_max,
env_max_stored = env_max,
};
struct automaton {
char *state_names[state_max]; // X: the set of states
char *event_names[event_max]; // E: the finite set of events
char *env_names[env_max]; // V: the finite set of env vars
unsigned char function[state_max][event_max]; // f: transition function
unsigned char initial_state; // x_0: the initial state
bool final_states[state_max]; // X_m: the set of marked states
};
struct automaton aut = {
.state_names = {
"dequeued",
"enqueued",
"running",
},
.event_names = {
"dequeue",
"enqueue",
"switch_in",
},
.env_names = {
"clk",
},
.function = {
{ INVALID_STATE, enqueued, INVALID_STATE },
{ INVALID_STATE, INVALID_STATE, running },
{ dequeued, INVALID_STATE, INVALID_STATE },
},
.initial_state = dequeued,
.final_states = { 1, 0, 0 },
};
static bool verify_constraint(enum states curr_state, enum events event,
enum states next_state)
{
bool res = true;
/* Validate guards as part of f */
if (curr_state == enqueued && event == switch_in)
res = get_env(clk) < threshold;
else if (curr_state == dequeued && event == enqueue)
reset_env(clk);
/* Validate invariants in i */
if (next_state == curr_state || !res)
return res;
if (next_state == enqueued)
ha_start_timer_jiffy(ha_mon, clk, threshold_jiffies);
else if (curr_state == enqueued)
res = !ha_cancel_timer(ha_mon);
return res;
}
The function ``verify_constraint``, here reported as simplified, checks guards,
performs resets and starts timers to validate invariants according to
specification, those cannot easily be represented in the automaton struct.
Due to the complex nature of environment variables, the user needs to provide
functions to get and reset environment variables that are not common clocks
(e.g. clocks with ns or jiffy granularity).
Since invariants are only defined as clock expirations (e.g. *clk <
threshold*), reaching the expiration of a timer armed when entering the state
is in fact a failure in the model and triggers a reaction. Leaving the state
stops the timer.
It is important to note that timers implemented with hrtimers introduce
overhead, if the monitor has several instances (e.g. all tasks) this can become
an issue. The impact can be decreased using the timer wheel (``HA_TIMER_TYPE``
set to ``HA_TIMER_WHEEL``), this lowers the responsiveness of the timer without
damaging the accuracy of the model, since the invariant condition is checked
before disabling the timer in case the callback is late.
Alternatively, if the monitor is guaranteed to *eventually* leave the state and
the incurred delay to wait for the next event is acceptable, guards can be used
in place of invariants, as seen in the stall example.
Graphviz .dot format
--------------------
Also the Graphviz representation of hybrid automata is an extension of the
deterministic automata one. Specifically, guards can be provided in the event
name separated by ``;``::
"state_start" -> "state_dest" [ label = "sched_waking;preemptible==0;reset(clk)" ];
Invariant can be specified in the state label (not the node name!) separated by ``\n``::
"enqueued" [label = "enqueued\nclk < threshold_jiffies"];
Constraints can be specified as valid C comparisons and allow spaces, the first
element of the comparison must be the clock while the second is a numerical or
parametrised value. Guards allow comparisons to be combined with boolean
operations (``&&`` and ``||``), resets must be separated from other constraints.
This is the full example of the last version of the 'stall' model in DOT::
digraph state_automaton {
{node [shape = circle] "enqueued"};
{node [shape = plaintext, style=invis, label=""] "__init_dequeued"};
{node [shape = doublecircle] "dequeued"};
{node [shape = circle] "running"};
"__init_dequeued" -> "dequeued";
"enqueued" [label = "enqueued\nclk < threshold_jiffies"];
"running" [label = "running"];
"dequeued" [label = "dequeued"];
"enqueued" -> "running" [ label = "switch_in" ];
"running" -> "dequeued" [ label = "dequeue" ];
"dequeued" -> "enqueued" [ label = "enqueue;reset(clk)" ];
{ rank = min ;
"__init_dequeued";
"dequeued";
}
}
References
----------
One book covering model checking and timed automata is::
Christel Baier and Joost-Pieter Katoen: Principles of Model Checking,
The MIT Press, 2008.
Hybrid automata are described in detail in::
Thomas Henzinger: The theory of hybrid automata,
Proceedings 11th Annual IEEE Symposium on Logic in Computer Science, 1996.
@@ -0,0 +1,20 @@
====================
Runtime Verification
====================
.. toctree::
:maxdepth: 2
:glob:
runtime-verification.rst
deterministic_automata.rst
linear_temporal_logic.rst
hybrid_automata.rst
monitor_synthesis.rst
da_monitor_instrumentation.rst
monitor_wip.rst
monitor_wwnr.rst
monitor_sched.rst
monitor_rtapp.rst
monitor_stall.rst
monitor_deadline.rst
@@ -0,0 +1,134 @@
Linear temporal logic
=====================
Introduction
------------
Runtime verification monitor is a verification technique which checks that the
kernel follows a specification. It does so by using tracepoints to monitor the
kernel's execution trace, and verifying that the execution trace sastifies the
specification.
Initially, the specification can only be written in the form of deterministic
automaton (DA). However, while attempting to implement DA monitors for some
complex specifications, deterministic automaton is found to be inappropriate as
the specification language. The automaton is complicated, hard to understand,
and error-prone.
Thus, RV monitors based on linear temporal logic (LTL) are introduced. This type
of monitor uses LTL as specification instead of DA. For some cases, writing the
specification as LTL is more concise and intuitive.
Many materials explain LTL in details. One book is::
Christel Baier and Joost-Pieter Katoen: Principles of Model Checking, The MIT
Press, 2008.
Grammar
-------
Unlike some existing syntax, kernel's implementation of LTL is more verbose.
This is motivated by considering that the people who read the LTL specifications
may not be well-versed in LTL.
Grammar:
ltl ::= opd | ( ltl ) | ltl binop ltl | unop ltl
Operands (opd):
true, false, user-defined names consisting of upper-case characters, digits,
and underscore.
Unary Operators (unop):
always
eventually
next
not
Binary Operators (binop):
until
and
or
imply
equivalent
This grammar is ambiguous: operator precedence is not defined. Parentheses must
be used.
Example linear temporal logic
-----------------------------
.. code-block::
RAIN imply (GO_OUTSIDE imply HAVE_UMBRELLA)
means: if it is raining, going outside means having an umbrella.
.. code-block::
RAIN imply (WET until not RAIN)
means: if it is raining, it is going to be wet until the rain stops.
.. code-block::
RAIN imply eventually not RAIN
means: if it is raining, rain will eventually stop.
The above examples are referring to the current time instance only. For kernel
verification, the `always` operator is usually desirable, to specify that
something is always true at the present and for all future. For example::
always (RAIN imply eventually not RAIN)
means: *all* rain eventually stops.
In the above examples, `RAIN`, `GO_OUTSIDE`, `HAVE_UMBRELLA` and `WET` are the
"atomic propositions".
Monitor synthesis
-----------------
To synthesize an LTL into a kernel monitor, the `rvgen` tool can be used:
`tools/verification/rvgen`. The specification needs to be provided as a file,
and it must have a "RULE = LTL" assignment. For example::
RULE = always (ACQUIRE imply ((not KILLED and not CRASHED) until RELEASE))
which says: if `ACQUIRE`, then `RELEASE` must happen before `KILLED` or
`CRASHED`.
The LTL can be broken down using sub-expressions. The above is equivalent to:
.. code-block::
RULE = always (ACQUIRE imply (ALIVE until RELEASE))
ALIVE = not KILLED and not CRASHED
From this specification, `rvgen` generates the C implementation of a Buchi
automaton - a non-deterministic state machine which checks the satisfiability of
the LTL. See Documentation/trace/rv/monitor_synthesis.rst for details on using
`rvgen`.
References
----------
One book covering model checking and linear temporal logic is::
Christel Baier and Joost-Pieter Katoen: Principles of Model Checking, The MIT
Press, 2008.
For an example of using linear temporal logic in software testing, see::
Ruijie Meng, Zhen Dong, Jialin Li, Ivan Beschastnikh, and Abhik Roychoudhury.
2022. Linear-time temporal logic guided greybox fuzzing. In Proceedings of the
44th International Conference on Software Engineering (ICSE '22). Association
for Computing Machinery, New York, NY, USA, 1343–1355.
https://doi.org/10.1145/3510003.3510082
The kernel's LTL monitor implementation is based on::
Gerth, R., Peled, D., Vardi, M.Y., Wolper, P. (1996). Simple On-the-fly
Automatic Verification of Linear Temporal Logic. In: Dembiński, P., Średniawa,
M. (eds) Protocol Specification, Testing and Verification XV. PSTV 1995. IFIP
Advances in Information and Communication Technology. Springer, Boston, MA.
https://doi.org/10.1007/978-0-387-34892-6_1
@@ -0,0 +1,84 @@
Deadline monitors
=================
- Name: deadline
- Type: container for multiple monitors
- Author: Gabriele Monaco <gmonaco@redhat.com>
Description
-----------
The deadline monitor is a set of specifications to describe the deadline
scheduler behaviour. It includes monitors per scheduling entity (deadline tasks
and servers) that work independently to verify different specifications the
deadline scheduler should follow.
Specifications
--------------
Monitor nomiss
~~~~~~~~~~~~~~
The nomiss monitor ensures dl entities get to run *and* run to completion
before their deadline, although deferrable servers may not run. An entity is
considered done if ``throttled``, either because it yielded or used up its
runtime, or when it voluntarily starts ``sleeping``.
The monitor includes a user configurable deadline threshold. If the total
utilisation of deadline tasks is larger than 1, they are only guaranteed
bounded tardiness. See Documentation/scheduler/sched-deadline.rst for more
details. The threshold (module parameter ``nomiss.deadline_thresh``) can be
configured to avoid the monitor to fail based on the acceptable tardiness in
the system. Since ``dl_throttle`` is a valid outcome for the entity to be done,
the minimum tardiness needs be 1 tick to consider the throttle delay, unless
the ``HRTICK_DL`` scheduler feature is active.
Servers have also an intermediate ``idle`` state, occurring as soon as no
runnable task is available from ready or running where no timing constraint
is applied. A server goes to sleep by stopping, there is no wakeup equivalent
as the order of a server starting and replenishing is not defined, hence a
server can run from sleeping without being ready::
|
sched_wakeup v
dl_replenish;reset(clk) -- #=========================#
| H H dl_replenish;reset(clk)
+-----------> H H <--------------------+
H H |
+- dl_server_stop ---- H ready H |
| +-----------------> H clk < DEADLINE_NS() H dl_throttle; |
| | H H is_defer == 1 |
| | sched_switch_in - H H -----------------+ |
| | | #=========================# | |
| | | | ^ | |
| | | dl_server_idle dl_replenish;reset(clk) | |
| | | v | | |
| | | +--------------+ | |
| | | +------ | | | |
| | | dl_server_idle | | dl_throttle | |
| | | | | idle | -----------------+ | |
| | | +-----> | | | | |
| | | | | | | |
| | | | | | | |
+--+--+---+--- dl_server_stop -- +--------------+ | | |
| | | | | ^ | | |
| | | | sched_switch_in dl_server_idle | | |
| | | | v | | | |
| | | | +---------- +---------------------+ | | |
| | | | sched_switch_in | | | | |
| | | | sched_wakeup | | | | |
| | | | dl_replenish; | running | -------+ | | |
| | | | reset(clk) | clk < DEADLINE_NS() | | | | |
| | | | +---------> | | dl_throttle | | |
| | | +----------------> | | | | | |
| | | +---------------------+ | | | |
| | sched_wakeup ^ sched_switch_suspend | | | |
v v dl_replenish;reset(clk) | dl_server_stop | | | |
+--------------+ | | v v v |
| | - sched_switch_in + | +---------------+
| | <---------------------+ dl_throttle +-- | |
| sleeping | sched_wakeup | | throttled |
| | -- dl_server_stop dl_server_idle +-> | |
| | dl_server_idle sched_switch_suspend +---------------+
+--------------+ <---------+ ^
| |
+------ dl_throttle;is_constr_dl == 1 || is_defer == 1 ------+
@@ -0,0 +1,133 @@
Real-time application monitors
==============================
- Name: rtapp
- Type: container for multiple monitors
- Author: Nam Cao <namcao@linutronix.de>
Description
-----------
Real-time applications may have design flaws such that they experience
unexpected latency and fail to meet their time requirements. Often, these flaws
follow a few patterns:
- Page faults: A real-time thread may access memory that does not have a
mapped physical backing or must first be copied (such as for copy-on-write).
Thus a page fault is raised and the kernel must first perform the expensive
action. This causes significant delays to the real-time thread
- Priority inversion: A real-time thread blocks waiting for a lower-priority
thread. This causes the real-time thread to effectively take on the
scheduling priority of the lower-priority thread. For example, the real-time
thread needs to access a shared resource that is protected by a
non-pi-mutex, but the mutex is currently owned by a non-real-time thread.
The `rtapp` monitor detects these patterns. It aids developers to identify
reasons for unexpected latency with real-time applications. It is a container of
multiple sub-monitors described in the following sections.
Monitor pagefault
+++++++++++++++++
The `pagefault` monitor reports real-time tasks raising page faults. Its
specification is::
RULE = always (RT imply not PAGEFAULT)
To fix warnings reported by this monitor, `mlockall()` or `mlock()` can be used
to ensure physical backing for memory.
This monitor may have false negatives because the pages used by the real-time
threads may just happen to be directly available during testing. To minimize
this, the system can be put under memory pressure (e.g. invoking the OOM killer
using a program that does `ptr = malloc(SIZE_OF_RAM); memset(ptr, 0,
SIZE_OF_RAM);`) so that the kernel executes aggressive strategies to recycle as
much physical memory as possible.
Monitor sleep
+++++++++++++
The `sleep` monitor reports real-time threads sleeping in a manner that may
cause undesirable latency. Real-time applications should only put a real-time
thread to sleep for one of the following reasons:
- Cyclic work: real-time thread sleeps waiting for the next cycle. For this
case, only the `clock_nanosleep` syscall should be used with `TIMER_ABSTIME`
(to avoid time drift) and `CLOCK_MONOTONIC` (to avoid the clock being
changed). No other method is safe for real-time. For example, threads
waiting for timerfd can be woken by softirq which provides no real-time
guarantee.
- Real-time thread waiting for something to happen (e.g. another thread
releasing shared resources, or a completion signal from another thread). In
this case, only futexes (FUTEX_LOCK_PI, FUTEX_LOCK_PI2 or one of
FUTEX_WAIT_*) should be used. Applications usually do not use futexes
directly, but use PI mutexes and PI condition variables which are built on
top of futexes. Be aware that the C library might not implement conditional
variables as safe for real-time. As an alternative, the librtpi library
exists to provide a conditional variable implementation that is correct for
real-time applications in Linux.
Beside the reason for sleeping, the eventual waker should also be
real-time-safe. Namely, one of:
- An equal-or-higher-priority thread
- Hard interrupt handler
- Non-maskable interrupt handler
This monitor's warning usually means one of the following:
- Real-time thread is blocked by a non-real-time thread (e.g. due to
contention on a mutex without priority inheritance). This is priority
inversion.
- Time-critical work waits for something which is not safe for real-time (e.g.
timerfd).
- The work executed by the real-time thread does not need to run at real-time
priority at all. This is not a problem for the real-time thread itself, but
it is potentially taking the CPU away from other important real-time work.
Application developers may purposely choose to have their real-time application
sleep in a way that is not safe for real-time. It is debatable whether that is a
problem. Application developers must analyze the warnings to make a proper
assessment.
The monitor's specification is::
RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
and ((not WAKE) until RT_FRIENDLY_WAKE)
RT_VALID_SLEEP_REASON = FUTEX_WAIT
or RT_FRIENDLY_NANOSLEEP
RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
and NANOSLEEP_TIMER_ABSTIME
and NANOSLEEP_CLOCK_MONOTONIC
RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
or WOKEN_BY_HARDIRQ
or WOKEN_BY_NMI
or KTHREAD_SHOULD_STOP
ALLOWLIST = BLOCK_ON_RT_MUTEX
or FUTEX_LOCK_PI
or TASK_IS_RCU
or TASK_IS_MIGRATION
Beside the scenarios described above, this specification also handle some
special cases:
- `KERNEL_THREAD`: kernel tasks do not have any pattern that can be recognized
as valid real-time sleeping reasons. Therefore sleeping reason is not
checked for kernel tasks.
- `KTHREAD_SHOULD_STOP`: a non-real-time thread may stop a real-time kernel
thread by waking it and waiting for it to exit (`kthread_stop()`). This
wakeup is safe for real-time.
- `ALLOWLIST`: to handle known false positives with the kernel.
- `BLOCK_ON_RT_MUTEX` is included in the allowlist due to its implementation.
In the release path of rt_mutex, a boosted task is de-boosted before waking
the rt_mutex's waiter. Consequently, the monitor may see a real-time-unsafe
wakeup (e.g. non-real-time task waking real-time task). This is actually
real-time-safe because preemption is disabled for the duration.
- `FUTEX_LOCK_PI` is included in the allowlist for the same reason as
`BLOCK_ON_RT_MUTEX`.
@@ -0,0 +1,371 @@
Scheduler monitors
==================
- Name: sched
- Type: container for multiple monitors
- Author: Gabriele Monaco <gmonaco@redhat.com>, Daniel Bristot de Oliveira <bristot@kernel.org>
Description
-----------
Monitors describing complex systems, such as the scheduler, can easily grow to
the point where they are just hard to understand because of the many possible
state transitions.
Often it is possible to break such descriptions into smaller monitors,
sharing some or all events. Enabling those smaller monitors concurrently is,
in fact, testing the system as if we had one single larger monitor.
Splitting models into multiple specification is not only easier to
understand, but gives some more clues when we see errors.
The sched monitor is a set of specifications to describe the scheduler behaviour.
It includes several per-cpu and per-task monitors that work independently to verify
different specifications the scheduler should follow.
To make this system as straightforward as possible, sched specifications are *nested*
monitors, whereas sched itself is a *container*.
From the interface perspective, sched includes other monitors as sub-directories,
enabling/disabling or setting reactors to sched, propagates the change to all monitors,
however single monitors can be used independently as well.
It is important that future modules are built after their container (sched, in
this case), otherwise the linker would not respect the order and the nesting
wouldn't work as expected.
To do so, simply add them after sched in the Makefile.
Specifications
--------------
The specifications included in sched are currently a work in progress, adapting the ones
defined by Daniel Bristot in [1]_.
Currently we included the following:
Monitor sco
~~~~~~~~~~~
The scheduling context operations (sco) monitor ensures changes in a task state
happen only in thread context::
|
|
v
sched_set_state +------------------+
+------------------ | |
| | thread_context |
+-----------------> | | <+
+------------------+ |
| |
| schedule_entry | schedule_exit
v |
|
scheduling_context -+
Monitor snroc
~~~~~~~~~~~~~
The set non runnable on its own context (snroc) monitor ensures changes in a
task state happens only in the respective task's context. This is a per-task
monitor::
|
|
v
+------------------+
| other_context | <+
+------------------+ |
| |
| sched_switch_in | sched_switch_out
v |
sched_set_state |
+------------------ |
| own_context |
+-----------------> -+
Monitor scpd
~~~~~~~~~~~~
The schedule called with preemption disabled (scpd) monitor ensures schedule is
called with preemption disabled::
|
|
v
+------------------+
| cant_sched | <+
+------------------+ |
| |
| preempt_disable | preempt_enable
v |
schedule_entry |
schedule_exit |
+----------------- can_sched |
| |
+----------------> -+
Monitor snep
~~~~~~~~~~~~
The schedule does not enable preempt (snep) monitor ensures a schedule call
does not enable preemption::
|
|
v
preempt_disable +------------------------+
preempt_enable | |
+------------------ | non_scheduling_context |
| | |
+-----------------> | | <+
+------------------------+ |
| |
| schedule_entry | schedule_exit
v |
|
scheduling_contex -+
Monitor sts
~~~~~~~~~~~
The schedule implies task switch (sts) monitor ensures a task switch happens
only in scheduling context and up to once, as well as scheduling occurs with
interrupts enabled but no task switch can happen before interrupts are
disabled. When the next task picked for execution is the same as the previously
running one, no real task switch occurs but interrupts are disabled nonetheless::
irq_entry |
+----+ |
v | v
+------------+ irq_enable #===================# irq_disable
| | ------------> H H irq_entry
| cant_sched | <------------ H H irq_enable
| | irq_disable H can_sched H --------------+
+------------+ H H |
H H |
+---------------> H H <-------------+
| #===================#
| |
schedule_exit | schedule_entry
| v
| +-------------------+ irq_enable
| | scheduling | <---------------+
| +-------------------+ |
| | |
| | irq_disable +--------+ irq_entry
| v | | --------+
| +-------------------+ irq_entry | in_irq | |
| | | -----------> | | <-------+
| | disable_to_switch | +--------+
| | | --+
| +-------------------+ |
| | |
| | sched_switch |
| v |
| +-------------------+ |
| | switching | | irq_enable
| +-------------------+ |
| | |
| | irq_enable |
| v |
| +-------------------+ |
+-- | enable_to_exit | <-+
+-------------------+
^ | irq_disable
| | irq_entry
+---------------+ irq_enable
Monitor nrp
-----------
The need resched preempts (nrp) monitor ensures preemption requires
``need_resched``. Only kernel preemption is considered, since preemption
while returning to userspace, for this monitor, is indistinguishable from
``sched_switch_yield`` (described in the sssw monitor).
A kernel preemption is whenever ``__schedule`` is called with the preemption
flag set to true (e.g. from preempt_enable or exiting from interrupts). This
type of preemption occurs after the need for ``rescheduling`` has been set.
This is not valid for the *lazy* variant of the flag, which causes only
userspace preemption.
A ``schedule_entry_preempt`` may involve a task switch or not, in the latter
case, a task goes through the scheduler from a preemption context but it is
picked as the next task to run. Since the scheduler runs, this clears the need
to reschedule. The ``any_thread_running`` state does not imply the monitored
task is not running as this monitor does not track the outcome of scheduling.
In theory, a preemption can only occur after the ``need_resched`` flag is set. In
practice, however, it is possible to see a preemption where the flag is not
set. This can happen in one specific condition::
need_resched
preempt_schedule()
preempt_schedule_irq()
__schedule()
!need_resched
__schedule()
In the situation above, standard preemption starts (e.g. from preempt_enable
when the flag is set), an interrupt occurs before scheduling and, on its exit
path, it schedules, which clears the ``need_resched`` flag.
When the preempted task runs again, the standard preemption started earlier
resumes, although the flag is no longer set. The monitor considers this a
``nested_preemption``, this allows another preemption without re-setting the
flag. This condition relaxes the monitor constraints and may catch false
negatives (i.e. no real ``nested_preemptions``) but makes the monitor more
robust and able to validate other scenarios.
For simplicity, the monitor starts in ``preempt_irq``, although no interrupt
occurred, as the situation above is hard to pinpoint::
schedule_entry
irq_entry #===========================================#
+-------------------------- H H
| H H
+-------------------------> H any_thread_running H
H H
+-------------------------> H H
| #===========================================#
| schedule_entry | ^
| schedule_entry_preempt | sched_need_resched | schedule_entry
| | schedule_entry_preempt
| v |
| +----------------------+ |
| +--- | | |
| sched_need_resched | | rescheduling | -+
| +--> | |
| +----------------------+
| | irq_entry
| v
| +----------------------+
| | | ---+
| ---> | | | sched_need_resched
| | preempt_irq | | irq_entry
| | | <--+
| | | <--+
| +----------------------+ |
| | schedule_entry | sched_need_resched
| | schedule_entry_preempt |
| v |
| +-----------------------+ |
+-------------------------- | nested_preempt | --+
+-----------------------+
^ irq_entry |
+-------------------+
Due to how the ``need_resched`` flag on the preemption count works on arm64,
this monitor is unstable on that architecture, as it often records preemption
when the flag is not set, even in presence of the workaround above.
For the time being, the monitor is disabled by default on arm64.
Monitor sssw
------------
The set state sleep and wakeup (sssw) monitor ensures ``set_state`` to
sleepable leads to sleeping and sleeping tasks require wakeup. It includes the
following types of switch:
* ``switch_suspend``:
a task puts itself to sleep, this can happen only after explicitly setting
the task to ``sleepable``. After a task is suspended, it needs to be woken up
(``waking`` state) before being switched in again.
Setting the task's state to ``sleepable`` can be reverted before switching if it
is woken up or set to ``runnable``.
* ``switch_blocking``:
a special case of a ``switch_suspend`` where the task is waiting on a
sleeping RT lock (``PREEMPT_RT`` only), it is common to see wakeup and set
state events racing with each other and this leads the model to perceive this
type of switch when the task is not set to sleepable. This is a limitation of
the model in SMP system and workarounds may slow down the system.
* ``switch_preempt``:
a task switch as a result of kernel preemption (``schedule_entry_preempt`` in
the nrp model).
* ``switch_yield``:
a task explicitly calls the scheduler or is preempted while returning to
userspace. It can happen after a ``yield`` system call, from the idle task or
if the ``need_resched`` flag is set. By definition, a task cannot yield while
``sleepable`` as that would be a suspension. A special case of a yield occurs
when a task in ``TASK_INTERRUPTIBLE`` calls the scheduler while a signal is
pending. The task doesn't go through the usual blocking/waking and is set
back to runnable, the resulting switch (if there) looks like a yield to the
``signal_wakeup`` state and is followed by the signal delivery. From this
state, the monitor expects a signal even if it sees a wakeup event, although
not necessary, to rule out false negatives.
This monitor doesn't include a running state, ``sleepable`` and ``runnable``
are only referring to the task's desired state, which could be scheduled out
(e.g. due to preemption). However, it does include the event
``sched_switch_in`` to represent when a task is allowed to become running. This
can be triggered also by preemption, but cannot occur after the task got to
``sleeping`` before a ``wakeup`` occurs::
+--------------------------------------------------------------------------+
| |
| |
| switch_suspend | |
| switch_blocking | |
v v |
+----------+ #==========================# set_state_runnable |
| | H H wakeup |
| | H H switch_in |
| | H H switch_yield |
| sleeping | H H switch_preempt |
| | H H signal_deliver |
| | switch_ H H ------+ |
| | _blocking H runnable H | |
| | <----------- H H <-----+ |
+----------+ H H |
| wakeup H H |
+---------------------> H H |
H H |
+---------> H H |
| #==========================# |
| | ^ |
| | | set_state_runnable |
| | | wakeup |
| set_state_sleepable | +------------------------+
| v | |
| +--------------------------+ set_state_sleepable
| | | switch_in
| | | switch_preempt
signal_deliver | sleepable | signal_deliver
| | | ------+
| | | |
| | | <-----+
| +--------------------------+
| | ^
| switch_yield | set_state_sleepable
| v |
| +---------------+ |
+---------- | signal_wakeup | -+
+---------------+
^ | switch_in
| | switch_preempt
| | switch_yield
+-----------+ wakeup
Monitor opid
------------
The operations with preemption and irq disabled (opid) monitor ensures
operations like ``wakeup`` and ``need_resched`` occur with interrupts and
preemption disabled.
``need_resched`` can be set by some RCU internals functions, in which case it
doesn't match a task wakeup and might occur with only interrupts disabled.
The interrupt and preemption status are validated by the hybrid automaton
constraints when processing the events::
|
|
v
#=========# sched_need_resched;irq_off == 1
H H sched_waking;irq_off == 1 && preempt_off == 1
H any H ------------------------------------------------+
H H |
H H <-----------------------------------------------+
#=========#
References
----------
.. [1] Daniel Bristot de Oliveira et al.:
`A thread synchronization model for the PREEMPT_RT Linux kernel
<https://www.iris.sssup.it/bitstream/11382/533630/1/Elsevier-JSA-2020.pdf>`_,
J. Syst. Archit., 2020.
@@ -0,0 +1,43 @@
Monitor stall
=============
- Name: stall - stalled task monitor
- Type: per-task hybrid automaton
- Author: Gabriele Monaco <gmonaco@redhat.com>
Description
-----------
The stalled task (stall) monitor is a sample per-task timed monitor that checks
if tasks are scheduled within a defined threshold after they are ready::
|
|
v
#==========================#
+-----------------> H dequeued H
| #==========================#
| |
sched_switch_wait | sched_wakeup;reset(clk)
| v
| +--------------------------+ <+
| | enqueued | | sched_wakeup
| | clk < threshold_jiffies | -+
| +--------------------------+
| | ^
| sched_switch_in sched_switch_preempt;reset(clk)
| v |
| +--------------------------+
+------------------ | running |
+--------------------------+
^ sched_switch_in |
| sched_wakeup |
+----------------------+
The threshold can be configured as a parameter by either booting with the
``stall.threshold_jiffies=<new value>`` argument or writing a new value to
``/sys/module/stall/parameters/threshold_jiffies``.
Specification
-------------
Graphviz Dot file in tools/verification/models/stall.dot
@@ -0,0 +1,382 @@
Runtime Verification Monitor Synthesis
======================================
The starting point for the application of runtime verification (RV) techniques
is the *specification* or *modeling* of the desired (or undesired) behavior
of the system under scrutiny.
The formal representation needs to be then *synthesized* into a *monitor*
that can then be used in the analysis of the trace of the system. The
*monitor* connects to the system via an *instrumentation* that converts
the events from the *system* to the events of the *specification*.
In Linux terms, the runtime verification monitors are encapsulated inside
the *RV monitor* abstraction. The RV monitor includes a set of instances
of the monitor (per-cpu monitor, per-task monitor, and so on), the helper
functions that glue the monitor to the system reference model, and the
trace output as a reaction to event parsing and exceptions, as depicted
below::
Linux +---- RV Monitor ----------------------------------+ Formal
Realm | | Realm
+-------------------+ +----------------+ +-----------------+
| Linux kernel | | Monitor | | Reference |
| Tracing | -> | Instance(s) | <- | Model |
| (instrumentation) | | (verification) | | (specification) |
+-------------------+ +----------------+ +-----------------+
| | |
| V |
| +----------+ |
| | Reaction | |
| +--+--+--+-+ |
| | | | |
| | | +-> trace output ? |
+------------------------|--|----------------------+
| +----> panic ?
+-------> <user-specified>
RV monitor synthesis
--------------------
The synthesis of a specification into the Linux *RV monitor* abstraction is
automated by the rvgen tool and the header file containing common code for
creating monitors. The header files are:
* rv/da_monitor.h for deterministic automaton monitor.
* rv/ltl_monitor.h for linear temporal logic monitor.
* rv/ha_monitor.h for hybrid automaton monitor.
rvgen
-----
The rvgen utility converts a specification into the C presentation and creating
the skeleton of a kernel monitor in C.
For example, it is possible to transform the wip.dot model present in
[1] into a per-cpu monitor with the following command::
$ rvgen monitor -c da -s wip.dot -t per_cpu
This will create a directory named wip/ with the following files:
- wip.h: the wip model in C
- wip.c: the RV monitor
The wip.c file contains the monitor declaration and the starting point for
the system instrumentation.
Similarly, a linear temporal logic monitor can be generated with the following
command::
$ rvgen monitor -c ltl -s pagefault.ltl -t per_task
This generates pagefault/ directory with:
- pagefault.h: The Buchi automaton (the non-deterministic state machine to
verify the specification)
- pagefault.c: The skeleton for the RV monitor
Monitor header files
--------------------
The header files:
- `rv/da_monitor.h` for deterministic automaton monitor
- `rv/ltl_monitor` for linear temporal logic monitor
include common macros and static functions for implementing *Monitor
Instance(s)*.
The benefits of having all common functionalities in a single header file are
3-fold:
- Reduce the code duplication;
- Facilitate the bug fix/improvement;
- Avoid the case of developers changing the core of the monitor code to
manipulate the model in a (let's say) non-standard way.
rv/da_monitor.h
+++++++++++++++
This initial implementation presents three different types of monitor instances:
- ``#define RV_MON_TYPE RV_MON_GLOBAL``
- ``#define RV_MON_TYPE RV_MON_PER_CPU``
- ``#define RV_MON_TYPE RV_MON_PER_TASK``
The first sets up functions declaration for a global deterministic automata
monitor, the second for monitors with per-cpu instances, and the third with
per-task instances.
In all cases, the C file must include the $(MODEL_NAME).h file (generated by
`rvgen`), for example, to define the per-cpu 'wip' monitor, the `wip.c` source
file must include::
#define RV_MON_TYPE RV_MON_PER_CPU
#include "wip.h"
#include <rv/da_monitor.h>
The monitor is executed by sending events to be processed via the functions
presented below::
da_handle_event($(event from event enum));
da_handle_start_event($(event from event enum));
da_handle_start_run_event($(event from event enum));
The function ``da_handle_event()`` is the regular case where
the event will be processed if the monitor is processing events.
When a monitor is enabled, it is placed in the initial state of the automata.
However, the monitor does not know if the system is in the *initial state*.
The ``da_handle_start_event()`` function is used to notify the
monitor that the system is returning to the initial state, so the monitor can
start monitoring the next event.
The ``da_handle_start_run_event()`` function is used to notify
the monitor that the system is known to be in the initial state, so the
monitor can start monitoring and monitor the current event.
Using the wip model as example, the events "preempt_disable" and
"sched_waking" should be sent to monitor, respectively, via [2]::
da_handle_event(preempt_disable_wip);
da_handle_event(sched_waking_wip);
While the event "preempt_enabled" will use::
da_handle_start_event(preempt_enable_wip);
To notify the monitor that the system will be returning to the initial state,
so the system and the monitor should be in sync.
rv/ltl_monitor.h
++++++++++++++++
This file must be combined with the $(MODEL_NAME).h file (generated by `rvgen`)
to be complete. For example, for the `pagefault` monitor, the `pagefault.c`
source file must include::
#include "pagefault.h"
#include <rv/ltl_monitor.h>
(the skeleton monitor file generated by `rvgen` already does this).
`$(MODEL_NAME).h` (`pagefault.h` in the above example) includes the
implementation of the Buchi automaton - a non-deterministic state machine that
verifies the LTL specification. While `rv/ltl_monitor.h` includes the common
helper functions to interact with the Buchi automaton and to implement an RV
monitor. An important definition in `$(MODEL_NAME).h` is::
enum ltl_atom {
LTL_$(FIRST_ATOMIC_PROPOSITION),
LTL_$(SECOND_ATOMIC_PROPOSITION),
...
LTL_NUM_ATOM
};
which is the list of atomic propositions present in the LTL specification
(prefixed with "LTL\_" to avoid name collision). This `enum` is passed to the
functions interacting with the Buchi automaton.
While generating code, `rvgen` cannot understand the meaning of the atomic
propositions. Thus, that task is left for manual work. The recommended practice
is adding tracepoints to places where the atomic propositions change; and in the
tracepoints' handlers: the Buchi automaton is executed using::
void ltl_atom_update(struct task_struct *task, enum ltl_atom atom, bool value)
which tells the Buchi automaton that the atomic proposition `atom` is now
`value`. The Buchi automaton checks whether the LTL specification is still
satisfied, and invokes the monitor's error tracepoint and the reactor if
violation is detected.
Tracepoints and `ltl_atom_update()` should be used whenever possible. However,
it is sometimes not the most convenient. For some atomic propositions which are
changed in multiple places in the kernel, it is cumbersome to trace all those
places. Furthermore, it may not be important that the atomic propositions are
updated at precise times. For example, considering the following linear temporal
logic::
RULE = always (RT imply not PAGEFAULT)
This LTL states that a real-time task does not raise page faults. For this
specification, it is not important when `RT` changes, as long as it has the
correct value when `PAGEFAULT` is true. Motivated by this case, another
function is introduced::
void ltl_atom_fetch(struct task_struct *task, struct ltl_monitor *mon)
This function is called whenever the Buchi automaton is triggered. Therefore, it
can be manually implemented to "fetch" `RT`::
void ltl_atom_fetch(struct task_struct *task, struct ltl_monitor *mon)
{
ltl_atom_set(mon, LTL_RT, rt_task(task));
}
Effectively, whenever `PAGEFAULT` is updated with a call to `ltl_atom_update()`,
`RT` is also fetched. Thus, the LTL specification can be verified without
tracing `RT` everywhere.
For atomic propositions which act like events, they usually need to be set (or
cleared) and then immediately cleared (or set). A convenient function is
provided::
void ltl_atom_pulse(struct task_struct *task, enum ltl_atom atom, bool value)
which is equivalent to::
ltl_atom_update(task, atom, value);
ltl_atom_update(task, atom, !value);
To initialize the atomic propositions, the following function must be
implemented::
ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
This function is called for all running tasks when the monitor is enabled. It is
also called for new tasks created after the enabling the monitor. It should
initialize as many atomic propositions as possible, for example::
void ltl_atom_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
{
ltl_atom_set(mon, LTL_RT, rt_task(task));
if (task_creation)
ltl_atom_set(mon, LTL_PAGEFAULT, false);
}
Atomic propositions not initialized by `ltl_atom_init()` will stay in the
unknown state until relevant tracepoints are hit, which can take some time. As
monitoring for a task cannot be done until all atomic propositions is known for
the task, the monitor may need some time to start validating tasks which have
been running before the monitor is enabled. Therefore, it is recommended to
start the tasks of interest after enabling the monitor.
rv/ha_monitor.h
+++++++++++++++
The implementation of hybrid automaton monitors derives directly from the
deterministic automaton one. Despite using a different header
(``ha_monitor.h``) the functions to handle events are the same (e.g.
``da_handle_event``).
Additionally, the `rvgen` tool populates skeletons for the
``ha_verify_constraint``, ``ha_get_env`` and ``ha_reset_env`` based on the
monitor specification in the monitor source file.
``ha_verify_constraint`` is typically ready as it is generated by `rvgen`:
* standard constraints on edges are turned into the form::
res = ha_get_env(ha_mon, ENV) < VALUE;
* reset constraints are turned into the form::
ha_reset_env(ha_mon, ENV);
* constraints on the state are implemented using timers
- armed before entering the state
- cancelled while entering any other state
- untouched if the state does not change as a result of the event
- checked if the timer expired but the callback did not run
- available implementation are `HA_TIMER_HRTIMER` and `HA_TIMER_WHEEL`
- hrtimers are more precise but may have higher overhead
- select by defining `HA_TIMER_TYPE` before including the header::
#define HA_TIMER_TYPE HA_TIMER_HRTIMER
Constraint values can be specified in different forms:
* literal value (with optional unit). E.g.::
preemptive == 0
clk < 100ns
threshold <= 10j
* constant value (uppercase string). E.g.::
clk < MAX_NS
* parameter (lowercase string). E.g.::
clk <= threshold_jiffies
* macro (uppercase string with parentheses). E.g.::
clk < MAX_NS()
* function (lowercase string with parentheses). E.g.::
clk <= threshold_jiffies()
In all cases, `rvgen` will try to understand the type of the environment
variable from the name or unit. For instance, constants or parameters
terminating with ``_NS`` or ``_jiffies`` are intended as clocks with ns and jiffy
granularity, respectively. Literals with measure unit `j` are jiffies and if a
time unit is specified (`ns` to `s`), `rvgen` will convert the value to `ns`.
Constants need to be defined by the user (but unlike the name, they don't
necessarily need to be defined as constants). Parameters get converted to
module parameters and the user needs to provide a default value.
Also function and macros are defined by the user, by default they get as an
argument the ``ha_monitor``, a common usage would be to get the required value
from the target, e.g. the task in per-task monitors, using the helper
``ha_get_target(ha_mon)``.
If `rvgen` determines that the variable is a clock, it provides the getter and
resetter based on the unit. Otherwise, the user needs to provide an appropriate
definition.
Typically non-clock environment variables are not reset. In such case only the
getter skeleton will be present in the file generated by `rvgen`.
For instance, the getter for preemptive can be filled as::
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env)
{
if (env == preemptible)
return preempt_count() == 0;
return ENV_INVALID_VALUE;
}
The function is supplied the ``ha_mon`` parameter in case some storage is
required (as it is for clocks), but environment variables without reset do not
require a storage and can ignore that argument.
The number of environment variables requiring a storage is limited by
``MAX_HA_ENV_LEN``, however such limitation doesn't stand for other variables.
Finally, constraints on states are only valid for clocks and only if the
constraint is of the form `clk < N`. This is because such constraints are
implemented with the expiration of a timer.
Typically the clock variables are reset just before arming the timer, but this
doesn't have to be the case and the available functions take care of it.
It is a responsibility of per-task monitors to make sure no timer is left
running when the task exits.
By default the generator implements timers with hrtimers (setting
``HA_TIMER_TYPE`` to ``HA_TIMER_HRTIMER``), this gives better responsiveness
but higher overhead. The timer wheel (``HA_TIMER_WHEEL``) is a good alternative
for monitors with several instances (e.g. per-task) that achieves lower
overhead with increased latency, yet without compromising precision.
Final remarks
-------------
With the monitor synthesis in place using the header files and
rvgen, the developer's work should be limited to the instrumentation
of the system, increasing the confidence in the overall approach.
[1] For details about deterministic automata format and the translation
from one representation to another, see::
Documentation/trace/rv/deterministic_automata.rst
[2] rvgen appends the monitor's name suffix to the events enums to
avoid conflicting variables when exporting the global vmlinux.h
use by BPF programs.
@@ -0,0 +1,55 @@
Monitor wip
===========
- Name: wip - wakeup in preemptive
- Type: per-cpu deterministic automaton
- Author: Daniel Bristot de Oliveira <bristot@kernel.org>
Description
-----------
The wakeup in preemptive (wip) monitor is a sample per-cpu monitor
that verifies if the wakeup events always take place with
preemption disabled::
|
|
v
#==================#
H preemptive H <+
#==================# |
| |
| preempt_disable | preempt_enable
v |
sched_waking +------------------+ |
+--------------- | | |
| | non_preemptive | |
+--------------> | | -+
+------------------+
The wakeup event always takes place with preemption disabled because
of the scheduler synchronization. However, because the preempt_count
and its trace event are not atomic with regard to interrupts, some
inconsistencies might happen. For example::
preempt_disable() {
__preempt_count_add(1)
-------> smp_apic_timer_interrupt() {
preempt_disable()
do not trace (preempt count >= 1)
wake up a thread
preempt_enable()
do not trace (preempt count >= 1)
}
<------
trace_preempt_disable();
}
This problem was reported and discussed here:
https://lore.kernel.org/r/cover.1559051152.git.bristot@redhat.com/
Specification
-------------
Grapviz Dot file in tools/verification/models/wip.dot
@@ -0,0 +1,45 @@
Monitor wwnr
============
- Name: wwrn - wakeup while not running
- Type: per-task deterministic automaton
- Author: Daniel Bristot de Oliveira <bristot@kernel.org>
Description
-----------
This is a per-task sample monitor, with the following
definition::
|
|
v
wakeup +-------------+
+--------- | |
| | not_running |
+--------> | | <+
+-------------+ |
| |
| switch_in | switch_out
v |
+-------------+ |
| running | -+
+-------------+
This model is broken, the reason is that a task can be running
in the processor without being set as RUNNABLE. Think about a
task about to sleep::
1: set_current_state(TASK_UNINTERRUPTIBLE);
2: schedule();
And then imagine an IRQ happening in between the lines one and two,
waking the task up. BOOM, the wakeup will happen while the task is
running.
- Why do we need this model, so?
- To test the reactors.
Specification
-------------
Grapviz Dot file in tools/verification/models/wwnr.dot
@@ -0,0 +1,231 @@
====================
Runtime Verification
====================
Runtime Verification (RV) is a lightweight (yet rigorous) method that
complements classical exhaustive verification techniques (such as *model
checking* and *theorem proving*) with a more practical approach for complex
systems.
Instead of relying on a fine-grained model of a system (e.g., a
re-implementation at instruction level), RV works by analyzing the trace of the
system's actual execution, comparing it against a formal specification of
the system behavior.
The main advantage is that RV can give precise information on the runtime
behavior of the monitored system, without the pitfalls of developing models
that require a re-implementation of the entire system in a modeling language.
Moreover, given an efficient monitoring method, it is possible to execute an
*online* verification of a system, enabling the *reaction* for unexpected
events, avoiding, for example, the propagation of a failure on safety-critical
systems.
Runtime Monitors and Reactors
=============================
A monitor is the central part of the runtime verification of a system. The
monitor stands in between the formal specification of the desired (or
undesired) behavior, and the trace of the actual system.
In Linux terms, the runtime verification monitors are encapsulated inside the
*RV monitor* abstraction. A *RV monitor* includes a reference model of the
system, a set of instances of the monitor (per-cpu monitor, per-task monitor,
and so on), and the helper functions that glue the monitor to the system via
trace, as depicted below::
Linux +---- RV Monitor ----------------------------------+ Formal
Realm | | Realm
+-------------------+ +----------------+ +-----------------+
| Linux kernel | | Monitor | | Reference |
| Tracing | -> | Instance(s) | <- | Model |
| (instrumentation) | | (verification) | | (specification) |
+-------------------+ +----------------+ +-----------------+
| | |
| V |
| +----------+ |
| | Reaction | |
| +--+--+--+-+ |
| | | | |
| | | +-> trace output ? |
+------------------------|--|----------------------+
| +----> panic ?
+-------> <user-specified>
In addition to the verification and monitoring of the system, a monitor can
react to an unexpected event. The forms of reaction can vary from logging the
event occurrence to the enforcement of the correct behavior to the extreme
action of taking a system down to avoid the propagation of a failure.
In Linux terms, a *reactor* is an reaction method available for *RV monitors*.
By default, all monitors should provide a trace output of their actions,
which is already a reaction. In addition, other reactions will be available
so the user can enable them as needed.
For further information about the principles of runtime verification and
RV applied to Linux:
Bartocci, Ezio, et al. *Introduction to runtime verification.* In: Lectures on
Runtime Verification. Springer, Cham, 2018. p. 1-33.
Falcone, Ylies, et al. *A taxonomy for classifying runtime verification tools.*
In: International Conference on Runtime Verification. Springer, Cham, 2018. p.
241-262.
De Oliveira, Daniel Bristot. *Automata-based formal analysis and
verification of the real-time Linux kernel.* Ph.D. Thesis, 2020.
Online RV monitors
==================
Monitors can be classified as *offline* and *online* monitors. *Offline*
monitor process the traces generated by a system after the events, generally by
reading the trace execution from a permanent storage system. *Online* monitors
process the trace during the execution of the system. Online monitors are said
to be *synchronous* if the processing of an event is attached to the system
execution, blocking the system during the event monitoring. On the other hand,
an *asynchronous* monitor has its execution detached from the system. Each type
of monitor has a set of advantages. For example, *offline* monitors can be
executed on different machines but require operations to save the log to a
file. In contrast, *synchronous online* method can react at the exact moment
a violation occurs.
Another important aspect regarding monitors is the overhead associated with the
event analysis. If the system generates events at a frequency higher than the
monitor's ability to process them in the same system, only the *offline*
methods are viable. On the other hand, if the tracing of the events incurs
on higher overhead than the simple handling of an event by a monitor, then a
*synchronous online* monitors will incur on lower overhead.
Indeed, the research presented in:
De Oliveira, Daniel Bristot; Cucinotta, Tommaso; De Oliveira, Romulo Silva.
*Efficient formal verification for the Linux kernel.* In: International
Conference on Software Engineering and Formal Methods. Springer, Cham, 2019.
p. 315-332.
Shows that for Deterministic Automata models, the synchronous processing of
events in-kernel causes lower overhead than saving the same events to the trace
buffer, not even considering collecting the trace for user-space analysis.
This motivated the development of an in-kernel interface for online monitors.
For further information about modeling of Linux kernel behavior using automata,
see:
De Oliveira, Daniel B.; De Oliveira, Romulo S.; Cucinotta, Tommaso. *A thread
synchronization model for the PREEMPT_RT Linux kernel.* Journal of Systems
Architecture, 2020, 107: 101729.
The user interface
==================
The user interface resembles the tracing interface (on purpose). It is
currently at "/sys/kernel/tracing/rv/".
The following files/folders are currently available:
**available_monitors**
- Reading list the available monitors, one per line
For example::
# cat available_monitors
wip
wwnr
**available_reactors**
- Reading shows the available reactors, one per line.
For example::
# cat available_reactors
nop
panic
printk
**enabled_monitors**:
- Reading lists the enabled monitors, one per line
- Writing to it enables a given monitor
- Writing a monitor name with a '!' prefix disables it
- Truncating the file disables all enabled monitors
For example::
# cat enabled_monitors
# echo wip > enabled_monitors
# echo wwnr >> enabled_monitors
# cat enabled_monitors
wip
wwnr
# echo '!wip' >> enabled_monitors
# cat enabled_monitors
wwnr
# echo > enabled_monitors
# cat enabled_monitors
#
Note that it is possible to enable more than one monitor concurrently.
**monitoring_on**
This is an on/off general switcher for monitoring. It resembles the
"tracing_on" switcher in the trace interface.
- Writing "0" stops the monitoring
- Writing "1" continues the monitoring
- Reading returns the current status of the monitoring
Note that it does not disable enabled monitors but stop the per-entity
monitors monitoring the events received from the system.
**reacting_on**
- Writing "0" prevents reactions for happening
- Writing "1" enable reactions
- Reading returns the current status of the reaction
**monitors/**
Each monitor will have its own directory inside "monitors/". There the
monitor-specific files will be presented. The "monitors/" directory resembles
the "events" directory on tracefs.
For example::
# cd monitors/wip/
# ls
desc enable
# cat desc
wakeup in preemptive per-cpu testing monitor.
# cat enable
0
**monitors/MONITOR/desc**
- Reading shows a description of the monitor *MONITOR*
**monitors/MONITOR/enable**
- Writing "0" disables the *MONITOR*
- Writing "1" enables the *MONITOR*
- Reading return the current status of the *MONITOR*
**monitors/MONITOR/reactors**
- List available reactors, with the select reaction for the given *MONITOR*
inside "[]". The default one is the nop (no operation) reactor.
- Writing the name of a reactor enables it to the given MONITOR.
For example::
# cat monitors/wip/reactors
[nop]
panic
printk
# echo panic > monitors/wip/reactors
# cat monitors/wip/reactors
nop
[panic]
printk
+143
View File
@@ -0,0 +1,143 @@
.. SPDX-License-Identifier: GPL-2.0
===================
System Trace Module
===================
System Trace Module (STM) is a device described in MIPI STP specs as
STP trace stream generator. STP (System Trace Protocol) is a trace
protocol multiplexing data from multiple trace sources, each one of
which is assigned a unique pair of master and channel. While some of
these masters and channels are statically allocated to certain
hardware trace sources, others are available to software. Software
trace sources are usually free to pick for themselves any
master/channel combination from this pool.
On the receiving end of this STP stream (the decoder side), trace
sources can only be identified by master/channel combination, so in
order for the decoder to be able to make sense of the trace that
involves multiple trace sources, it needs to be able to map those
master/channel pairs to the trace sources that it understands.
For instance, it is helpful to know that syslog messages come on
master 7 channel 15, while arbitrary user applications can use masters
48 to 63 and channels 0 to 127.
To solve this mapping problem, stm class provides a policy management
mechanism via configfs, that allows defining rules that map string
identifiers to ranges of masters and channels. If these rules (policy)
are consistent with what decoder expects, it will be able to properly
process the trace data.
This policy is a tree structure containing rules (policy_node) that
have a name (string identifier) and a range of masters and channels
associated with it, located in "stp-policy" subsystem directory in
configfs. The topmost directory's name (the policy) is formatted as
the STM device name to which this policy applies and an arbitrary
string identifier separated by a stop. From the example above, a rule
may look like this::
$ ls /config/stp-policy/dummy_stm.my-policy/user
channels masters
$ cat /config/stp-policy/dummy_stm.my-policy/user/masters
48 63
$ cat /config/stp-policy/dummy_stm.my-policy/user/channels
0 127
which means that the master allocation pool for this rule consists of
masters 48 through 63 and channel allocation pool has channels 0
through 127 in it. Now, any producer (trace source) identifying itself
with "user" identification string will be allocated a master and
channel from within these ranges.
These rules can be nested, for example, one can define a rule "dummy"
under "user" directory from the example above and this new rule will
be used for trace sources with the id string of "user/dummy".
Trace sources have to open the stm class device's node and write their
trace data into its file descriptor.
In order to find an appropriate policy node for a given trace source,
several mechanisms can be used. First, a trace source can explicitly
identify itself by calling an STP_POLICY_ID_SET ioctl on the character
device's file descriptor, providing their id string, before they write
any data there. Secondly, if they chose not to perform the explicit
identification (because you may not want to patch existing software
to do this), they can just start writing the data, at which point the
stm core will try to find a policy node with the name matching the
task's name (e.g., "syslogd") and if one exists, it will be used.
Thirdly, if the task name can't be found among the policy nodes, the
catch-all entry "default" will be used, if it exists. This entry also
needs to be created and configured by the system administrator or
whatever tools are taking care of the policy configuration. Finally,
if all the above steps failed, the write() to an stm file descriptor
will return a error (EINVAL).
Previously, if no policy nodes were found for a trace source, the stm
class would silently fall back to allocating the first available
contiguous range of master/channels from the beginning of the device's
master/channel range. The new requirement for a policy node to exist
will help programmers and sysadmins identify gaps in configuration
and have better control over the un-identified sources.
Some STM devices may allow direct mapping of the channel mmio regions
to userspace for zero-copy writing. One mappable page (in terms of
mmu) will usually contain multiple channels' mmios, so the user will
need to allocate that many channels to themselves (via the
aforementioned ioctl() call) to be able to do this. That is, if your
stm device's channel mmio region is 64 bytes and hardware page size is
4096 bytes, after a successful STP_POLICY_ID_SET ioctl() call with
width==64, you should be able to mmap() one page on this file
descriptor and obtain direct access to an mmio region for 64 channels.
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
[2].
stm_source
==========
For kernel-based trace sources, there is "stm_source" device
class. Devices of this class can be connected and disconnected to/from
stm devices at runtime via a sysfs attribute called "stm_source_link"
by writing the name of the desired stm device there, for example::
$ echo dummy_stm.0 > /sys/class/stm_source/console/stm_source_link
For examples on how to use stm_source interface in the kernel, refer
to stm_console, stm_heartbeat or stm_ftrace drivers.
Each stm_source device will need to assume a master and a range of
channels, depending on how many channels it requires. These are
allocated for the device according to the policy configuration. If
there's a node in the root of the policy directory that matches the
stm_source device's name (for example, "console"), this node will be
used to allocate master and channel numbers. If there's no such policy
node, the stm core will use the catch-all entry "default", if one
exists. If neither policy nodes exist, the write() to stm_source_link
will return an error.
stm_console
===========
One implementation of this interface also used in the example above is
the "stm_console" driver, which basically provides a one-way console
for kernel messages over an stm device.
To configure the master/channel pair that will be assigned to this
console in the STP stream, create a "console" policy entry (see the
beginning of this text on how to do that). When initialized, it will
consume one channel.
stm_ftrace
==========
This is another "stm_source" device, once the stm_ftrace has been
linked with an stm device, and if "function" tracer is enabled,
function address and parent function address which Ftrace subsystem
would store into ring buffer will be exported via the stm device at
the same time.
Currently only Ftrace "function" tracer is supported.
* [1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
* [2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html
+62
View File
@@ -0,0 +1,62 @@
.. SPDX-License-Identifier: GPL-2.0
===================
MIPI SyS-T over STP
===================
The MIPI SyS-T protocol driver can be used with STM class devices to
generate standardized trace stream. Aside from being a standard, it
provides better trace source identification and timestamp correlation.
In order to use the MIPI SyS-T protocol driver with your STM device,
first, you'll need CONFIG_STM_PROTO_SYS_T.
Now, you can select which protocol driver you want to use when you create
a policy for your STM device, by specifying it in the policy name:
# mkdir /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/
In other words, the policy name format is extended like this:
<device_name>:<protocol_name>.<policy_name>
With Intel TH, therefore it can look like "0-sth:p_sys-t.my-policy".
If the protocol name is omitted, the STM class will chose whichever
protocol driver was loaded first.
You can also double check that everything is working as expected by
# cat /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/protocol
p_sys-t
Now, with the MIPI SyS-T protocol driver, each policy node in the
configfs gets a few additional attributes, which determine per-source
parameters specific to the protocol:
# mkdir /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/default
# ls /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/default
channels
clocksync_interval
do_len
masters
ts_interval
uuid
The most important one here is the "uuid", which determines the UUID
that will be used to tag all data coming from this source. It is
automatically generated when a new node is created, but it is likely
that you would want to change it.
do_len switches on/off the additional "payload length" field in the
MIPI SyS-T message header. It is off by default as the STP already
marks message boundaries.
ts_interval and clocksync_interval determine how much time in milliseconds
can pass before we need to include a protocol (not transport, aka STP)
timestamp in a message header or send a CLOCKSYNC packet, respectively.
See Documentation/ABI/testing/configfs-stp-policy-p_sys-t for more
details.
* [1] https://www.mipi.org/specifications/sys-t
@@ -0,0 +1,260 @@
###############
Timerlat tracer
###############
The timerlat tracer aims to help the preemptive kernel developers to
find sources of wakeup latencies of real-time threads. Like cyclictest,
the tracer sets a periodic timer that wakes up a thread. The thread then
computes a *wakeup latency* value as the difference between the *current
time* and the *absolute time* that the timer was set to expire. The main
goal of timerlat is tracing in such a way to help kernel developers.
Usage
-----
Write the ASCII text "timerlat" into the current_tracer file of the
tracing system (generally mounted at /sys/kernel/tracing).
For example::
[root@f32 ~]# cd /sys/kernel/tracing/
[root@f32 tracing]# echo timerlat > current_tracer
It is possible to follow the trace by reading the trace file::
[root@f32 tracing]# cat trace
# tracer: timerlat
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# || /
# |||| ACTIVATION
# TASK-PID CPU# |||| TIMESTAMP ID CONTEXT LATENCY
# | | | |||| | | | |
<idle>-0 [000] d.h1 54.029328: #1 context irq timer_latency 932 ns
<...>-867 [000] .... 54.029339: #1 context thread timer_latency 11700 ns
<idle>-0 [001] dNh1 54.029346: #1 context irq timer_latency 2833 ns
<...>-868 [001] .... 54.029353: #1 context thread timer_latency 9820 ns
<idle>-0 [000] d.h1 54.030328: #2 context irq timer_latency 769 ns
<...>-867 [000] .... 54.030330: #2 context thread timer_latency 3070 ns
<idle>-0 [001] d.h1 54.030344: #2 context irq timer_latency 935 ns
<...>-868 [001] .... 54.030347: #2 context thread timer_latency 4351 ns
The tracer creates a per-cpu kernel thread with real-time priority
SCHED_FIFO:95 that prints two lines at every activation. The first is
the *timer latency* observed at the *hardirq* context before the activation
of the thread. The second is the *timer latency* observed by the thread.
The ACTIVATION ID field serves to relate the *irq* execution to its
respective *thread* execution.
The *irq*/*thread* splitting is important to clarify in which context
the unexpected high value is coming from. The *irq* context can be
delayed by hardware-related actions, such as SMIs, NMIs, IRQs,
or by thread masking interrupts. Once the timer happens, the delay
can also be influenced by blocking caused by threads. For example, by
postponing the scheduler execution via preempt_disable(), scheduler
execution, or masking interrupts. Threads can also be delayed by the
interference from other threads and IRQs.
Tracer options
---------------------
The timerlat tracer is built on top of osnoise tracer.
So its configuration is also done in the osnoise/ config
directory. The timerlat configs are:
- cpus: CPUs at which a timerlat thread will execute.
- timerlat_period_us: the period of the timerlat thread.
- stop_tracing_us: stop the system tracing if a
timer latency at the *irq* context higher than the configured
value happens. Writing 0 disables this option.
- stop_tracing_total_us: stop the system tracing if a
timer latency at the *thread* context is higher than the configured
value happens. Writing 0 disables this option.
- print_stack: save the stack of the IRQ occurrence. The stack is printed
after the *thread context* event, or at the IRQ handler if *stop_tracing_us*
is hit.
timerlat and osnoise
----------------------------
The timerlat can also take advantage of the osnoise: traceevents.
For example::
[root@f32 ~]# cd /sys/kernel/tracing/
[root@f32 tracing]# echo timerlat > current_tracer
[root@f32 tracing]# echo 1 > events/osnoise/enable
[root@f32 tracing]# echo 25 > osnoise/stop_tracing_total_us
[root@f32 tracing]# tail -10 trace
cc1-87882 [005] d..h... 548.771078: #402268 context irq timer_latency 13585 ns
cc1-87882 [005] dNLh1.. 548.771082: irq_noise: local_timer:236 start 548.771077442 duration 7597 ns
cc1-87882 [005] dNLh2.. 548.771099: irq_noise: qxl:21 start 548.771085017 duration 7139 ns
cc1-87882 [005] d...3.. 548.771102: thread_noise: cc1:87882 start 548.771078243 duration 9909 ns
timerlat/5-1035 [005] ....... 548.771104: #402268 context thread timer_latency 39960 ns
In this case, the root cause of the timer latency does not point to a
single cause but to multiple ones. Firstly, the timer IRQ was delayed
for 13 us, which may point to a long IRQ disabled section (see IRQ
stacktrace section). Then the timer interrupt that wakes up the timerlat
thread took 7597 ns, and the qxl:21 device IRQ took 7139 ns. Finally,
the cc1 thread noise took 9909 ns of time before the context switch.
Such pieces of evidence are useful for the developer to use other
tracing methods to figure out how to debug and optimize the system.
It is worth mentioning that the *duration* values reported
by the osnoise: events are *net* values. For example, the
thread_noise does not include the duration of the overhead caused
by the IRQ execution (which indeed accounted for 12736 ns). But
the values reported by the timerlat tracer (timerlat_latency)
are *gross* values.
The art below illustrates a CPU timeline and how the timerlat tracer
observes it at the top and the osnoise: events at the bottom. Each "-"
in the timelines means circa 1 us, and the time moves ==>::
External timer irq thread
clock latency latency
event 13585 ns 39960 ns
| ^ ^
v | |
|-------------| |
|-------------+-------------------------|
^ ^
========================================================================
[tmr irq] [dev irq]
[another thread...^ v..^ v.......][timerlat/ thread] <-- CPU timeline
=========================================================================
|-------| |-------|
|--^ v-------|
| | |
| | + thread_noise: 9909 ns
| +-> irq_noise: 6139 ns
+-> irq_noise: 7597 ns
IRQ stacktrace
---------------------------
The osnoise/print_stack option is helpful for the cases in which a thread
noise causes the major factor for the timer latency, because of preempt or
irq disabled. For example::
[root@f32 tracing]# echo 500 > osnoise/stop_tracing_total_us
[root@f32 tracing]# echo 500 > osnoise/print_stack
[root@f32 tracing]# echo timerlat > current_tracer
[root@f32 tracing]# tail -21 per_cpu/cpu7/trace
insmod-1026 [007] dN.h1.. 200.201948: irq_noise: local_timer:236 start 200.201939376 duration 7872 ns
insmod-1026 [007] d..h1.. 200.202587: #29800 context irq timer_latency 1616 ns
insmod-1026 [007] dN.h2.. 200.202598: irq_noise: local_timer:236 start 200.202586162 duration 11855 ns
insmod-1026 [007] dN.h3.. 200.202947: irq_noise: local_timer:236 start 200.202939174 duration 7318 ns
insmod-1026 [007] d...3.. 200.203444: thread_noise: insmod:1026 start 200.202586933 duration 838681 ns
timerlat/7-1001 [007] ....... 200.203445: #29800 context thread timer_latency 859978 ns
timerlat/7-1001 [007] ....1.. 200.203446: <stack trace>
=> timerlat_irq
=> __hrtimer_run_queues
=> hrtimer_interrupt
=> __sysvec_apic_timer_interrupt
=> asm_call_irq_on_stack
=> sysvec_apic_timer_interrupt
=> asm_sysvec_apic_timer_interrupt
=> delay_tsc
=> dummy_load_1ms_pd_init
=> do_one_initcall
=> do_init_module
=> __do_sys_finit_module
=> do_syscall_64
=> entry_SYSCALL_64_after_hwframe
In this case, it is possible to see that the thread added the highest
contribution to the *timer latency* and the stack trace, saved during
the timerlat IRQ handler, points to a function named
dummy_load_1ms_pd_init, which had the following code (on purpose)::
static int __init dummy_load_1ms_pd_init(void)
{
preempt_disable();
mdelay(1);
preempt_enable();
return 0;
}
User-space interface
---------------------------
Timerlat allows user-space threads to use timerlat infra-structure to
measure scheduling latency. This interface is accessible via a per-CPU
file descriptor inside $tracing_dir/osnoise/per_cpu/cpu$ID/timerlat_fd.
This interface is accessible under the following conditions:
- timerlat tracer is enable
- osnoise workload option is set to NO_OSNOISE_WORKLOAD
- The user-space thread is affined to a single processor
- The thread opens the file associated with its single processor
- Only one thread can access the file at a time
The open() syscall will fail if any of these conditions are not met.
After opening the file descriptor, the user space can read from it.
The read() system call will run a timerlat code that will arm the
timer in the future and wait for it as the regular kernel thread does.
When the timer IRQ fires, the timerlat IRQ will execute, report the
IRQ latency and wake up the thread waiting in the read. The thread will be
scheduled and report the thread latency via tracer - as for the kernel
thread.
The difference from the in-kernel timerlat is that, instead of re-arming
the timer, timerlat will return to the read() system call. At this point,
the user can run any code.
If the application rereads the file timerlat file descriptor, the tracer
will report the return from user-space latency, which is the total
latency. If this is the end of the work, it can be interpreted as the
response time for the request.
After reporting the total latency, timerlat will restart the cycle, arm
a timer, and go to sleep for the following activation.
If at any time one of the conditions is broken, e.g., the thread migrates
while in user space, or the timerlat tracer is disabled, the SIG_KILL
signal will be sent to the user-space thread.
Here is an basic example of user-space code for timerlat::
int main(void)
{
char buffer[1024];
int timerlat_fd;
int retval;
long cpu = 0; /* place in CPU 0 */
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
if (sched_setaffinity(gettid(), sizeof(set), &set) == -1)
return 1;
snprintf(buffer, sizeof(buffer),
"/sys/kernel/tracing/osnoise/per_cpu/cpu%ld/timerlat_fd",
cpu);
timerlat_fd = open(buffer, O_RDONLY);
if (timerlat_fd < 0) {
printf("error opening %s: %s\n", buffer, strerror(errno));
exit(1);
}
for (;;) {
retval = read(timerlat_fd, buffer, 1024);
if (retval < 0)
break;
}
close(timerlat_fd);
exit(0);
}
@@ -0,0 +1,338 @@
=========================================================
Notes on Analysing Behaviour Using Events and Tracepoints
=========================================================
:Author: Mel Gorman (PCL information heavily based on email from Ingo Molnar)
1. Introduction
===============
Tracepoints (see Documentation/trace/tracepoints.rst) can be used without
creating custom kernel modules to register probe functions using the event
tracing infrastructure.
Simplistically, tracepoints represent important events that can be
taken in conjunction with other tracepoints to build a "Big Picture" of
what is going on within the system. There are a large number of methods for
gathering and interpreting these events. Lacking any current Best Practises,
this document describes some of the methods that can be used.
This document assumes that debugfs is mounted on /sys/kernel/debug and that
the appropriate tracing options have been configured into the kernel. It is
assumed that the PCL tool tools/perf has been installed and is in your path.
2. Listing Available Events
===========================
2.1 Standard Utilities
----------------------
All possible events are visible from /sys/kernel/tracing/events. Simply
calling::
$ find /sys/kernel/tracing/events -type d
will give a fair indication of the number of events available.
2.2 PCL (Performance Counters for Linux)
----------------------------------------
Discovery and enumeration of all counters and events, including tracepoints,
are available with the perf tool. Getting a list of available events is a
simple case of::
$ perf list 2>&1 | grep Tracepoint
ext4:ext4_free_inode [Tracepoint event]
ext4:ext4_request_inode [Tracepoint event]
ext4:ext4_allocate_inode [Tracepoint event]
ext4:ext4_write_begin [Tracepoint event]
ext4:ext4_ordered_write_end [Tracepoint event]
[ .... remaining output snipped .... ]
3. Enabling Events
==================
3.1 System-Wide Event Enabling
------------------------------
See Documentation/trace/events.rst for a proper description on how events
can be enabled system-wide. A short example of enabling all events related
to page allocation would look something like::
$ for i in `find /sys/kernel/tracing/events -name "enable" | grep mm_`; do echo 1 > $i; done
3.2 System-Wide Event Enabling with SystemTap
---------------------------------------------
In SystemTap, tracepoints are accessible using the kernel.trace() function
call. The following is an example that reports every 5 seconds what processes
were allocating the pages.
::
global page_allocs
probe kernel.trace("mm_page_alloc") {
page_allocs[execname()]++
}
function print_count() {
printf ("%-25s %-s\n", "#Pages Allocated", "Process Name")
foreach (proc in page_allocs-)
printf("%-25d %s\n", page_allocs[proc], proc)
printf ("\n")
delete page_allocs
}
probe timer.s(5) {
print_count()
}
3.3 System-Wide Event Enabling with PCL
---------------------------------------
By specifying the -a switch and analysing sleep, the system-wide events
for a duration of time can be examined.
::
$ perf stat -a \
-e kmem:mm_page_alloc -e kmem:mm_page_free \
-e kmem:mm_page_free_batched \
sleep 10
Performance counter stats for 'sleep 10':
9630 kmem:mm_page_alloc
2143 kmem:mm_page_free
7424 kmem:mm_page_free_batched
10.002577764 seconds time elapsed
Similarly, one could execute a shell and exit it as desired to get a report
at that point.
3.4 Local Event Enabling
------------------------
Documentation/trace/ftrace.rst describes how to enable events on a per-thread
basis using set_ftrace_pid.
3.5 Local Event Enablement with PCL
-----------------------------------
Events can be activated and tracked for the duration of a process on a local
basis using PCL such as follows.
::
$ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
-e kmem:mm_page_free_batched ./hackbench 10
Time: 0.909
Performance counter stats for './hackbench 10':
17803 kmem:mm_page_alloc
12398 kmem:mm_page_free
4827 kmem:mm_page_free_batched
0.973913387 seconds time elapsed
4. Event Filtering
==================
Documentation/trace/ftrace.rst covers in-depth how to filter events in
ftrace. Obviously using grep and awk of trace_pipe is an option as well
as any script reading trace_pipe.
5. Analysing Event Variances with PCL
=====================================
Any workload can exhibit variances between runs and it can be important
to know what the standard deviation is. By and large, this is left to the
performance analyst to do it by hand. In the event that the discrete event
occurrences are useful to the performance analyst, then perf can be used.
::
$ perf stat --repeat 5 -e kmem:mm_page_alloc -e kmem:mm_page_free
-e kmem:mm_page_free_batched ./hackbench 10
Time: 0.890
Time: 0.895
Time: 0.915
Time: 1.001
Time: 0.899
Performance counter stats for './hackbench 10' (5 runs):
16630 kmem:mm_page_alloc ( +- 3.542% )
11486 kmem:mm_page_free ( +- 4.771% )
4730 kmem:mm_page_free_batched ( +- 2.325% )
0.982653002 seconds time elapsed ( +- 1.448% )
In the event that some higher-level event is required that depends on some
aggregation of discrete events, then a script would need to be developed.
Using --repeat, it is also possible to view how events are fluctuating over
time on a system-wide basis using -a and sleep.
::
$ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
-e kmem:mm_page_free_batched \
-a --repeat 10 \
sleep 1
Performance counter stats for 'sleep 1' (10 runs):
1066 kmem:mm_page_alloc ( +- 26.148% )
182 kmem:mm_page_free ( +- 5.464% )
890 kmem:mm_page_free_batched ( +- 30.079% )
1.002251757 seconds time elapsed ( +- 0.005% )
6. Higher-Level Analysis with Helper Scripts
============================================
When events are enabled the events that are triggering can be read from
/sys/kernel/tracing/trace_pipe in human-readable format although binary
options exist as well. By post-processing the output, further information can
be gathered on-line as appropriate. Examples of post-processing might include
- Reading information from /proc for the PID that triggered the event
- Deriving a higher-level event from a series of lower-level events.
- Calculating latencies between two events
Documentation/trace/postprocess/trace-pagealloc-postprocess.pl is an example
script that can read trace_pipe from STDIN or a copy of a trace. When used
on-line, it can be interrupted once to generate a report without exiting
and twice to exit.
Simplistically, the script just reads STDIN and counts up events but it
also can do more such as
- Derive high-level events from many low-level events. If a number of pages
are freed to the main allocator from the per-CPU lists, it recognises
that as one per-CPU drain even though there is no specific tracepoint
for that event
- It can aggregate based on PID or individual process number
- In the event memory is getting externally fragmented, it reports
on whether the fragmentation event was severe or moderate.
- When receiving an event about a PID, it can record who the parent was so
that if large numbers of events are coming from very short-lived
processes, the parent process responsible for creating all the helpers
can be identified
7. Lower-Level Analysis with PCL
================================
There may also be a requirement to identify what functions within a program
were generating events within the kernel. To begin this sort of analysis, the
data must be recorded. At the time of writing, this required root:
::
$ perf record -c 1 \
-e kmem:mm_page_alloc -e kmem:mm_page_free \
-e kmem:mm_page_free_batched \
./hackbench 10
Time: 0.894
[ perf record: Captured and wrote 0.733 MB perf.data (~32010 samples) ]
Note the use of '-c 1' to set the event period to sample. The default sample
period is quite high to minimise overhead but the information collected can be
very coarse as a result.
This record outputted a file called perf.data which can be analysed using
perf report.
::
$ perf report
# Samples: 30922
#
# Overhead Command Shared Object
# ........ ......... ................................
#
87.27% hackbench [vdso]
6.85% hackbench /lib/i686/cmov/libc-2.9.so
2.62% hackbench /lib/ld-2.9.so
1.52% perf [vdso]
1.22% hackbench ./hackbench
0.48% hackbench [kernel]
0.02% perf /lib/i686/cmov/libc-2.9.so
0.01% perf /usr/bin/perf
0.01% perf /lib/ld-2.9.so
0.00% hackbench /lib/i686/cmov/libpthread-2.9.so
#
# (For more details, try: perf report --sort comm,dso,symbol)
#
According to this, the vast majority of events triggered on events
within the VDSO. With simple binaries, this will often be the case so let's
take a slightly different example. In the course of writing this, it was
noticed that X was generating an insane amount of page allocations so let's look
at it:
::
$ perf record -c 1 -f \
-e kmem:mm_page_alloc -e kmem:mm_page_free \
-e kmem:mm_page_free_batched \
-p `pidof X`
This was interrupted after a few seconds and
::
$ perf report
# Samples: 27666
#
# Overhead Command Shared Object
# ........ ....... .......................................
#
51.95% Xorg [vdso]
47.95% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1
0.09% Xorg /lib/i686/cmov/libc-2.9.so
0.01% Xorg [kernel]
#
# (For more details, try: perf report --sort comm,dso,symbol)
#
So, almost half of the events are occurring in a library. To get an idea which
symbol:
::
$ perf report --sort comm,dso,symbol
# Samples: 27666
#
# Overhead Command Shared Object Symbol
# ........ ....... ....................................... ......
#
51.95% Xorg [vdso] [.] 0x000000ffffe424
47.93% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1 [.] pixmanFillsse2
0.09% Xorg /lib/i686/cmov/libc-2.9.so [.] _int_malloc
0.01% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1 [.] pixman_region32_copy_f
0.01% Xorg [kernel] [k] read_hpet
0.01% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1 [.] get_fast_path
0.00% Xorg [kernel] [k] ftrace_trace_userstack
To see where within the function pixmanFillsse2 things are going wrong:
::
$ perf annotate pixmanFillsse2
[ ... ]
0.00 : 34eeb: 0f 18 08 prefetcht0 (%eax)
: }
:
: extern __inline void __attribute__((__gnu_inline__, __always_inline__, _
: _mm_store_si128 (__m128i *__P, __m128i __B) : {
: *__P = __B;
12.40 : 34eee: 66 0f 7f 80 40 ff ff movdqa %xmm0,-0xc0(%eax)
0.00 : 34ef5: ff
12.40 : 34ef6: 66 0f 7f 80 50 ff ff movdqa %xmm0,-0xb0(%eax)
0.00 : 34efd: ff
12.39 : 34efe: 66 0f 7f 80 60 ff ff movdqa %xmm0,-0xa0(%eax)
0.00 : 34f05: ff
12.67 : 34f06: 66 0f 7f 80 70 ff ff movdqa %xmm0,-0x90(%eax)
0.00 : 34f0d: ff
12.58 : 34f0e: 66 0f 7f 40 80 movdqa %xmm0,-0x80(%eax)
12.31 : 34f13: 66 0f 7f 40 90 movdqa %xmm0,-0x70(%eax)
12.40 : 34f18: 66 0f 7f 40 a0 movdqa %xmm0,-0x60(%eax)
12.31 : 34f1d: 66 0f 7f 40 b0 movdqa %xmm0,-0x50(%eax)
At a glance, it looks like the time is being spent copying pixmaps to
the card. Further investigation would be needed to determine why pixmaps
are being copied around so much but a starting point would be to take an
ancient build of libpixmap out of the library path where it was totally
forgotten about from months ago!
@@ -0,0 +1,180 @@
==================================
Using the Linux Kernel Tracepoints
==================================
:Author: Mathieu Desnoyers
This document introduces Linux Kernel Tracepoints and their use. It
provides examples of how to insert tracepoints in the kernel and
connect probe functions to them and provides some examples of probe
functions.
Purpose of tracepoints
----------------------
A tracepoint placed in code provides a hook to call a function (probe)
that you can provide at runtime. A tracepoint can be "on" (a probe is
connected to it) or "off" (no probe is attached). When a tracepoint is
"off" it has no effect, except for adding a tiny time penalty
(checking a condition for a branch) and space penalty (adding a few
bytes for the function call at the end of the instrumented function
and adds a data structure in a separate section). When a tracepoint
is "on", the function you provide is called each time the tracepoint
is executed, in the execution context of the caller. When the function
provided ends its execution, it returns to the caller (continuing from
the tracepoint site).
You can put tracepoints at important locations in the code. They are
lightweight hooks that can pass an arbitrary number of parameters,
whose prototypes are described in a tracepoint declaration placed in a
header file.
They can be used for tracing and performance accounting.
Usage
-----
Two elements are required for tracepoints :
- A tracepoint definition, placed in a header file.
- The tracepoint statement, in C code.
In order to use tracepoints, you should include linux/tracepoint.h.
In include/trace/events/subsys.h::
#undef TRACE_SYSTEM
#define TRACE_SYSTEM subsys
#if !defined(_TRACE_SUBSYS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SUBSYS_H
#include <linux/tracepoint.h>
DECLARE_TRACE(subsys_eventname,
TP_PROTO(int firstarg, struct task_struct *p),
TP_ARGS(firstarg, p));
#endif /* _TRACE_SUBSYS_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
In subsys/file.c (where the tracing statement must be added)::
#include <trace/events/subsys.h>
#define CREATE_TRACE_POINTS
DEFINE_TRACE(subsys_eventname);
void somefct(void)
{
...
trace_subsys_eventname_tp(arg, task);
...
}
Where :
- subsys_eventname is an identifier unique to your event
- subsys is the name of your subsystem.
- eventname is the name of the event to trace.
- `TP_PROTO(int firstarg, struct task_struct *p)` is the prototype of the
function called by this tracepoint.
- `TP_ARGS(firstarg, p)` are the parameters names, same as found in the
prototype.
- if you use the header in multiple source files, `#define CREATE_TRACE_POINTS`
should appear only in one source file.
Connecting a function (probe) to a tracepoint is done by providing a
probe (function to call) for the specific tracepoint through
register_trace_subsys_eventname(). Removing a probe is done through
unregister_trace_subsys_eventname(); it will remove the probe.
tracepoint_synchronize_unregister() must be called before the end of
the module exit function to make sure there is no caller left using
the probe. This, and the fact that preemption is disabled around the
probe call, make sure that probe removal and module unload are safe.
The tracepoint mechanism supports inserting multiple instances of the
same tracepoint, but a single definition must be made of a given
tracepoint name over all the kernel to make sure no type conflict will
occur. Name mangling of the tracepoints is done using the prototypes
to make sure typing is correct. Verification of probe type correctness
is done at the registration site by the compiler. Tracepoints can be
put in inline functions, inlined static functions, and unrolled loops
as well as regular functions.
The naming scheme "subsys_event" is suggested here as a convention
intended to limit collisions. Tracepoint names are global to the
kernel: they are considered as being the same whether they are in the
core kernel image or in modules.
If the tracepoint has to be used in kernel modules, an
EXPORT_TRACEPOINT_SYMBOL_GPL() or EXPORT_TRACEPOINT_SYMBOL() can be
used to export the defined tracepoints.
If you need to do a bit of work for a tracepoint parameter, and
that work is only used for the tracepoint, that work can be encapsulated
within an if statement with the following::
if (trace_foo_bar_enabled()) {
int i;
int tot = 0;
for (i = 0; i < count; i++)
tot += calculate_nuggets();
trace_foo_bar_tp(tot);
}
All trace_<tracepoint>_tp() calls have a matching trace_<tracepoint>_enabled()
function defined that returns true if the tracepoint is enabled and
false otherwise. The trace_<tracepoint>_tp() should always be within the
block of the if (trace_<tracepoint>_enabled()) to prevent races between
the tracepoint being enabled and the check being seen.
The advantage of using the trace_<tracepoint>_enabled() is that it uses
the static_key of the tracepoint to allow the if statement to be implemented
with jump labels and avoid conditional branches.
.. note:: The convenience macro TRACE_EVENT provides an alternative way to
define tracepoints. Note, DECLARE_TRACE(foo) creates a function
"trace_foo_tp()" whereas TRACE_EVENT(foo) creates a function
"trace_foo()", and also exposes the tracepoint as a trace event in
/sys/kernel/tracing/events directory. Check http://lwn.net/Articles/379903,
http://lwn.net/Articles/381064 and http://lwn.net/Articles/383362
for a series of articles with more details.
If you require calling a tracepoint from a header file, it is not
recommended to call one directly or to use the trace_<tracepoint>_enabled()
function call, as tracepoints in header files can have side effects if a
header is included from a file that has CREATE_TRACE_POINTS set, as
well as the trace_<tracepoint>() is not that small of an inline
and can bloat the kernel if used by other inlined functions. Instead,
include tracepoint-defs.h and use tracepoint_enabled().
In a C file::
void do_trace_foo_bar_wrapper(args)
{
trace_foo_bar_tp(args); // for tracepoints created via DECLARE_TRACE
// or
trace_foo_bar(args); // for tracepoints created via TRACE_EVENT
}
In the header file::
DECLARE_TRACEPOINT(foo_bar);
static inline void some_inline_function()
{
[..]
if (tracepoint_enabled(foo_bar))
do_trace_foo_bar_wrapper(args);
[..]
}
@@ -0,0 +1,186 @@
=========================================
Uprobe-tracer: Uprobe-based Event Tracing
=========================================
:Author: Srikar Dronamraju
Overview
--------
Uprobe based trace events are similar to kprobe based trace events.
To enable this feature, build your kernel with CONFIG_UPROBE_EVENTS=y.
Similar to the kprobe-event tracer, this doesn't need to be activated via
current_tracer. Instead of that, add probe points via
/sys/kernel/tracing/uprobe_events, and enable it via
/sys/kernel/tracing/events/uprobes/<EVENT>/enable.
However unlike kprobe-event tracer, the uprobe event interface expects the
user to calculate the offset of the probepoint in the object.
You can also use /sys/kernel/tracing/dynamic_events instead of
uprobe_events. That interface will provide unified access to other
dynamic events too.
Synopsis of uprobe_tracer
-------------------------
::
p[:[GRP/][EVENT]] PATH:OFFSET [FETCHARGS] : Set a uprobe
r[:[GRP/][EVENT]] PATH:OFFSET [FETCHARGS] : Set a return uprobe (uretprobe)
p[:[GRP/][EVENT]] PATH:OFFSET%return [FETCHARGS] : Set a return uprobe (uretprobe)
-:[GRP/][EVENT] : Clear uprobe or uretprobe event
GRP : Group name. If omitted, "uprobes" is the default value.
EVENT : Event name. If omitted, the event name is generated based
on PATH+OFFSET.
PATH : Path to an executable or a library.
OFFSET : Offset where the probe is inserted.
OFFSET%return : Offset where the return probe is inserted.
FETCHARGS : Arguments. Each probe can have up to 128 args.
%REG : Fetch register REG
@ADDR : Fetch memory at ADDR (ADDR should be in userspace)
@+OFFSET : Fetch memory at OFFSET (OFFSET from same file as PATH)
$stackN : Fetch Nth entry of stack (N >= 0)
$stack : Fetch stack address.
$retval : Fetch return value.(\*1)
$comm : Fetch current task comm.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*2)(\*3)
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
(x8/x16/x32/x64), "string" and bitfield are supported.
(\*1) only for return probe.
(\*2) this is useful for fetching a field of data structures.
(\*3) Unlike kprobe event, "u" prefix will just be ignored, because uprobe
events can access only user-space memory.
Types
-----
Several types are supported for fetch-args. Uprobe tracer will access memory
by given type. Prefix 's' and 'u' means those types are signed and unsigned
respectively. 'x' prefix implies it is unsigned. Traced arguments are shown
in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32'
or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and
x86-64 uses x64).
String type is a special type, which fetches a "null-terminated" string from
user space.
Bitfield is another special type, which takes 3 parameters, bit-width, bit-
offset, and container-size (usually 32). The syntax is::
b<bit-width>@<bit-offset>/<container-size>
For $comm, the default type is "string"; any other type is invalid.
Event Profiling
---------------
You can check the total number of probe hits per event via
/sys/kernel/tracing/uprobe_profile. The first column is the filename,
the second is the event name, the third is the number of probe hits.
Usage examples
--------------
* Add a probe as a new uprobe event, write a new definition to uprobe_events
as below (sets a uprobe at an offset of 0x4245c0 in the executable /bin/bash)::
echo 'p /bin/bash:0x4245c0' > /sys/kernel/tracing/uprobe_events
* Add a probe as a new uretprobe event::
echo 'r /bin/bash:0x4245c0' > /sys/kernel/tracing/uprobe_events
* Unset registered event::
echo '-:p_bash_0x4245c0' >> /sys/kernel/tracing/uprobe_events
* Print out the events that are registered::
cat /sys/kernel/tracing/uprobe_events
* Clear all events::
echo > /sys/kernel/tracing/uprobe_events
Following example shows how to dump the instruction pointer and %ax register
at the probed text address. Probe zfree function in /bin/zsh::
# cd /sys/kernel/tracing/
# cat /proc/`pgrep zsh`/maps | grep /bin/zsh | grep r-xp
00400000-0048a000 r-xp 00000000 08:03 130904 /bin/zsh
# objdump -T /bin/zsh | grep -w zfree
0000000000446420 g DF .text 0000000000000012 Base zfree
0x46420 is the offset of zfree in object /bin/zsh that is loaded at
0x00400000. Hence the command to uprobe would be::
# echo 'p:zfree_entry /bin/zsh:0x46420 %ip %ax' > uprobe_events
And the same for the uretprobe would be::
# echo 'r:zfree_exit /bin/zsh:0x46420 %ip %ax' >> uprobe_events
.. note:: User has to explicitly calculate the offset of the probe-point
in the object.
We can see the events that are registered by looking at the uprobe_events file.
::
# cat uprobe_events
p:uprobes/zfree_entry /bin/zsh:0x00046420 arg1=%ip arg2=%ax
r:uprobes/zfree_exit /bin/zsh:0x00046420 arg1=%ip arg2=%ax
Format of events can be seen by viewing the file events/uprobes/zfree_entry/format.
::
# cat events/uprobes/zfree_entry/format
name: zfree_entry
ID: 922
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:int common_padding; offset:8; size:4; signed:1;
field:unsigned long __probe_ip; offset:12; size:4; signed:0;
field:u32 arg1; offset:16; size:4; signed:0;
field:u32 arg2; offset:20; size:4; signed:0;
print fmt: "(%lx) arg1=%lx arg2=%lx", REC->__probe_ip, REC->arg1, REC->arg2
Right after definition, each event is disabled by default. For tracing these
events, you need to enable it by::
# echo 1 > events/uprobes/enable
Lets start tracing, sleep for some time and stop tracing.
::
# echo 1 > tracing_on
# sleep 20
# echo 0 > tracing_on
Also, you can disable the event by::
# echo 0 > events/uprobes/enable
And you can see the traced information via /sys/kernel/tracing/trace.
::
# cat trace
# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
zsh-24842 [006] 258544.995456: zfree_entry: (0x446420) arg1=446420 arg2=79
zsh-24842 [007] 258545.000270: zfree_exit: (0x446540 <- 0x446420) arg1=446540 arg2=0
zsh-24842 [002] 258545.043929: zfree_entry: (0x446420) arg1=446420 arg2=79
zsh-24842 [004] 258547.046129: zfree_exit: (0x446540 <- 0x446420) arg1=446540 arg2=0
Output shows us uprobe was triggered for a pid 24842 with ip being 0x446420
and contents of ax register being 79. And uretprobe was triggered with ip at
0x446540 with counterpart function entry at 0x446420.
@@ -0,0 +1,304 @@
=========================================
user_events: User-based Event Tracing
=========================================
:Author: Beau Belgrave
Overview
--------
User based trace events allow user processes to create events and trace data
that can be viewed via existing tools, such as ftrace and perf.
To enable this feature, build your kernel with CONFIG_USER_EVENTS=y.
Programs can view status of the events via
/sys/kernel/tracing/user_events_status and can both register and write
data out via /sys/kernel/tracing/user_events_data.
Programs can also use /sys/kernel/tracing/dynamic_events to register and
delete user based events via the u: prefix. The format of the command to
dynamic_events is the same as the ioctl with the u: prefix applied. This
requires CAP_PERFMON due to the event persisting, otherwise -EPERM is returned.
Typically programs will register a set of events that they wish to expose to
tools that can read trace_events (such as ftrace and perf). The registration
process tells the kernel which address and bit to reflect if any tool has
enabled the event and data should be written. The registration will give back
a write index which describes the data when a write() or writev() is called
on the /sys/kernel/tracing/user_events_data file.
The structures referenced in this document are contained within the
/include/uapi/linux/user_events.h file in the source tree.
**NOTE:** *Both user_events_status and user_events_data are under the tracefs
filesystem and may be mounted at different paths than above.*
Registering
-----------
Registering within a user process is done via ioctl() out to the
/sys/kernel/tracing/user_events_data file. The command to issue is
DIAG_IOCSREG.
This command takes a packed struct user_reg as an argument::
struct user_reg {
/* Input: Size of the user_reg structure being used */
__u32 size;
/* Input: Bit in enable address to use */
__u8 enable_bit;
/* Input: Enable size in bytes at address */
__u8 enable_size;
/* Input: Flags to use, if any */
__u16 flags;
/* Input: Address to update when enabled */
__u64 enable_addr;
/* Input: Pointer to string with event name, description and flags */
__u64 name_args;
/* Output: Index of the event to use when writing data */
__u32 write_index;
} __attribute__((__packed__));
The struct user_reg requires all the above inputs to be set appropriately.
+ size: This must be set to sizeof(struct user_reg).
+ enable_bit: The bit to reflect the event status at the address specified by
enable_addr.
+ enable_size: The size of the value specified by enable_addr.
This must be 4 (32-bit) or 8 (64-bit). 64-bit values are only allowed to be
used on 64-bit kernels, however, 32-bit can be used on all kernels.
+ flags: The flags to use, if any.
Callers should first attempt to use flags and retry without flags to ensure
support for lower versions of the kernel. If a flag is not supported -EINVAL
is returned.
+ enable_addr: The address of the value to use to reflect event status. This
must be naturally aligned and write accessible within the user program.
+ name_args: The name and arguments to describe the event, see command format
for details.
The following flags are currently supported.
+ USER_EVENT_REG_PERSIST: The event will not delete upon the last reference
closing. Callers may use this if an event should exist even after the
process closes or unregisters the event. Requires CAP_PERFMON otherwise
-EPERM is returned.
+ USER_EVENT_REG_MULTI_FORMAT: The event can contain multiple formats. This
allows programs to prevent themselves from being blocked when their event
format changes and they wish to use the same name. When this flag is used the
tracepoint name will be in the new format of "name.unique_id" vs the older
format of "name". A tracepoint will be created for each unique pair of name
and format. This means if several processes use the same name and format,
they will use the same tracepoint. If yet another process uses the same name,
but a different format than the other processes, it will use a different
tracepoint with a new unique id. Recording programs need to scan tracefs for
the various different formats of the event name they are interested in
recording. The system name of the tracepoint will also use "user_events_multi"
instead of "user_events". This prevents single-format event names conflicting
with any multi-format event names within tracefs. The unique_id is output as
a hex string. Recording programs should ensure the tracepoint name starts with
the event name they registered and has a suffix that starts with . and only
has hex characters. For example to find all versions of the event "test" you
can use the regex "^test\.[0-9a-fA-F]+$".
Upon successful registration the following is set.
+ write_index: The index to use for this file descriptor that represents this
event when writing out data. The index is unique to this instance of the file
descriptor that was used for the registration. See writing data for details.
User based events show up under tracefs like any other event under the
subsystem named "user_events". This means tools that wish to attach to the
events need to use /sys/kernel/tracing/events/user_events/[name]/enable
or perf record -e user_events:[name] when attaching/recording.
**NOTE:** The event subsystem name by default is "user_events". Callers should
not assume it will always be "user_events". Operators reserve the right in the
future to change the subsystem name per-process to accommodate event isolation.
In addition if the USER_EVENT_REG_MULTI_FORMAT flag is used the tracepoint name
will have a unique id appended to it and the system name will be
"user_events_multi" as described above.
Command Format
^^^^^^^^^^^^^^
The command string format is as follows::
name[:FLAG1[,FLAG2...]] [Field1[;Field2...]]
Supported Flags
^^^^^^^^^^^^^^^
None yet
Field Format
^^^^^^^^^^^^
::
type name [size]
Basic types are supported (__data_loc, u32, u64, int, char, char[20], etc).
User programs are encouraged to use clearly sized types like u32.
**NOTE:** *Long is not supported since size can vary between user and kernel.*
The size is only valid for types that start with a struct prefix.
This allows user programs to describe custom structs out to tools, if required.
For example, a struct in C that looks like this::
struct mytype {
char data[20];
};
Would be represented by the following field::
struct mytype myname 20
Deleting
--------
Deleting an event from within a user process is done via ioctl() out to the
/sys/kernel/tracing/user_events_data file. The command to issue is
DIAG_IOCSDEL.
This command only requires a single string specifying the event to delete by
its name. Delete will only succeed if there are no references left to the
event (in both user and kernel space). User programs should use a separate file
to request deletes than the one used for registration due to this.
**NOTE:** By default events will auto-delete when there are no references left
to the event. If programs do not want auto-delete, they must use the
USER_EVENT_REG_PERSIST flag when registering the event. Once that flag is used
the event exists until DIAG_IOCSDEL is invoked. Both register and delete of an
event that persists requires CAP_PERFMON, otherwise -EPERM is returned. When
there are multiple formats of the same event name, all events with the same
name will be attempted to be deleted. If only a specific version is wanted to
be deleted then the /sys/kernel/tracing/dynamic_events file should be used for
that specific format of the event.
Unregistering
-------------
If after registering an event it is no longer wanted to be updated then it can
be disabled via ioctl() out to the /sys/kernel/tracing/user_events_data file.
The command to issue is DIAG_IOCSUNREG. This is different than deleting, where
deleting actually removes the event from the system. Unregistering simply tells
the kernel your process is no longer interested in updates to the event.
This command takes a packed struct user_unreg as an argument::
struct user_unreg {
/* Input: Size of the user_unreg structure being used */
__u32 size;
/* Input: Bit to unregister */
__u8 disable_bit;
/* Input: Reserved, set to 0 */
__u8 __reserved;
/* Input: Reserved, set to 0 */
__u16 __reserved2;
/* Input: Address to unregister */
__u64 disable_addr;
} __attribute__((__packed__));
The struct user_unreg requires all the above inputs to be set appropriately.
+ size: This must be set to sizeof(struct user_unreg).
+ disable_bit: This must be set to the bit to disable (same bit that was
previously registered via enable_bit).
+ disable_addr: This must be set to the address to disable (same address that was
previously registered via enable_addr).
**NOTE:** Events are automatically unregistered when execve() is invoked. During
fork() the registered events will be retained and must be unregistered manually
in each process if wanted.
Status
------
When tools attach/record user based events the status of the event is updated
in realtime. This allows user programs to only incur the cost of the write() or
writev() calls when something is actively attached to the event.
The kernel will update the specified bit that was registered for the event as
tools attach/detach from the event. User programs simply check if the bit is set
to see if something is attached or not.
Administrators can easily check the status of all registered events by reading
the user_events_status file directly via a terminal. The output is as follows::
Name [# Comments]
...
Active: ActiveCount
Busy: BusyCount
For example, on a system that has a single event the output looks like this::
test
Active: 1
Busy: 0
If a user enables the user event via ftrace, the output would change to this::
test # Used by ftrace
Active: 1
Busy: 1
Writing Data
------------
After registering an event the same fd that was used to register can be used
to write an entry for that event. The write_index returned must be at the start
of the data, then the remaining data is treated as the payload of the event.
For example, if write_index returned was 1 and I wanted to write out an int
payload of the event. Then the data would have to be 8 bytes (2 ints) in size,
with the first 4 bytes being equal to 1 and the last 4 bytes being equal to the
value I want as the payload.
In memory this would look like this::
int index;
int payload;
User programs might have well known structs that they wish to use to emit out
as payloads. In those cases writev() can be used, with the first vector being
the index and the following vector(s) being the actual event payload.
For example, if I have a struct like this::
struct payload {
int src;
int dst;
int flags;
} __attribute__((__packed__));
It's advised for user programs to do the following::
struct iovec io[2];
struct payload e;
io[0].iov_base = &write_index;
io[0].iov_len = sizeof(write_index);
io[1].iov_base = &e;
io[1].iov_len = sizeof(e);
writev(fd, (const struct iovec*)io, 2);
**NOTE:** *The write_index is not emitted out into the trace being recorded.*
Example Code
------------
See sample code in samples/user_events.