tracing: Add support for named hist triggers
Allow users to define 'named' hist triggers. All triggers created with the same 'name=xxx' option will update the same shared histogram data. This expands the hist trigger syntax from this: # echo hist:keys=xxx ... [ if filter] > event/trigger to this: # echo hist:name=xxx:keys=xxx ... [ if filter] > event/trigger Named histograms must use a 'compatible' set of keys and values, which means each event added to a set of named triggers must have the same names and types. Reading the 'hist' file of any of the participating events will produce the same output as any other participating event, which is to be expected since they share the same data. Link: http://lkml.kernel.org/r/1dbc84ee3322a75daaf5b3ef1d0cc0a2fb682fc7.1457029949.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Reviewed-by: Namhyung Kim <namhyung@kernel.org> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
parent
db1388b4ff
commit
5463bfda32
|
@ -524,7 +524,7 @@ The following commands are supported:
|
||||||
|
|
||||||
hist:keys=<field1[,field2,...]>[:values=<field1[,field2,...]>]
|
hist:keys=<field1[,field2,...]>[:values=<field1[,field2,...]>]
|
||||||
[:sort=<field1[,field2,...]>][:size=#entries][:pause][:continue]
|
[:sort=<field1[,field2,...]>][:size=#entries][:pause][:continue]
|
||||||
[:clear] [if <filter>]
|
[:clear][:name=histname1] [if <filter>]
|
||||||
|
|
||||||
When a matching event is hit, an entry is added to a hash table
|
When a matching event is hit, an entry is added to a hash table
|
||||||
using the key(s) and value(s) named. Keys and values correspond to
|
using the key(s) and value(s) named. Keys and values correspond to
|
||||||
|
@ -546,18 +546,28 @@ The following commands are supported:
|
||||||
specified by the 'sort' keyword. If more than one field is
|
specified by the 'sort' keyword. If more than one field is
|
||||||
specified, the result will be a 'sort within a sort': the first key
|
specified, the result will be a 'sort within a sort': the first key
|
||||||
is taken to be the primary sort key and the second the secondary
|
is taken to be the primary sort key and the second the secondary
|
||||||
key.
|
key. If a hist trigger is given a name using the 'name' parameter,
|
||||||
|
its histogram data will be shared with other triggers of the same
|
||||||
|
name, and trigger hits will update this common data. Only triggers
|
||||||
|
with 'compatible' fields can be combined in this way; triggers are
|
||||||
|
'compatible' if the fields named in the trigger share the same
|
||||||
|
number and type of fields and those fields also have the same names.
|
||||||
|
Note that any two events always share the compatible 'hitcount' and
|
||||||
|
'stacktrace' fields and can therefore be combined using those
|
||||||
|
fields, however pointless that may be.
|
||||||
|
|
||||||
'hist' triggers add a 'hist' file to each event's subdirectory.
|
'hist' triggers add a 'hist' file to each event's subdirectory.
|
||||||
Reading the 'hist' file for the event will dump the hash table in
|
Reading the 'hist' file for the event will dump the hash table in
|
||||||
its entirety to stdout. If there are multiple hist triggers
|
its entirety to stdout. If there are multiple hist triggers
|
||||||
attached to an event, there will be a table for each trigger in the
|
attached to an event, there will be a table for each trigger in the
|
||||||
output. Each printed hash table entry is a simple list of the keys
|
output. The table displayed for a named trigger will be the same as
|
||||||
and values comprising the entry; keys are printed first and are
|
any other instance having the same name. Each printed hash table
|
||||||
delineated by curly braces, and are followed by the set of value
|
entry is a simple list of the keys and values comprising the entry;
|
||||||
fields for the entry. By default, numeric fields are displayed as
|
keys are printed first and are delineated by curly braces, and are
|
||||||
base-10 integers. This can be modified by appending any of the
|
followed by the set of value fields for the entry. By default,
|
||||||
following modifiers to the field name:
|
numeric fields are displayed as base-10 integers. This can be
|
||||||
|
modified by appending any of the following modifiers to the field
|
||||||
|
name:
|
||||||
|
|
||||||
.hex display a number as a hex value
|
.hex display a number as a hex value
|
||||||
.sym display an address as a symbol
|
.sym display an address as a symbol
|
||||||
|
@ -1809,3 +1819,251 @@ The following commands are supported:
|
||||||
Hits: 0
|
Hits: 0
|
||||||
Entries: 0
|
Entries: 0
|
||||||
Dropped: 0
|
Dropped: 0
|
||||||
|
|
||||||
|
Named triggers can be used to have triggers share a common set of
|
||||||
|
histogram data. This capability is mostly useful for combining the
|
||||||
|
output of events generated by tracepoints contained inside inline
|
||||||
|
functions, but names can be used in a hist trigger on any event.
|
||||||
|
For example, these two triggers when hit will update the same 'len'
|
||||||
|
field in the shared 'foo' histogram data:
|
||||||
|
|
||||||
|
# echo 'hist:name=foo:keys=skbaddr.hex:vals=len' > \
|
||||||
|
/sys/kernel/debug/tracing/events/net/netif_receive_skb/trigger
|
||||||
|
# echo 'hist:name=foo:keys=skbaddr.hex:vals=len' > \
|
||||||
|
/sys/kernel/debug/tracing/events/net/netif_rx/trigger
|
||||||
|
|
||||||
|
You can see that they're updating common histogram data by reading
|
||||||
|
each event's hist files at the same time:
|
||||||
|
|
||||||
|
# cat /sys/kernel/debug/tracing/events/net/netif_receive_skb/hist;
|
||||||
|
cat /sys/kernel/debug/tracing/events/net/netif_rx/hist
|
||||||
|
|
||||||
|
# event histogram
|
||||||
|
#
|
||||||
|
# trigger info: hist:name=foo:keys=skbaddr.hex:vals=hitcount,len:sort=hitcount:size=2048 [active]
|
||||||
|
#
|
||||||
|
|
||||||
|
{ skbaddr: ffff88000ad53500 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800af5a1500 } hitcount: 1 len: 76
|
||||||
|
{ skbaddr: ffff8800d62a1900 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bccb00 } hitcount: 1 len: 468
|
||||||
|
{ skbaddr: ffff8800d3c69900 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff88009ff09100 } hitcount: 1 len: 52
|
||||||
|
{ skbaddr: ffff88010f13ab00 } hitcount: 1 len: 168
|
||||||
|
{ skbaddr: ffff88006a54f400 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bcc500 } hitcount: 1 len: 260
|
||||||
|
{ skbaddr: ffff880064505000 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800baf24e00 } hitcount: 1 len: 32
|
||||||
|
{ skbaddr: ffff88009fe0ad00 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d3edff00 } hitcount: 1 len: 44
|
||||||
|
{ skbaddr: ffff88009fe0b400 } hitcount: 1 len: 168
|
||||||
|
{ skbaddr: ffff8800a1c55a00 } hitcount: 1 len: 40
|
||||||
|
{ skbaddr: ffff8800d2bcd100 } hitcount: 1 len: 40
|
||||||
|
{ skbaddr: ffff880064505f00 } hitcount: 1 len: 174
|
||||||
|
{ skbaddr: ffff8800a8bff200 } hitcount: 1 len: 160
|
||||||
|
{ skbaddr: ffff880044e3cc00 } hitcount: 1 len: 76
|
||||||
|
{ skbaddr: ffff8800a8bfe700 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bcdc00 } hitcount: 1 len: 32
|
||||||
|
{ skbaddr: ffff8800a1f64800 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bcde00 } hitcount: 1 len: 988
|
||||||
|
{ skbaddr: ffff88006a5dea00 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff88002e37a200 } hitcount: 1 len: 44
|
||||||
|
{ skbaddr: ffff8800a1f32c00 } hitcount: 2 len: 676
|
||||||
|
{ skbaddr: ffff88000ad52600 } hitcount: 2 len: 107
|
||||||
|
{ skbaddr: ffff8800a1f91e00 } hitcount: 2 len: 92
|
||||||
|
{ skbaddr: ffff8800af5a0200 } hitcount: 2 len: 142
|
||||||
|
{ skbaddr: ffff8800d2bcc600 } hitcount: 2 len: 220
|
||||||
|
{ skbaddr: ffff8800ba36f500 } hitcount: 2 len: 92
|
||||||
|
{ skbaddr: ffff8800d021f800 } hitcount: 2 len: 92
|
||||||
|
{ skbaddr: ffff8800a1f33600 } hitcount: 2 len: 675
|
||||||
|
{ skbaddr: ffff8800a8bfff00 } hitcount: 3 len: 138
|
||||||
|
{ skbaddr: ffff8800d62a1300 } hitcount: 3 len: 138
|
||||||
|
{ skbaddr: ffff88002e37a100 } hitcount: 4 len: 184
|
||||||
|
{ skbaddr: ffff880064504400 } hitcount: 4 len: 184
|
||||||
|
{ skbaddr: ffff8800a8bfec00 } hitcount: 4 len: 184
|
||||||
|
{ skbaddr: ffff88000ad53700 } hitcount: 5 len: 230
|
||||||
|
{ skbaddr: ffff8800d2bcdb00 } hitcount: 5 len: 196
|
||||||
|
{ skbaddr: ffff8800a1f90000 } hitcount: 6 len: 276
|
||||||
|
{ skbaddr: ffff88006a54f900 } hitcount: 6 len: 276
|
||||||
|
|
||||||
|
Totals:
|
||||||
|
Hits: 81
|
||||||
|
Entries: 42
|
||||||
|
Dropped: 0
|
||||||
|
# event histogram
|
||||||
|
#
|
||||||
|
# trigger info: hist:name=foo:keys=skbaddr.hex:vals=hitcount,len:sort=hitcount:size=2048 [active]
|
||||||
|
#
|
||||||
|
|
||||||
|
{ skbaddr: ffff88000ad53500 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800af5a1500 } hitcount: 1 len: 76
|
||||||
|
{ skbaddr: ffff8800d62a1900 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bccb00 } hitcount: 1 len: 468
|
||||||
|
{ skbaddr: ffff8800d3c69900 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff88009ff09100 } hitcount: 1 len: 52
|
||||||
|
{ skbaddr: ffff88010f13ab00 } hitcount: 1 len: 168
|
||||||
|
{ skbaddr: ffff88006a54f400 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bcc500 } hitcount: 1 len: 260
|
||||||
|
{ skbaddr: ffff880064505000 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800baf24e00 } hitcount: 1 len: 32
|
||||||
|
{ skbaddr: ffff88009fe0ad00 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d3edff00 } hitcount: 1 len: 44
|
||||||
|
{ skbaddr: ffff88009fe0b400 } hitcount: 1 len: 168
|
||||||
|
{ skbaddr: ffff8800a1c55a00 } hitcount: 1 len: 40
|
||||||
|
{ skbaddr: ffff8800d2bcd100 } hitcount: 1 len: 40
|
||||||
|
{ skbaddr: ffff880064505f00 } hitcount: 1 len: 174
|
||||||
|
{ skbaddr: ffff8800a8bff200 } hitcount: 1 len: 160
|
||||||
|
{ skbaddr: ffff880044e3cc00 } hitcount: 1 len: 76
|
||||||
|
{ skbaddr: ffff8800a8bfe700 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bcdc00 } hitcount: 1 len: 32
|
||||||
|
{ skbaddr: ffff8800a1f64800 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff8800d2bcde00 } hitcount: 1 len: 988
|
||||||
|
{ skbaddr: ffff88006a5dea00 } hitcount: 1 len: 46
|
||||||
|
{ skbaddr: ffff88002e37a200 } hitcount: 1 len: 44
|
||||||
|
{ skbaddr: ffff8800a1f32c00 } hitcount: 2 len: 676
|
||||||
|
{ skbaddr: ffff88000ad52600 } hitcount: 2 len: 107
|
||||||
|
{ skbaddr: ffff8800a1f91e00 } hitcount: 2 len: 92
|
||||||
|
{ skbaddr: ffff8800af5a0200 } hitcount: 2 len: 142
|
||||||
|
{ skbaddr: ffff8800d2bcc600 } hitcount: 2 len: 220
|
||||||
|
{ skbaddr: ffff8800ba36f500 } hitcount: 2 len: 92
|
||||||
|
{ skbaddr: ffff8800d021f800 } hitcount: 2 len: 92
|
||||||
|
{ skbaddr: ffff8800a1f33600 } hitcount: 2 len: 675
|
||||||
|
{ skbaddr: ffff8800a8bfff00 } hitcount: 3 len: 138
|
||||||
|
{ skbaddr: ffff8800d62a1300 } hitcount: 3 len: 138
|
||||||
|
{ skbaddr: ffff88002e37a100 } hitcount: 4 len: 184
|
||||||
|
{ skbaddr: ffff880064504400 } hitcount: 4 len: 184
|
||||||
|
{ skbaddr: ffff8800a8bfec00 } hitcount: 4 len: 184
|
||||||
|
{ skbaddr: ffff88000ad53700 } hitcount: 5 len: 230
|
||||||
|
{ skbaddr: ffff8800d2bcdb00 } hitcount: 5 len: 196
|
||||||
|
{ skbaddr: ffff8800a1f90000 } hitcount: 6 len: 276
|
||||||
|
{ skbaddr: ffff88006a54f900 } hitcount: 6 len: 276
|
||||||
|
|
||||||
|
Totals:
|
||||||
|
Hits: 81
|
||||||
|
Entries: 42
|
||||||
|
Dropped: 0
|
||||||
|
|
||||||
|
And here's an example that shows how to combine histogram data from
|
||||||
|
any two events even if they don't share any 'compatible' fields
|
||||||
|
other than 'hitcount' and 'stacktrace'. These commands create a
|
||||||
|
couple of triggers named 'bar' using those fields:
|
||||||
|
|
||||||
|
# echo 'hist:name=bar:key=stacktrace:val=hitcount' > \
|
||||||
|
/sys/kernel/debug/tracing/events/sched/sched_process_fork/trigger
|
||||||
|
# echo 'hist:name=bar:key=stacktrace:val=hitcount' > \
|
||||||
|
/sys/kernel/debug/tracing/events/net/netif_rx/trigger
|
||||||
|
|
||||||
|
And displaying the output of either shows some interesting if
|
||||||
|
somewhat confusing output:
|
||||||
|
|
||||||
|
# cat /sys/kernel/debug/tracing/events/sched/sched_process_fork/hist
|
||||||
|
# cat /sys/kernel/debug/tracing/events/net/netif_rx/hist
|
||||||
|
|
||||||
|
# event histogram
|
||||||
|
#
|
||||||
|
# trigger info: hist:name=bar:keys=stacktrace:vals=hitcount:sort=hitcount:size=2048 [active]
|
||||||
|
#
|
||||||
|
|
||||||
|
{ stacktrace:
|
||||||
|
_do_fork+0x18e/0x330
|
||||||
|
kernel_thread+0x29/0x30
|
||||||
|
kthreadd+0x154/0x1b0
|
||||||
|
ret_from_fork+0x3f/0x70
|
||||||
|
} hitcount: 1
|
||||||
|
{ stacktrace:
|
||||||
|
netif_rx_internal+0xb2/0xd0
|
||||||
|
netif_rx_ni+0x20/0x70
|
||||||
|
dev_loopback_xmit+0xaa/0xd0
|
||||||
|
ip_mc_output+0x126/0x240
|
||||||
|
ip_local_out_sk+0x31/0x40
|
||||||
|
igmp_send_report+0x1e9/0x230
|
||||||
|
igmp_timer_expire+0xe9/0x120
|
||||||
|
call_timer_fn+0x39/0xf0
|
||||||
|
run_timer_softirq+0x1e1/0x290
|
||||||
|
__do_softirq+0xfd/0x290
|
||||||
|
irq_exit+0x98/0xb0
|
||||||
|
smp_apic_timer_interrupt+0x4a/0x60
|
||||||
|
apic_timer_interrupt+0x6d/0x80
|
||||||
|
cpuidle_enter+0x17/0x20
|
||||||
|
call_cpuidle+0x3b/0x60
|
||||||
|
cpu_startup_entry+0x22d/0x310
|
||||||
|
} hitcount: 1
|
||||||
|
{ stacktrace:
|
||||||
|
netif_rx_internal+0xb2/0xd0
|
||||||
|
netif_rx_ni+0x20/0x70
|
||||||
|
dev_loopback_xmit+0xaa/0xd0
|
||||||
|
ip_mc_output+0x17f/0x240
|
||||||
|
ip_local_out_sk+0x31/0x40
|
||||||
|
ip_send_skb+0x1a/0x50
|
||||||
|
udp_send_skb+0x13e/0x270
|
||||||
|
udp_sendmsg+0x2bf/0x980
|
||||||
|
inet_sendmsg+0x67/0xa0
|
||||||
|
sock_sendmsg+0x38/0x50
|
||||||
|
SYSC_sendto+0xef/0x170
|
||||||
|
SyS_sendto+0xe/0x10
|
||||||
|
entry_SYSCALL_64_fastpath+0x12/0x6a
|
||||||
|
} hitcount: 2
|
||||||
|
{ stacktrace:
|
||||||
|
netif_rx_internal+0xb2/0xd0
|
||||||
|
netif_rx+0x1c/0x60
|
||||||
|
loopback_xmit+0x6c/0xb0
|
||||||
|
dev_hard_start_xmit+0x219/0x3a0
|
||||||
|
__dev_queue_xmit+0x415/0x4f0
|
||||||
|
dev_queue_xmit_sk+0x13/0x20
|
||||||
|
ip_finish_output2+0x237/0x340
|
||||||
|
ip_finish_output+0x113/0x1d0
|
||||||
|
ip_output+0x66/0xc0
|
||||||
|
ip_local_out_sk+0x31/0x40
|
||||||
|
ip_send_skb+0x1a/0x50
|
||||||
|
udp_send_skb+0x16d/0x270
|
||||||
|
udp_sendmsg+0x2bf/0x980
|
||||||
|
inet_sendmsg+0x67/0xa0
|
||||||
|
sock_sendmsg+0x38/0x50
|
||||||
|
___sys_sendmsg+0x14e/0x270
|
||||||
|
} hitcount: 76
|
||||||
|
{ stacktrace:
|
||||||
|
netif_rx_internal+0xb2/0xd0
|
||||||
|
netif_rx+0x1c/0x60
|
||||||
|
loopback_xmit+0x6c/0xb0
|
||||||
|
dev_hard_start_xmit+0x219/0x3a0
|
||||||
|
__dev_queue_xmit+0x415/0x4f0
|
||||||
|
dev_queue_xmit_sk+0x13/0x20
|
||||||
|
ip_finish_output2+0x237/0x340
|
||||||
|
ip_finish_output+0x113/0x1d0
|
||||||
|
ip_output+0x66/0xc0
|
||||||
|
ip_local_out_sk+0x31/0x40
|
||||||
|
ip_send_skb+0x1a/0x50
|
||||||
|
udp_send_skb+0x16d/0x270
|
||||||
|
udp_sendmsg+0x2bf/0x980
|
||||||
|
inet_sendmsg+0x67/0xa0
|
||||||
|
sock_sendmsg+0x38/0x50
|
||||||
|
___sys_sendmsg+0x269/0x270
|
||||||
|
} hitcount: 77
|
||||||
|
{ stacktrace:
|
||||||
|
netif_rx_internal+0xb2/0xd0
|
||||||
|
netif_rx+0x1c/0x60
|
||||||
|
loopback_xmit+0x6c/0xb0
|
||||||
|
dev_hard_start_xmit+0x219/0x3a0
|
||||||
|
__dev_queue_xmit+0x415/0x4f0
|
||||||
|
dev_queue_xmit_sk+0x13/0x20
|
||||||
|
ip_finish_output2+0x237/0x340
|
||||||
|
ip_finish_output+0x113/0x1d0
|
||||||
|
ip_output+0x66/0xc0
|
||||||
|
ip_local_out_sk+0x31/0x40
|
||||||
|
ip_send_skb+0x1a/0x50
|
||||||
|
udp_send_skb+0x16d/0x270
|
||||||
|
udp_sendmsg+0x2bf/0x980
|
||||||
|
inet_sendmsg+0x67/0xa0
|
||||||
|
sock_sendmsg+0x38/0x50
|
||||||
|
SYSC_sendto+0xef/0x170
|
||||||
|
} hitcount: 88
|
||||||
|
{ stacktrace:
|
||||||
|
_do_fork+0x18e/0x330
|
||||||
|
SyS_clone+0x19/0x20
|
||||||
|
entry_SYSCALL_64_fastpath+0x12/0x6a
|
||||||
|
} hitcount: 244
|
||||||
|
|
||||||
|
Totals:
|
||||||
|
Hits: 489
|
||||||
|
Entries: 7
|
||||||
|
Dropped: 0
|
||||||
|
|
|
@ -3842,6 +3842,7 @@ static const char readme_msg[] =
|
||||||
"\t [:sort=<field1[,field2,...]>]\n"
|
"\t [:sort=<field1[,field2,...]>]\n"
|
||||||
"\t [:size=#entries]\n"
|
"\t [:size=#entries]\n"
|
||||||
"\t [:pause][:continue][:clear]\n"
|
"\t [:pause][:continue][:clear]\n"
|
||||||
|
"\t [:name=histname1]\n"
|
||||||
"\t [if <filter>]\n\n"
|
"\t [if <filter>]\n\n"
|
||||||
"\t When a matching event is hit, an entry is added to a hash\n"
|
"\t When a matching event is hit, an entry is added to a hash\n"
|
||||||
"\t table using the key(s) and value(s) named, and the value of a\n"
|
"\t table using the key(s) and value(s) named, and the value of a\n"
|
||||||
|
@ -3854,13 +3855,18 @@ static const char readme_msg[] =
|
||||||
"\t specified using the 'sort' keyword. The sort direction can\n"
|
"\t specified using the 'sort' keyword. The sort direction can\n"
|
||||||
"\t be modified by appending '.descending' or '.ascending' to a\n"
|
"\t be modified by appending '.descending' or '.ascending' to a\n"
|
||||||
"\t sort field. The 'size' parameter can be used to specify more\n"
|
"\t sort field. The 'size' parameter can be used to specify more\n"
|
||||||
"\t or fewer than the default 2048 entries for the hashtable size.\n\n"
|
"\t or fewer than the default 2048 entries for the hashtable size.\n"
|
||||||
|
"\t If a hist trigger is given a name using the 'name' parameter,\n"
|
||||||
|
"\t its histogram data will be shared with other triggers of the\n"
|
||||||
|
"\t same name, and trigger hits will update this common data.\n\n"
|
||||||
"\t Reading the 'hist' file for the event will dump the hash\n"
|
"\t Reading the 'hist' file for the event will dump the hash\n"
|
||||||
"\t table in its entirety to stdout. If there are multiple hist\n"
|
"\t table in its entirety to stdout. If there are multiple hist\n"
|
||||||
"\t triggers attached to an event, there will be a table for each\n"
|
"\t triggers attached to an event, there will be a table for each\n"
|
||||||
"\t trigger in the output. The default format used to display a\n"
|
"\t trigger in the output. The table displayed for a named\n"
|
||||||
"\t given field can be modified by appending any of the following\n"
|
"\t trigger will be the same as any other instance having the\n"
|
||||||
"\t modifiers to the field name, as applicable:\n\n"
|
"\t same name. The default format used to display a given field\n"
|
||||||
|
"\t can be modified by appending any of the following modifiers\n"
|
||||||
|
"\t to the field name, as applicable:\n\n"
|
||||||
"\t .hex display a number as a hex value\n"
|
"\t .hex display a number as a hex value\n"
|
||||||
"\t .sym display an address as a symbol\n"
|
"\t .sym display an address as a symbol\n"
|
||||||
"\t .sym-offset display an address as a symbol and offset\n"
|
"\t .sym-offset display an address as a symbol and offset\n"
|
||||||
|
|
|
@ -117,6 +117,7 @@ struct hist_trigger_attrs {
|
||||||
char *keys_str;
|
char *keys_str;
|
||||||
char *vals_str;
|
char *vals_str;
|
||||||
char *sort_key_str;
|
char *sort_key_str;
|
||||||
|
char *name;
|
||||||
bool pause;
|
bool pause;
|
||||||
bool cont;
|
bool cont;
|
||||||
bool clear;
|
bool clear;
|
||||||
|
@ -200,6 +201,7 @@ static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
|
||||||
if (!attrs)
|
if (!attrs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
kfree(attrs->name);
|
||||||
kfree(attrs->sort_key_str);
|
kfree(attrs->sort_key_str);
|
||||||
kfree(attrs->keys_str);
|
kfree(attrs->keys_str);
|
||||||
kfree(attrs->vals_str);
|
kfree(attrs->vals_str);
|
||||||
|
@ -227,6 +229,8 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
|
||||||
attrs->vals_str = kstrdup(str, GFP_KERNEL);
|
attrs->vals_str = kstrdup(str, GFP_KERNEL);
|
||||||
else if (strncmp(str, "sort=", strlen("sort=")) == 0)
|
else if (strncmp(str, "sort=", strlen("sort=")) == 0)
|
||||||
attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
|
attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
|
||||||
|
else if (strncmp(str, "name=", strlen("name=")) == 0)
|
||||||
|
attrs->name = kstrdup(str, GFP_KERNEL);
|
||||||
else if (strcmp(str, "pause") == 0)
|
else if (strcmp(str, "pause") == 0)
|
||||||
attrs->pause = true;
|
attrs->pause = true;
|
||||||
else if ((strcmp(str, "cont") == 0) ||
|
else if ((strcmp(str, "cont") == 0) ||
|
||||||
|
@ -1131,7 +1135,12 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||||
struct hist_field *key_field;
|
struct hist_field *key_field;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
seq_puts(m, "hist:keys=");
|
seq_puts(m, "hist:");
|
||||||
|
|
||||||
|
if (data->name)
|
||||||
|
seq_printf(m, "%s:", data->name);
|
||||||
|
|
||||||
|
seq_puts(m, "keys=");
|
||||||
|
|
||||||
for_each_hist_key_field(i, hist_data) {
|
for_each_hist_key_field(i, hist_data) {
|
||||||
key_field = hist_data->fields[i];
|
key_field = hist_data->fields[i];
|
||||||
|
@ -1196,6 +1205,19 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int event_hist_trigger_init(struct event_trigger_ops *ops,
|
||||||
|
struct event_trigger_data *data)
|
||||||
|
{
|
||||||
|
struct hist_trigger_data *hist_data = data->private_data;
|
||||||
|
|
||||||
|
if (!data->ref && hist_data->attrs->name)
|
||||||
|
save_named_trigger(hist_data->attrs->name, data);
|
||||||
|
|
||||||
|
data->ref++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void event_hist_trigger_free(struct event_trigger_ops *ops,
|
static void event_hist_trigger_free(struct event_trigger_ops *ops,
|
||||||
struct event_trigger_data *data)
|
struct event_trigger_data *data)
|
||||||
{
|
{
|
||||||
|
@ -1206,6 +1228,8 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops,
|
||||||
|
|
||||||
data->ref--;
|
data->ref--;
|
||||||
if (!data->ref) {
|
if (!data->ref) {
|
||||||
|
if (data->name)
|
||||||
|
del_named_trigger(data);
|
||||||
trigger_data_free(data);
|
trigger_data_free(data);
|
||||||
destroy_hist_data(hist_data);
|
destroy_hist_data(hist_data);
|
||||||
}
|
}
|
||||||
|
@ -1214,10 +1238,44 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops,
|
||||||
static struct event_trigger_ops event_hist_trigger_ops = {
|
static struct event_trigger_ops event_hist_trigger_ops = {
|
||||||
.func = event_hist_trigger,
|
.func = event_hist_trigger,
|
||||||
.print = event_hist_trigger_print,
|
.print = event_hist_trigger_print,
|
||||||
.init = event_trigger_init,
|
.init = event_hist_trigger_init,
|
||||||
.free = event_hist_trigger_free,
|
.free = event_hist_trigger_free,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int event_hist_trigger_named_init(struct event_trigger_ops *ops,
|
||||||
|
struct event_trigger_data *data)
|
||||||
|
{
|
||||||
|
data->ref++;
|
||||||
|
|
||||||
|
save_named_trigger(data->named_data->name, data);
|
||||||
|
|
||||||
|
event_hist_trigger_init(ops, data->named_data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_hist_trigger_named_free(struct event_trigger_ops *ops,
|
||||||
|
struct event_trigger_data *data)
|
||||||
|
{
|
||||||
|
if (WARN_ON_ONCE(data->ref <= 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
event_hist_trigger_free(ops, data->named_data);
|
||||||
|
|
||||||
|
data->ref--;
|
||||||
|
if (!data->ref) {
|
||||||
|
del_named_trigger(data);
|
||||||
|
trigger_data_free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct event_trigger_ops event_hist_trigger_named_ops = {
|
||||||
|
.func = event_hist_trigger,
|
||||||
|
.print = event_hist_trigger_print,
|
||||||
|
.init = event_hist_trigger_named_init,
|
||||||
|
.free = event_hist_trigger_named_free,
|
||||||
|
};
|
||||||
|
|
||||||
static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
|
static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
|
||||||
char *param)
|
char *param)
|
||||||
{
|
{
|
||||||
|
@ -1227,26 +1285,54 @@ static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
|
||||||
static void hist_clear(struct event_trigger_data *data)
|
static void hist_clear(struct event_trigger_data *data)
|
||||||
{
|
{
|
||||||
struct hist_trigger_data *hist_data = data->private_data;
|
struct hist_trigger_data *hist_data = data->private_data;
|
||||||
bool paused;
|
|
||||||
|
|
||||||
paused = data->paused;
|
if (data->name)
|
||||||
data->paused = true;
|
pause_named_trigger(data);
|
||||||
|
|
||||||
synchronize_sched();
|
synchronize_sched();
|
||||||
|
|
||||||
tracing_map_clear(hist_data->map);
|
tracing_map_clear(hist_data->map);
|
||||||
|
|
||||||
data->paused = paused;
|
if (data->name)
|
||||||
|
unpause_named_trigger(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compatible_field(struct ftrace_event_field *field,
|
||||||
|
struct ftrace_event_field *test_field)
|
||||||
|
{
|
||||||
|
if (field == test_field)
|
||||||
|
return true;
|
||||||
|
if (field == NULL || test_field == NULL)
|
||||||
|
return false;
|
||||||
|
if (strcmp(field->name, test_field->name) != 0)
|
||||||
|
return false;
|
||||||
|
if (strcmp(field->type, test_field->type) != 0)
|
||||||
|
return false;
|
||||||
|
if (field->size != test_field->size)
|
||||||
|
return false;
|
||||||
|
if (field->is_signed != test_field->is_signed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hist_trigger_match(struct event_trigger_data *data,
|
static bool hist_trigger_match(struct event_trigger_data *data,
|
||||||
struct event_trigger_data *data_test)
|
struct event_trigger_data *data_test,
|
||||||
|
struct event_trigger_data *named_data,
|
||||||
|
bool ignore_filter)
|
||||||
{
|
{
|
||||||
struct tracing_map_sort_key *sort_key, *sort_key_test;
|
struct tracing_map_sort_key *sort_key, *sort_key_test;
|
||||||
struct hist_trigger_data *hist_data, *hist_data_test;
|
struct hist_trigger_data *hist_data, *hist_data_test;
|
||||||
struct hist_field *key_field, *key_field_test;
|
struct hist_field *key_field, *key_field_test;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
if (named_data && (named_data != data_test) &&
|
||||||
|
(named_data != data_test->named_data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!named_data && is_named_trigger(data_test))
|
||||||
|
return false;
|
||||||
|
|
||||||
hist_data = data->private_data;
|
hist_data = data->private_data;
|
||||||
hist_data_test = data_test->private_data;
|
hist_data_test = data_test->private_data;
|
||||||
|
|
||||||
|
@ -1255,9 +1341,11 @@ static bool hist_trigger_match(struct event_trigger_data *data,
|
||||||
hist_data->n_sort_keys != hist_data_test->n_sort_keys)
|
hist_data->n_sort_keys != hist_data_test->n_sort_keys)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((data->filter_str && !data_test->filter_str) ||
|
if (!ignore_filter) {
|
||||||
(!data->filter_str && data_test->filter_str))
|
if ((data->filter_str && !data_test->filter_str) ||
|
||||||
return false;
|
(!data->filter_str && data_test->filter_str))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for_each_hist_field(i, hist_data) {
|
for_each_hist_field(i, hist_data) {
|
||||||
key_field = hist_data->fields[i];
|
key_field = hist_data->fields[i];
|
||||||
|
@ -1265,7 +1353,7 @@ static bool hist_trigger_match(struct event_trigger_data *data,
|
||||||
|
|
||||||
if (key_field->flags != key_field_test->flags)
|
if (key_field->flags != key_field_test->flags)
|
||||||
return false;
|
return false;
|
||||||
if (key_field->field != key_field_test->field)
|
if (!compatible_field(key_field->field, key_field_test->field))
|
||||||
return false;
|
return false;
|
||||||
if (key_field->offset != key_field_test->offset)
|
if (key_field->offset != key_field_test->offset)
|
||||||
return false;
|
return false;
|
||||||
|
@ -1280,7 +1368,7 @@ static bool hist_trigger_match(struct event_trigger_data *data,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->filter_str &&
|
if (!ignore_filter && data->filter_str &&
|
||||||
(strcmp(data->filter_str, data_test->filter_str) != 0))
|
(strcmp(data->filter_str, data_test->filter_str) != 0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1292,12 +1380,26 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
|
||||||
struct trace_event_file *file)
|
struct trace_event_file *file)
|
||||||
{
|
{
|
||||||
struct hist_trigger_data *hist_data = data->private_data;
|
struct hist_trigger_data *hist_data = data->private_data;
|
||||||
struct event_trigger_data *test;
|
struct event_trigger_data *test, *named_data = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (hist_data->attrs->name) {
|
||||||
|
named_data = find_named_trigger(hist_data->attrs->name);
|
||||||
|
if (named_data) {
|
||||||
|
if (!hist_trigger_match(data, named_data, named_data,
|
||||||
|
true)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hist_data->attrs->name && !named_data)
|
||||||
|
goto new;
|
||||||
|
|
||||||
list_for_each_entry_rcu(test, &file->triggers, list) {
|
list_for_each_entry_rcu(test, &file->triggers, list) {
|
||||||
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||||
if (!hist_trigger_match(data, test))
|
if (!hist_trigger_match(data, test, named_data, false))
|
||||||
continue;
|
continue;
|
||||||
if (hist_data->attrs->pause)
|
if (hist_data->attrs->pause)
|
||||||
test->paused = true;
|
test->paused = true;
|
||||||
|
@ -1310,12 +1412,19 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new:
|
||||||
if (hist_data->attrs->cont || hist_data->attrs->clear) {
|
if (hist_data->attrs->cont || hist_data->attrs->clear) {
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (named_data) {
|
||||||
|
destroy_hist_data(data->private_data);
|
||||||
|
data->private_data = named_data->private_data;
|
||||||
|
set_named_trigger_data(data, named_data);
|
||||||
|
data->ops = &event_hist_trigger_named_ops;
|
||||||
|
}
|
||||||
|
|
||||||
if (hist_data->attrs->pause)
|
if (hist_data->attrs->pause)
|
||||||
data->paused = true;
|
data->paused = true;
|
||||||
|
|
||||||
|
@ -1329,6 +1438,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
|
||||||
ret++;
|
ret++;
|
||||||
|
|
||||||
update_cond_flag(file);
|
update_cond_flag(file);
|
||||||
|
|
||||||
if (trace_event_trigger_enable_disable(file, 1) < 0) {
|
if (trace_event_trigger_enable_disable(file, 1) < 0) {
|
||||||
list_del_rcu(&data->list);
|
list_del_rcu(&data->list);
|
||||||
update_cond_flag(file);
|
update_cond_flag(file);
|
||||||
|
@ -1342,12 +1452,16 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
|
||||||
struct event_trigger_data *data,
|
struct event_trigger_data *data,
|
||||||
struct trace_event_file *file)
|
struct trace_event_file *file)
|
||||||
{
|
{
|
||||||
struct event_trigger_data *test;
|
struct hist_trigger_data *hist_data = data->private_data;
|
||||||
|
struct event_trigger_data *test, *named_data = NULL;
|
||||||
bool unregistered = false;
|
bool unregistered = false;
|
||||||
|
|
||||||
|
if (hist_data->attrs->name)
|
||||||
|
named_data = find_named_trigger(hist_data->attrs->name);
|
||||||
|
|
||||||
list_for_each_entry_rcu(test, &file->triggers, list) {
|
list_for_each_entry_rcu(test, &file->triggers, list) {
|
||||||
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||||
if (!hist_trigger_match(data, test))
|
if (!hist_trigger_match(data, test, named_data, false))
|
||||||
continue;
|
continue;
|
||||||
unregistered = true;
|
unregistered = true;
|
||||||
list_del_rcu(&test->list);
|
list_del_rcu(&test->list);
|
||||||
|
|
Loading…
Reference in New Issue