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:
Tom Zanussi 2016-03-03 12:54:59 -06:00 committed by Steven Rostedt
parent db1388b4ff
commit 5463bfda32
3 changed files with 407 additions and 29 deletions

View File

@ -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

View File

@ -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"

View File

@ -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 (!ignore_filter) {
if ((data->filter_str && !data_test->filter_str) || if ((data->filter_str && !data_test->filter_str) ||
(!data->filter_str && data_test->filter_str)) (!data->filter_str && data_test->filter_str))
return false; 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);