Skip to content

Commit 69a0200

Browse files
tzanussirostedt
authored andcommitted
tracing: Add hist trigger support for stacktraces as keys
It's often useful to be able to use a stacktrace as a hash key, for keeping a count of the number of times a particular call path resulted in a trace event, for instance. Add a special key named 'stacktrace' which can be used as key in a 'keys=' param for this purpose: # echo hist:keys=stacktrace ... \ [ if filter] > event/trigger Link: http://lkml.kernel.org/r/87515e90b3785232a874a12156174635a348edb1.1457029949.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi <[email protected]> Tested-by: Masami Hiramatsu <[email protected]> Reviewed-by: Namhyung Kim <[email protected]> Signed-off-by: Steven Rostedt <[email protected]>
1 parent 3169619 commit 69a0200

File tree

2 files changed

+109
-42
lines changed

2 files changed

+109
-42
lines changed

kernel/trace/trace.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3843,14 +3843,14 @@ static const char readme_msg[] =
38433843
"\t table using the key(s) and value(s) named, and the value of a\n"
38443844
"\t sum called 'hitcount' is incremented. Keys and values\n"
38453845
"\t correspond to fields in the event's format description. Keys\n"
3846-
"\t can be any field. Compound keys consisting of up to two\n"
3847-
"\t fields can be specified by the 'keys' keyword. Values must\n"
3848-
"\t correspond to numeric fields. Sort keys consisting of up to\n"
3849-
"\t two fields can be specified using the 'sort' keyword. The\n"
3850-
"\t sort direction can be modified by appending '.descending' or\n"
3851-
"\t '.ascending' to a sort field. The 'size' parameter can be\n"
3852-
"\t used to specify more or fewer than the default 2048 entries\n"
3853-
"\t for the hashtable size.\n\n"
3846+
"\t can be any field, or the special string 'stacktrace'.\n"
3847+
"\t Compound keys consisting of up to two fields can be specified\n"
3848+
"\t by the 'keys' keyword. Values must correspond to numeric\n"
3849+
"\t fields. Sort keys consisting of up to two fields can be\n"
3850+
"\t specified using the 'sort' keyword. The sort direction can\n"
3851+
"\t be modified by appending '.descending' or '.ascending' to a\n"
3852+
"\t sort field. The 'size' parameter can be used to specify more\n"
3853+
"\t or fewer than the default 2048 entries for the hashtable size.\n\n"
38543854
"\t Reading the 'hist' file for the event will dump the hash\n"
38553855
"\t table in its entirety to stdout. The default format used to\n"
38563856
"\t display a given field can be modified by appending any of the\n"

kernel/trace/trace_events_hist.c

Lines changed: 101 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ struct hist_field {
3535
unsigned int offset;
3636
};
3737

38+
static u64 hist_field_none(struct hist_field *field, void *event)
39+
{
40+
return 0;
41+
}
42+
3843
static u64 hist_field_counter(struct hist_field *field, void *event)
3944
{
4045
return 1;
@@ -73,8 +78,12 @@ DEFINE_HIST_FIELD_FN(u8);
7378
#define for_each_hist_key_field(i, hist_data) \
7479
for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++)
7580

81+
#define HIST_STACKTRACE_DEPTH 16
82+
#define HIST_STACKTRACE_SIZE (HIST_STACKTRACE_DEPTH * sizeof(unsigned long))
83+
#define HIST_STACKTRACE_SKIP 5
84+
7685
#define HITCOUNT_IDX 0
77-
#define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + sizeof(u64))
86+
#define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE)
7887

7988
enum hist_field_flags {
8089
HIST_FIELD_FL_HITCOUNT = 1,
@@ -85,6 +94,7 @@ enum hist_field_flags {
8594
HIST_FIELD_FL_SYM_OFFSET = 32,
8695
HIST_FIELD_FL_EXECNAME = 64,
8796
HIST_FIELD_FL_SYSCALL = 128,
97+
HIST_FIELD_FL_STACKTRACE = 256,
8898
};
8999

90100
struct hist_trigger_attrs {
@@ -323,6 +333,11 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
323333
goto out;
324334
}
325335

336+
if (flags & HIST_FIELD_FL_STACKTRACE) {
337+
hist_field->fn = hist_field_none;
338+
goto out;
339+
}
340+
326341
if (is_string_field(field)) {
327342
flags |= HIST_FIELD_FL_STRING;
328343
hist_field->fn = hist_field_string;
@@ -456,41 +471,46 @@ static int create_key_field(struct hist_trigger_data *hist_data,
456471
struct ftrace_event_field *field = NULL;
457472
unsigned long flags = 0;
458473
unsigned int key_size;
459-
char *field_name;
460474
int ret = 0;
461475

462476
if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX))
463477
return -EINVAL;
464478

465479
flags |= HIST_FIELD_FL_KEY;
466480

467-
field_name = strsep(&field_str, ".");
468-
if (field_str) {
469-
if (strcmp(field_str, "hex") == 0)
470-
flags |= HIST_FIELD_FL_HEX;
471-
else if (strcmp(field_str, "sym") == 0)
472-
flags |= HIST_FIELD_FL_SYM;
473-
else if (strcmp(field_str, "sym-offset") == 0)
474-
flags |= HIST_FIELD_FL_SYM_OFFSET;
475-
else if ((strcmp(field_str, "execname") == 0) &&
476-
(strcmp(field_name, "common_pid") == 0))
477-
flags |= HIST_FIELD_FL_EXECNAME;
478-
else if (strcmp(field_str, "syscall") == 0)
479-
flags |= HIST_FIELD_FL_SYSCALL;
480-
else {
481+
if (strcmp(field_str, "stacktrace") == 0) {
482+
flags |= HIST_FIELD_FL_STACKTRACE;
483+
key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH;
484+
} else {
485+
char *field_name = strsep(&field_str, ".");
486+
487+
if (field_str) {
488+
if (strcmp(field_str, "hex") == 0)
489+
flags |= HIST_FIELD_FL_HEX;
490+
else if (strcmp(field_str, "sym") == 0)
491+
flags |= HIST_FIELD_FL_SYM;
492+
else if (strcmp(field_str, "sym-offset") == 0)
493+
flags |= HIST_FIELD_FL_SYM_OFFSET;
494+
else if ((strcmp(field_str, "execname") == 0) &&
495+
(strcmp(field_name, "common_pid") == 0))
496+
flags |= HIST_FIELD_FL_EXECNAME;
497+
else if (strcmp(field_str, "syscall") == 0)
498+
flags |= HIST_FIELD_FL_SYSCALL;
499+
else {
500+
ret = -EINVAL;
501+
goto out;
502+
}
503+
}
504+
505+
field = trace_find_event_field(file->event_call, field_name);
506+
if (!field) {
481507
ret = -EINVAL;
482508
goto out;
483509
}
484-
}
485510

486-
field = trace_find_event_field(file->event_call, field_name);
487-
if (!field) {
488-
ret = -EINVAL;
489-
goto out;
511+
key_size = field->size;
490512
}
491513

492-
key_size = field->size;
493-
494514
hist_data->fields[key_idx] = create_hist_field(field, flags);
495515
if (!hist_data->fields[key_idx]) {
496516
ret = -ENOMEM;
@@ -679,7 +699,9 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
679699

680700
field = hist_field->field;
681701

682-
if (is_string_field(field))
702+
if (hist_field->flags & HIST_FIELD_FL_STACKTRACE)
703+
cmp_fn = tracing_map_cmp_none;
704+
else if (is_string_field(field))
683705
cmp_fn = tracing_map_cmp_string;
684706
else
685707
cmp_fn = tracing_map_cmp_num(field->size,
@@ -786,7 +808,9 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
786808
static void event_hist_trigger(struct event_trigger_data *data, void *rec)
787809
{
788810
struct hist_trigger_data *hist_data = data->private_data;
811+
unsigned long entries[HIST_STACKTRACE_DEPTH];
789812
char compound_key[HIST_KEY_SIZE_MAX];
813+
struct stack_trace stacktrace;
790814
struct hist_field *key_field;
791815
struct tracing_map_elt *elt;
792816
u64 field_contents;
@@ -799,15 +823,27 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec)
799823
for_each_hist_key_field(i, hist_data) {
800824
key_field = hist_data->fields[i];
801825

802-
field_contents = key_field->fn(key_field, rec);
803-
if (key_field->flags & HIST_FIELD_FL_STRING)
804-
key = (void *)(unsigned long)field_contents;
805-
else
806-
key = (void *)&field_contents;
826+
if (key_field->flags & HIST_FIELD_FL_STACKTRACE) {
827+
stacktrace.max_entries = HIST_STACKTRACE_DEPTH;
828+
stacktrace.entries = entries;
829+
stacktrace.nr_entries = 0;
830+
stacktrace.skip = HIST_STACKTRACE_SKIP;
807831

808-
if (hist_data->n_keys > 1) {
809-
memcpy(compound_key + key_field->offset, key,
810-
key_field->size);
832+
memset(stacktrace.entries, 0, HIST_STACKTRACE_SIZE);
833+
save_stack_trace(&stacktrace);
834+
835+
key = entries;
836+
} else {
837+
field_contents = key_field->fn(key_field, rec);
838+
if (key_field->flags & HIST_FIELD_FL_STRING)
839+
key = (void *)(unsigned long)field_contents;
840+
else
841+
key = (void *)&field_contents;
842+
843+
if (hist_data->n_keys > 1) {
844+
memcpy(compound_key + key_field->offset, key,
845+
key_field->size);
846+
}
811847
}
812848
}
813849

@@ -819,13 +855,32 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec)
819855
hist_trigger_elt_update(hist_data, elt, rec);
820856
}
821857

858+
static void hist_trigger_stacktrace_print(struct seq_file *m,
859+
unsigned long *stacktrace_entries,
860+
unsigned int max_entries)
861+
{
862+
char str[KSYM_SYMBOL_LEN];
863+
unsigned int spaces = 8;
864+
unsigned int i;
865+
866+
for (i = 0; i < max_entries; i++) {
867+
if (stacktrace_entries[i] == ULONG_MAX)
868+
return;
869+
870+
seq_printf(m, "%*c", 1 + spaces, ' ');
871+
sprint_symbol(str, stacktrace_entries[i]);
872+
seq_printf(m, "%s\n", str);
873+
}
874+
}
875+
822876
static void
823877
hist_trigger_entry_print(struct seq_file *m,
824878
struct hist_trigger_data *hist_data, void *key,
825879
struct tracing_map_elt *elt)
826880
{
827881
struct hist_field *key_field;
828882
char str[KSYM_SYMBOL_LEN];
883+
bool multiline = false;
829884
unsigned int i;
830885
u64 uval;
831886

@@ -867,6 +922,12 @@ hist_trigger_entry_print(struct seq_file *m,
867922

868923
seq_printf(m, "%s: %-30s[%3llu]",
869924
key_field->field->name, syscall_name, uval);
925+
} else if (key_field->flags & HIST_FIELD_FL_STACKTRACE) {
926+
seq_puts(m, "stacktrace:\n");
927+
hist_trigger_stacktrace_print(m,
928+
key + key_field->offset,
929+
HIST_STACKTRACE_DEPTH);
930+
multiline = true;
870931
} else if (key_field->flags & HIST_FIELD_FL_STRING) {
871932
seq_printf(m, "%s: %-50s", key_field->field->name,
872933
(char *)(key + key_field->offset));
@@ -877,7 +938,10 @@ hist_trigger_entry_print(struct seq_file *m,
877938
}
878939
}
879940

880-
seq_puts(m, " }");
941+
if (!multiline)
942+
seq_puts(m, " ");
943+
944+
seq_puts(m, "}");
881945

882946
seq_printf(m, " hitcount: %10llu",
883947
tracing_map_read_sum(elt, HITCOUNT_IDX));
@@ -1021,7 +1085,10 @@ static int event_hist_trigger_print(struct seq_file *m,
10211085
if (i > hist_data->n_vals)
10221086
seq_puts(m, ",");
10231087

1024-
hist_field_print(m, key_field);
1088+
if (key_field->flags & HIST_FIELD_FL_STACKTRACE)
1089+
seq_puts(m, "stacktrace");
1090+
else
1091+
hist_field_print(m, key_field);
10251092
}
10261093

10271094
seq_puts(m, ":vals=");

0 commit comments

Comments
 (0)