Skip to content

Commit 288709c

Browse files
committed
tracing: Allow stacktraces to be saved as histogram variables
Allow to save stacktraces into a histogram variable. This will be used by synthetic events to allow a stacktrace from one event to be passed and displayed by another event. The special keyword "stacktrace" is to be used to trigger a stack trace for the event that the histogram trigger is attached to. echo 'hist:keys=pid:st=stacktrace" > events/sched/sched_waking/trigger Currently nothing can get access to the "$st" variable above that contains the stack trace, but that will soon change. Link: https://lkml.kernel.org/r/20230117152235.856323729@goodmis.org Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Tom Zanussi <zanussi@kernel.org> Cc: Ross Zwisler <zwisler@google.com> Cc: Ching-lin Yu <chinglinyu@google.com> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
1 parent 19ff804 commit 288709c

1 file changed

Lines changed: 42 additions & 10 deletions

File tree

kernel/trace/trace_events_hist.c

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,8 @@ static const char *hist_field_name(struct hist_field *field,
13601360
field_name = field->name;
13611361
} else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
13621362
field_name = "common_timestamp";
1363+
else if (field->flags & HIST_FIELD_FL_STACKTRACE)
1364+
field_name = "stacktrace";
13631365
else if (field->flags & HIST_FIELD_FL_HITCOUNT)
13641366
field_name = "hitcount";
13651367

@@ -1980,6 +1982,10 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
19801982

19811983
if (flags & HIST_FIELD_FL_STACKTRACE) {
19821984
hist_field->fn_num = HIST_FIELD_FN_NOP;
1985+
hist_field->size = HIST_STACKTRACE_SIZE;
1986+
hist_field->type = kstrdup_const("unsigned long[]", GFP_KERNEL);
1987+
if (!hist_field->type)
1988+
goto free;
19831989
goto out;
19841990
}
19851991

@@ -2351,6 +2357,8 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
23512357
hist_data->enable_timestamps = true;
23522358
if (*flags & HIST_FIELD_FL_TIMESTAMP_USECS)
23532359
hist_data->attrs->ts_in_usecs = true;
2360+
} else if (strcmp(field_name, "stacktrace") == 0) {
2361+
*flags |= HIST_FIELD_FL_STACKTRACE;
23542362
} else if (strcmp(field_name, "common_cpu") == 0)
23552363
*flags |= HIST_FIELD_FL_CPU;
23562364
else if (strcmp(field_name, "hitcount") == 0)
@@ -3119,13 +3127,24 @@ static inline void __update_field_vars(struct tracing_map_elt *elt,
31193127
var_val = hist_fn_call(val, elt, buffer, rbe, rec);
31203128
var_idx = var->var.idx;
31213129

3122-
if (val->flags & HIST_FIELD_FL_STRING) {
3130+
if (val->flags & (HIST_FIELD_FL_STRING |
3131+
HIST_FIELD_FL_STACKTRACE)) {
31233132
char *str = elt_data->field_var_str[j++];
31243133
char *val_str = (char *)(uintptr_t)var_val;
31253134
unsigned int size;
31263135

3127-
size = min(val->size, STR_VAR_LEN_MAX);
3128-
strscpy(str, val_str, size);
3136+
if (val->flags & HIST_FIELD_FL_STRING) {
3137+
size = min(val->size, STR_VAR_LEN_MAX);
3138+
strscpy(str, val_str, size);
3139+
} else {
3140+
int e;
3141+
3142+
e = stack_trace_save((void *)str,
3143+
HIST_STACKTRACE_DEPTH,
3144+
HIST_STACKTRACE_SKIP);
3145+
if (e < HIST_STACKTRACE_DEPTH - 1)
3146+
((unsigned long *)str)[e] = 0;
3147+
}
31293148
var_val = (u64)(uintptr_t)str;
31303149
}
31313150
tracing_map_set_var(elt, var_idx, var_val);
@@ -3824,7 +3843,8 @@ static void save_field_var(struct hist_trigger_data *hist_data,
38243843
{
38253844
hist_data->field_vars[hist_data->n_field_vars++] = field_var;
38263845

3827-
if (field_var->val->flags & HIST_FIELD_FL_STRING)
3846+
/* Stack traces are saved in the string storage too */
3847+
if (field_var->val->flags & (HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE))
38283848
hist_data->n_field_var_str++;
38293849
}
38303850

@@ -4103,7 +4123,8 @@ static int action_create(struct hist_trigger_data *hist_data,
41034123
}
41044124

41054125
hist_data->save_vars[hist_data->n_save_vars++] = field_var;
4106-
if (field_var->val->flags & HIST_FIELD_FL_STRING)
4126+
if (field_var->val->flags &
4127+
(HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE))
41074128
hist_data->n_save_var_str++;
41084129
kfree(param);
41094130
}
@@ -4351,7 +4372,8 @@ static int create_var_field(struct hist_trigger_data *hist_data,
43514372
if (!ret && hist_data->fields[val_idx]->flags & HIST_FIELD_FL_EXECNAME)
43524373
update_var_execname(hist_data->fields[val_idx]);
43534374

4354-
if (!ret && hist_data->fields[val_idx]->flags & HIST_FIELD_FL_STRING)
4375+
if (!ret && hist_data->fields[val_idx]->flags &
4376+
(HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE))
43554377
hist_data->fields[val_idx]->var_str_idx = hist_data->n_var_str++;
43564378

43574379
return ret;
@@ -5092,7 +5114,8 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
50925114
if (hist_field->flags & HIST_FIELD_FL_VAR) {
50935115
var_idx = hist_field->var.idx;
50945116

5095-
if (hist_field->flags & HIST_FIELD_FL_STRING) {
5117+
if (hist_field->flags &
5118+
(HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE)) {
50965119
unsigned int str_start, var_str_idx, idx;
50975120
char *str, *val_str;
50985121
unsigned int size;
@@ -5105,9 +5128,18 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
51055128
str = elt_data->field_var_str[idx];
51065129
val_str = (char *)(uintptr_t)hist_val;
51075130

5108-
size = min(hist_field->size, STR_VAR_LEN_MAX);
5109-
strscpy(str, val_str, size);
5110-
5131+
if (hist_field->flags & HIST_FIELD_FL_STRING) {
5132+
size = min(hist_field->size, STR_VAR_LEN_MAX);
5133+
strscpy(str, val_str, size);
5134+
} else {
5135+
int e;
5136+
5137+
e = stack_trace_save((void *)str,
5138+
HIST_STACKTRACE_DEPTH,
5139+
HIST_STACKTRACE_SKIP);
5140+
if (e < HIST_STACKTRACE_DEPTH - 1)
5141+
((unsigned long *)str)[e] = 0;
5142+
}
51115143
hist_val = (u64)(uintptr_t)str;
51125144
}
51135145
tracing_map_set_var(elt, var_idx, hist_val);

0 commit comments

Comments
 (0)