tracing: Add hist trigger support for compound keys
Allow users to specify multiple trace event fields to use in keys by allowing multiple fields in the 'keys=' keyword. With this addition, any unique combination of any of the fields named in the 'keys' keyword will result in a new entry being added to the hash table. Link: http://lkml.kernel.org/r/0cfa24e6ac3b0dcece7737d94aa1f322ae3afc4b.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
f2606835d7
commit
76a3b0c8ac
|
@ -3833,7 +3833,7 @@ static const char readme_msg[] =
|
||||||
"\t Filters can be ignored when removing a trigger.\n"
|
"\t Filters can be ignored when removing a trigger.\n"
|
||||||
#ifdef CONFIG_HIST_TRIGGERS
|
#ifdef CONFIG_HIST_TRIGGERS
|
||||||
" hist trigger\t- If set, event hits are aggregated into a hash table\n"
|
" hist trigger\t- If set, event hits are aggregated into a hash table\n"
|
||||||
"\t Format: hist:keys=<field1>\n"
|
"\t Format: hist:keys=<field1[,field2,...]>\n"
|
||||||
"\t [:values=<field1[,field2,...]>]\n"
|
"\t [:values=<field1[,field2,...]>]\n"
|
||||||
"\t [:size=#entries]\n"
|
"\t [:size=#entries]\n"
|
||||||
"\t [if <filter>]\n\n"
|
"\t [if <filter>]\n\n"
|
||||||
|
@ -3841,9 +3841,11 @@ static const char readme_msg[] =
|
||||||
"\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"
|
||||||
"\t sum called 'hitcount' is incremented. Keys and values\n"
|
"\t sum called 'hitcount' is incremented. Keys and values\n"
|
||||||
"\t correspond to fields in the event's format description. Keys\n"
|
"\t correspond to fields in the event's format description. Keys\n"
|
||||||
"\t can be any field. Values must correspond to numeric fields.\n"
|
"\t can be any field. Compound keys consisting of up to two\n"
|
||||||
"\t The 'size' parameter can be used to specify more or fewer\n"
|
"\t fields can be specified by the 'keys' keyword. Values must\n"
|
||||||
"\t than the default 2048 entries for the hashtable size.\n\n"
|
"\t correspond to numeric fields. The 'size' parameter can be\n"
|
||||||
|
"\t used to specify more or fewer than the default 2048 entries\n"
|
||||||
|
"\t for the hashtable size.\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."
|
"\t table in its entirety to stdout."
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct hist_field {
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
hist_field_fn_t fn;
|
hist_field_fn_t fn;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
|
unsigned int offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
static u64 hist_field_counter(struct hist_field *field, void *event)
|
static u64 hist_field_counter(struct hist_field *field, void *event)
|
||||||
|
@ -73,8 +74,7 @@ DEFINE_HIST_FIELD_FN(u8);
|
||||||
for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++)
|
for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++)
|
||||||
|
|
||||||
#define HITCOUNT_IDX 0
|
#define HITCOUNT_IDX 0
|
||||||
#define HIST_KEY_MAX 1
|
#define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + sizeof(u64))
|
||||||
#define HIST_KEY_SIZE_MAX MAX_FILTER_STR_VAL
|
|
||||||
|
|
||||||
enum hist_field_flags {
|
enum hist_field_flags {
|
||||||
HIST_FIELD_FL_HITCOUNT = 1,
|
HIST_FIELD_FL_HITCOUNT = 1,
|
||||||
|
@ -351,6 +351,7 @@ static int create_val_fields(struct hist_trigger_data *hist_data,
|
||||||
|
|
||||||
static int create_key_field(struct hist_trigger_data *hist_data,
|
static int create_key_field(struct hist_trigger_data *hist_data,
|
||||||
unsigned int key_idx,
|
unsigned int key_idx,
|
||||||
|
unsigned int key_offset,
|
||||||
struct trace_event_file *file,
|
struct trace_event_file *file,
|
||||||
char *field_str)
|
char *field_str)
|
||||||
{
|
{
|
||||||
|
@ -380,7 +381,8 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||||
|
|
||||||
key_size = ALIGN(key_size, sizeof(u64));
|
key_size = ALIGN(key_size, sizeof(u64));
|
||||||
hist_data->fields[key_idx]->size = key_size;
|
hist_data->fields[key_idx]->size = key_size;
|
||||||
hist_data->key_size = key_size;
|
hist_data->fields[key_idx]->offset = key_offset;
|
||||||
|
hist_data->key_size += key_size;
|
||||||
if (hist_data->key_size > HIST_KEY_SIZE_MAX) {
|
if (hist_data->key_size > HIST_KEY_SIZE_MAX) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -399,7 +401,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||||
static int create_key_fields(struct hist_trigger_data *hist_data,
|
static int create_key_fields(struct hist_trigger_data *hist_data,
|
||||||
struct trace_event_file *file)
|
struct trace_event_file *file)
|
||||||
{
|
{
|
||||||
unsigned int i, n_vals = hist_data->n_vals;
|
unsigned int i, key_offset = 0, n_vals = hist_data->n_vals;
|
||||||
char *fields_str, *field_str;
|
char *fields_str, *field_str;
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
|
||||||
|
@ -411,13 +413,15 @@ static int create_key_fields(struct hist_trigger_data *hist_data,
|
||||||
if (!fields_str)
|
if (!fields_str)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
for (i = n_vals; i < n_vals + HIST_KEY_MAX; i++) {
|
for (i = n_vals; i < n_vals + TRACING_MAP_KEYS_MAX; i++) {
|
||||||
field_str = strsep(&fields_str, ",");
|
field_str = strsep(&fields_str, ",");
|
||||||
if (!field_str)
|
if (!field_str)
|
||||||
break;
|
break;
|
||||||
ret = create_key_field(hist_data, i, file, field_str);
|
ret = create_key_field(hist_data, i, key_offset,
|
||||||
|
file, field_str);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
key_offset += ret;
|
||||||
}
|
}
|
||||||
if (fields_str) {
|
if (fields_str) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -482,7 +486,10 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
|
||||||
else
|
else
|
||||||
cmp_fn = tracing_map_cmp_num(field->size,
|
cmp_fn = tracing_map_cmp_num(field->size,
|
||||||
field->is_signed);
|
field->is_signed);
|
||||||
idx = tracing_map_add_key_field(map, 0, cmp_fn);
|
idx = tracing_map_add_key_field(map,
|
||||||
|
hist_field->offset,
|
||||||
|
cmp_fn);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
idx = tracing_map_add_sum_field(map);
|
idx = tracing_map_add_sum_field(map);
|
||||||
|
|
||||||
|
@ -562,12 +569,16 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||||
static void event_hist_trigger(struct event_trigger_data *data, void *rec)
|
static void event_hist_trigger(struct event_trigger_data *data, void *rec)
|
||||||
{
|
{
|
||||||
struct hist_trigger_data *hist_data = data->private_data;
|
struct hist_trigger_data *hist_data = data->private_data;
|
||||||
|
char compound_key[HIST_KEY_SIZE_MAX];
|
||||||
struct hist_field *key_field;
|
struct hist_field *key_field;
|
||||||
struct tracing_map_elt *elt;
|
struct tracing_map_elt *elt;
|
||||||
u64 field_contents;
|
u64 field_contents;
|
||||||
void *key = NULL;
|
void *key = NULL;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
if (hist_data->n_keys > 1)
|
||||||
|
memset(compound_key, 0, hist_data->key_size);
|
||||||
|
|
||||||
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];
|
||||||
|
|
||||||
|
@ -576,8 +587,16 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec)
|
||||||
key = (void *)(unsigned long)field_contents;
|
key = (void *)(unsigned long)field_contents;
|
||||||
else
|
else
|
||||||
key = (void *)&field_contents;
|
key = (void *)&field_contents;
|
||||||
|
|
||||||
|
if (hist_data->n_keys > 1) {
|
||||||
|
memcpy(compound_key + key_field->offset, key,
|
||||||
|
key_field->size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hist_data->n_keys > 1)
|
||||||
|
key = compound_key;
|
||||||
|
|
||||||
elt = tracing_map_insert(hist_data->map, key);
|
elt = tracing_map_insert(hist_data->map, key);
|
||||||
if (elt)
|
if (elt)
|
||||||
hist_trigger_elt_update(hist_data, elt, rec);
|
hist_trigger_elt_update(hist_data, elt, rec);
|
||||||
|
@ -602,11 +621,11 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||||
|
|
||||||
if (key_field->flags & HIST_FIELD_FL_STRING) {
|
if (key_field->flags & HIST_FIELD_FL_STRING) {
|
||||||
seq_printf(m, "%s: %-50s", key_field->field->name,
|
seq_printf(m, "%s: %-50s", key_field->field->name,
|
||||||
(char *)key);
|
(char *)(key + key_field->offset));
|
||||||
} else {
|
} else {
|
||||||
uval = *(u64 *)key;
|
uval = *(u64 *)(key + key_field->offset);
|
||||||
seq_printf(m, "%s: %10llu",
|
seq_printf(m, "%s: %10llu", key_field->field->name,
|
||||||
key_field->field->name, uval);
|
uval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue