diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/trace/Kconfig | 16 | ||||
-rw-r--r-- | kernel/trace/Makefile | 1 | ||||
-rw-r--r-- | kernel/trace/trace.c | 17 | ||||
-rw-r--r-- | kernel/trace/trace.h | 7 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 4 | ||||
-rw-r--r-- | kernel/trace/trace_events_hist.c | 849 | ||||
-rw-r--r-- | kernel/trace/trace_events_trigger.c | 1 |
7 files changed, 895 insertions, 0 deletions
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index d39556fd863a..fafeaf803bd0 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
@@ -538,6 +538,22 @@ config TRACING_MAP | |||
538 | generally used outside of that context, and is normally | 538 | generally used outside of that context, and is normally |
539 | selected by tracers that use it. | 539 | selected by tracers that use it. |
540 | 540 | ||
541 | config HIST_TRIGGERS | ||
542 | bool "Histogram triggers" | ||
543 | depends on ARCH_HAVE_NMI_SAFE_CMPXCHG | ||
544 | select TRACING_MAP | ||
545 | default n | ||
546 | help | ||
547 | Hist triggers allow one or more arbitrary trace event fields | ||
548 | to be aggregated into hash tables and dumped to stdout by | ||
549 | reading a debugfs/tracefs file. They're useful for | ||
550 | gathering quick and dirty (though precise) summaries of | ||
551 | event activity as an initial guide for further investigation | ||
552 | using more advanced tools. | ||
553 | |||
554 | See Documentation/trace/events.txt. | ||
555 | If in doubt, say N. | ||
556 | |||
541 | config MMIOTRACE_TEST | 557 | config MMIOTRACE_TEST |
542 | tristate "Test module for mmiotrace" | 558 | tristate "Test module for mmiotrace" |
543 | depends on MMIOTRACE && m | 559 | depends on MMIOTRACE && m |
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 4255c4057aaa..979e7bfbde7a 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -54,6 +54,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o | |||
54 | endif | 54 | endif |
55 | obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o | 55 | obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o |
56 | obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o | 56 | obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o |
57 | obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o | ||
57 | obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o | 58 | obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o |
58 | obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o | 59 | obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o |
59 | obj-$(CONFIG_TRACEPOINTS) += power-traces.o | 60 | obj-$(CONFIG_TRACEPOINTS) += power-traces.o |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0d12dbde8399..6cf8fd03b028 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -3813,6 +3813,9 @@ static const char readme_msg[] = | |||
3813 | #ifdef CONFIG_TRACER_SNAPSHOT | 3813 | #ifdef CONFIG_TRACER_SNAPSHOT |
3814 | "\t\t snapshot\n" | 3814 | "\t\t snapshot\n" |
3815 | #endif | 3815 | #endif |
3816 | #ifdef CONFIG_HIST_TRIGGERS | ||
3817 | "\t\t hist (see below)\n" | ||
3818 | #endif | ||
3816 | "\t example: echo traceoff > events/block/block_unplug/trigger\n" | 3819 | "\t example: echo traceoff > events/block/block_unplug/trigger\n" |
3817 | "\t echo traceoff:3 > events/block/block_unplug/trigger\n" | 3820 | "\t echo traceoff:3 > events/block/block_unplug/trigger\n" |
3818 | "\t echo 'enable_event:kmem:kmalloc:3 if nr_rq > 1' > \\\n" | 3821 | "\t echo 'enable_event:kmem:kmalloc:3 if nr_rq > 1' > \\\n" |
@@ -3828,6 +3831,20 @@ static const char readme_msg[] = | |||
3828 | "\t To remove a trigger with a count:\n" | 3831 | "\t To remove a trigger with a count:\n" |
3829 | "\t echo '!<trigger>:0 > <system>/<event>/trigger\n" | 3832 | "\t echo '!<trigger>:0 > <system>/<event>/trigger\n" |
3830 | "\t Filters can be ignored when removing a trigger.\n" | 3833 | "\t Filters can be ignored when removing a trigger.\n" |
3834 | #ifdef CONFIG_HIST_TRIGGERS | ||
3835 | " hist trigger\t- If set, event hits are aggregated into a hash table\n" | ||
3836 | "\t Format: hist:keys=<field1>\n" | ||
3837 | "\t [:size=#entries]\n" | ||
3838 | "\t [if <filter>]\n\n" | ||
3839 | "\t When a matching event is hit, an entry is added to a hash\n" | ||
3840 | "\t table using the key named, and the value of a sum called\n" | ||
3841 | "\t 'hitcount' is incremented. Keys correspond to fields in the\n" | ||
3842 | "\t event's format description. Keys can be any field. The\n" | ||
3843 | "\t 'size' parameter can be used to specify more or fewer than\n" | ||
3844 | "\t the default 2048 entries for the hashtable size.\n\n" | ||
3845 | "\t Reading the 'hist' file for the event will dump the hash\n" | ||
3846 | "\t table in its entirety to stdout." | ||
3847 | #endif | ||
3831 | ; | 3848 | ; |
3832 | 3849 | ||
3833 | static ssize_t | 3850 | static ssize_t |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2525042760e6..505f8a45f426 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -1162,6 +1162,13 @@ extern struct mutex event_mutex; | |||
1162 | extern struct list_head ftrace_events; | 1162 | extern struct list_head ftrace_events; |
1163 | 1163 | ||
1164 | extern const struct file_operations event_trigger_fops; | 1164 | extern const struct file_operations event_trigger_fops; |
1165 | extern const struct file_operations event_hist_fops; | ||
1166 | |||
1167 | #ifdef CONFIG_HIST_TRIGGERS | ||
1168 | extern int register_trigger_hist_cmd(void); | ||
1169 | #else | ||
1170 | static inline int register_trigger_hist_cmd(void) { return 0; } | ||
1171 | #endif | ||
1165 | 1172 | ||
1166 | extern int register_trigger_cmds(void); | 1173 | extern int register_trigger_cmds(void); |
1167 | extern void clear_event_triggers(struct trace_array *tr); | 1174 | extern void clear_event_triggers(struct trace_array *tr); |
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index add81dff7520..e7cb983ee93c 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -2141,6 +2141,10 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file) | |||
2141 | trace_create_file("trigger", 0644, file->dir, file, | 2141 | trace_create_file("trigger", 0644, file->dir, file, |
2142 | &event_trigger_fops); | 2142 | &event_trigger_fops); |
2143 | 2143 | ||
2144 | #ifdef CONFIG_HIST_TRIGGERS | ||
2145 | trace_create_file("hist", 0444, file->dir, file, | ||
2146 | &event_hist_fops); | ||
2147 | #endif | ||
2144 | trace_create_file("format", 0444, file->dir, call, | 2148 | trace_create_file("format", 0444, file->dir, call, |
2145 | &ftrace_event_format_fops); | 2149 | &ftrace_event_format_fops); |
2146 | 2150 | ||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c new file mode 100644 index 000000000000..23b45e462117 --- /dev/null +++ b/kernel/trace/trace_events_hist.c | |||
@@ -0,0 +1,849 @@ | |||
1 | /* | ||
2 | * trace_events_hist - trace event hist triggers | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com> | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/kallsyms.h> | ||
19 | #include <linux/mutex.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/stacktrace.h> | ||
22 | |||
23 | #include "tracing_map.h" | ||
24 | #include "trace.h" | ||
25 | |||
26 | struct hist_field; | ||
27 | |||
28 | typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event); | ||
29 | |||
30 | struct hist_field { | ||
31 | struct ftrace_event_field *field; | ||
32 | unsigned long flags; | ||
33 | hist_field_fn_t fn; | ||
34 | unsigned int size; | ||
35 | }; | ||
36 | |||
37 | static u64 hist_field_counter(struct hist_field *field, void *event) | ||
38 | { | ||
39 | return 1; | ||
40 | } | ||
41 | |||
42 | static u64 hist_field_string(struct hist_field *hist_field, void *event) | ||
43 | { | ||
44 | char *addr = (char *)(event + hist_field->field->offset); | ||
45 | |||
46 | return (u64)(unsigned long)addr; | ||
47 | } | ||
48 | |||
49 | #define DEFINE_HIST_FIELD_FN(type) \ | ||
50 | static u64 hist_field_##type(struct hist_field *hist_field, void *event)\ | ||
51 | { \ | ||
52 | type *addr = (type *)(event + hist_field->field->offset); \ | ||
53 | \ | ||
54 | return (u64)*addr; \ | ||
55 | } | ||
56 | |||
57 | DEFINE_HIST_FIELD_FN(s64); | ||
58 | DEFINE_HIST_FIELD_FN(u64); | ||
59 | DEFINE_HIST_FIELD_FN(s32); | ||
60 | DEFINE_HIST_FIELD_FN(u32); | ||
61 | DEFINE_HIST_FIELD_FN(s16); | ||
62 | DEFINE_HIST_FIELD_FN(u16); | ||
63 | DEFINE_HIST_FIELD_FN(s8); | ||
64 | DEFINE_HIST_FIELD_FN(u8); | ||
65 | |||
66 | #define for_each_hist_field(i, hist_data) \ | ||
67 | for ((i) = 0; (i) < (hist_data)->n_fields; (i)++) | ||
68 | |||
69 | #define for_each_hist_val_field(i, hist_data) \ | ||
70 | for ((i) = 0; (i) < (hist_data)->n_vals; (i)++) | ||
71 | |||
72 | #define for_each_hist_key_field(i, hist_data) \ | ||
73 | for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++) | ||
74 | |||
75 | #define HITCOUNT_IDX 0 | ||
76 | #define HIST_KEY_MAX 1 | ||
77 | #define HIST_KEY_SIZE_MAX MAX_FILTER_STR_VAL | ||
78 | |||
79 | enum hist_field_flags { | ||
80 | HIST_FIELD_FL_HITCOUNT = 1, | ||
81 | HIST_FIELD_FL_KEY = 2, | ||
82 | HIST_FIELD_FL_STRING = 4, | ||
83 | }; | ||
84 | |||
85 | struct hist_trigger_attrs { | ||
86 | char *keys_str; | ||
87 | unsigned int map_bits; | ||
88 | }; | ||
89 | |||
90 | struct hist_trigger_data { | ||
91 | struct hist_field *fields[TRACING_MAP_FIELDS_MAX]; | ||
92 | unsigned int n_vals; | ||
93 | unsigned int n_keys; | ||
94 | unsigned int n_fields; | ||
95 | unsigned int key_size; | ||
96 | struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX]; | ||
97 | unsigned int n_sort_keys; | ||
98 | struct trace_event_file *event_file; | ||
99 | struct hist_trigger_attrs *attrs; | ||
100 | struct tracing_map *map; | ||
101 | }; | ||
102 | |||
103 | static hist_field_fn_t select_value_fn(int field_size, int field_is_signed) | ||
104 | { | ||
105 | hist_field_fn_t fn = NULL; | ||
106 | |||
107 | switch (field_size) { | ||
108 | case 8: | ||
109 | if (field_is_signed) | ||
110 | fn = hist_field_s64; | ||
111 | else | ||
112 | fn = hist_field_u64; | ||
113 | break; | ||
114 | case 4: | ||
115 | if (field_is_signed) | ||
116 | fn = hist_field_s32; | ||
117 | else | ||
118 | fn = hist_field_u32; | ||
119 | break; | ||
120 | case 2: | ||
121 | if (field_is_signed) | ||
122 | fn = hist_field_s16; | ||
123 | else | ||
124 | fn = hist_field_u16; | ||
125 | break; | ||
126 | case 1: | ||
127 | if (field_is_signed) | ||
128 | fn = hist_field_s8; | ||
129 | else | ||
130 | fn = hist_field_u8; | ||
131 | break; | ||
132 | } | ||
133 | |||
134 | return fn; | ||
135 | } | ||
136 | |||
137 | static int parse_map_size(char *str) | ||
138 | { | ||
139 | unsigned long size, map_bits; | ||
140 | int ret; | ||
141 | |||
142 | strsep(&str, "="); | ||
143 | if (!str) { | ||
144 | ret = -EINVAL; | ||
145 | goto out; | ||
146 | } | ||
147 | |||
148 | ret = kstrtoul(str, 0, &size); | ||
149 | if (ret) | ||
150 | goto out; | ||
151 | |||
152 | map_bits = ilog2(roundup_pow_of_two(size)); | ||
153 | if (map_bits < TRACING_MAP_BITS_MIN || | ||
154 | map_bits > TRACING_MAP_BITS_MAX) | ||
155 | ret = -EINVAL; | ||
156 | else | ||
157 | ret = map_bits; | ||
158 | out: | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs) | ||
163 | { | ||
164 | if (!attrs) | ||
165 | return; | ||
166 | |||
167 | kfree(attrs->keys_str); | ||
168 | kfree(attrs); | ||
169 | } | ||
170 | |||
171 | static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str) | ||
172 | { | ||
173 | struct hist_trigger_attrs *attrs; | ||
174 | int ret = 0; | ||
175 | |||
176 | attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); | ||
177 | if (!attrs) | ||
178 | return ERR_PTR(-ENOMEM); | ||
179 | |||
180 | while (trigger_str) { | ||
181 | char *str = strsep(&trigger_str, ":"); | ||
182 | |||
183 | if ((strncmp(str, "key=", strlen("key=")) == 0) || | ||
184 | (strncmp(str, "keys=", strlen("keys=")) == 0)) | ||
185 | attrs->keys_str = kstrdup(str, GFP_KERNEL); | ||
186 | else if (strncmp(str, "size=", strlen("size=")) == 0) { | ||
187 | int map_bits = parse_map_size(str); | ||
188 | |||
189 | if (map_bits < 0) { | ||
190 | ret = map_bits; | ||
191 | goto free; | ||
192 | } | ||
193 | attrs->map_bits = map_bits; | ||
194 | } else { | ||
195 | ret = -EINVAL; | ||
196 | goto free; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | if (!attrs->keys_str) { | ||
201 | ret = -EINVAL; | ||
202 | goto free; | ||
203 | } | ||
204 | |||
205 | return attrs; | ||
206 | free: | ||
207 | destroy_hist_trigger_attrs(attrs); | ||
208 | |||
209 | return ERR_PTR(ret); | ||
210 | } | ||
211 | |||
212 | static void destroy_hist_field(struct hist_field *hist_field) | ||
213 | { | ||
214 | kfree(hist_field); | ||
215 | } | ||
216 | |||
217 | static struct hist_field *create_hist_field(struct ftrace_event_field *field, | ||
218 | unsigned long flags) | ||
219 | { | ||
220 | struct hist_field *hist_field; | ||
221 | |||
222 | if (field && is_function_field(field)) | ||
223 | return NULL; | ||
224 | |||
225 | hist_field = kzalloc(sizeof(struct hist_field), GFP_KERNEL); | ||
226 | if (!hist_field) | ||
227 | return NULL; | ||
228 | |||
229 | if (flags & HIST_FIELD_FL_HITCOUNT) { | ||
230 | hist_field->fn = hist_field_counter; | ||
231 | goto out; | ||
232 | } | ||
233 | |||
234 | if (is_string_field(field)) { | ||
235 | flags |= HIST_FIELD_FL_STRING; | ||
236 | hist_field->fn = hist_field_string; | ||
237 | } else { | ||
238 | hist_field->fn = select_value_fn(field->size, | ||
239 | field->is_signed); | ||
240 | if (!hist_field->fn) { | ||
241 | destroy_hist_field(hist_field); | ||
242 | return NULL; | ||
243 | } | ||
244 | } | ||
245 | out: | ||
246 | hist_field->field = field; | ||
247 | hist_field->flags = flags; | ||
248 | |||
249 | return hist_field; | ||
250 | } | ||
251 | |||
252 | static void destroy_hist_fields(struct hist_trigger_data *hist_data) | ||
253 | { | ||
254 | unsigned int i; | ||
255 | |||
256 | for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) { | ||
257 | if (hist_data->fields[i]) { | ||
258 | destroy_hist_field(hist_data->fields[i]); | ||
259 | hist_data->fields[i] = NULL; | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | static int create_hitcount_val(struct hist_trigger_data *hist_data) | ||
265 | { | ||
266 | hist_data->fields[HITCOUNT_IDX] = | ||
267 | create_hist_field(NULL, HIST_FIELD_FL_HITCOUNT); | ||
268 | if (!hist_data->fields[HITCOUNT_IDX]) | ||
269 | return -ENOMEM; | ||
270 | |||
271 | hist_data->n_vals++; | ||
272 | |||
273 | if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) | ||
274 | return -EINVAL; | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static int create_val_fields(struct hist_trigger_data *hist_data, | ||
280 | struct trace_event_file *file) | ||
281 | { | ||
282 | int ret; | ||
283 | |||
284 | ret = create_hitcount_val(hist_data); | ||
285 | |||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | static int create_key_field(struct hist_trigger_data *hist_data, | ||
290 | unsigned int key_idx, | ||
291 | struct trace_event_file *file, | ||
292 | char *field_str) | ||
293 | { | ||
294 | struct ftrace_event_field *field = NULL; | ||
295 | unsigned long flags = 0; | ||
296 | unsigned int key_size; | ||
297 | int ret = 0; | ||
298 | |||
299 | if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX)) | ||
300 | return -EINVAL; | ||
301 | |||
302 | flags |= HIST_FIELD_FL_KEY; | ||
303 | |||
304 | field = trace_find_event_field(file->event_call, field_str); | ||
305 | if (!field) { | ||
306 | ret = -EINVAL; | ||
307 | goto out; | ||
308 | } | ||
309 | |||
310 | key_size = field->size; | ||
311 | |||
312 | hist_data->fields[key_idx] = create_hist_field(field, flags); | ||
313 | if (!hist_data->fields[key_idx]) { | ||
314 | ret = -ENOMEM; | ||
315 | goto out; | ||
316 | } | ||
317 | |||
318 | key_size = ALIGN(key_size, sizeof(u64)); | ||
319 | hist_data->fields[key_idx]->size = key_size; | ||
320 | hist_data->key_size = key_size; | ||
321 | if (hist_data->key_size > HIST_KEY_SIZE_MAX) { | ||
322 | ret = -EINVAL; | ||
323 | goto out; | ||
324 | } | ||
325 | |||
326 | hist_data->n_keys++; | ||
327 | |||
328 | if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX)) | ||
329 | return -EINVAL; | ||
330 | |||
331 | ret = key_size; | ||
332 | out: | ||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | static int create_key_fields(struct hist_trigger_data *hist_data, | ||
337 | struct trace_event_file *file) | ||
338 | { | ||
339 | unsigned int i, n_vals = hist_data->n_vals; | ||
340 | char *fields_str, *field_str; | ||
341 | int ret = -EINVAL; | ||
342 | |||
343 | fields_str = hist_data->attrs->keys_str; | ||
344 | if (!fields_str) | ||
345 | goto out; | ||
346 | |||
347 | strsep(&fields_str, "="); | ||
348 | if (!fields_str) | ||
349 | goto out; | ||
350 | |||
351 | for (i = n_vals; i < n_vals + HIST_KEY_MAX; i++) { | ||
352 | field_str = strsep(&fields_str, ","); | ||
353 | if (!field_str) | ||
354 | break; | ||
355 | ret = create_key_field(hist_data, i, file, field_str); | ||
356 | if (ret < 0) | ||
357 | goto out; | ||
358 | } | ||
359 | if (fields_str) { | ||
360 | ret = -EINVAL; | ||
361 | goto out; | ||
362 | } | ||
363 | ret = 0; | ||
364 | out: | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | static int create_hist_fields(struct hist_trigger_data *hist_data, | ||
369 | struct trace_event_file *file) | ||
370 | { | ||
371 | int ret; | ||
372 | |||
373 | ret = create_val_fields(hist_data, file); | ||
374 | if (ret) | ||
375 | goto out; | ||
376 | |||
377 | ret = create_key_fields(hist_data, file); | ||
378 | if (ret) | ||
379 | goto out; | ||
380 | |||
381 | hist_data->n_fields = hist_data->n_vals + hist_data->n_keys; | ||
382 | out: | ||
383 | return ret; | ||
384 | } | ||
385 | |||
386 | static int create_sort_keys(struct hist_trigger_data *hist_data) | ||
387 | { | ||
388 | int ret = 0; | ||
389 | |||
390 | hist_data->n_sort_keys = 1; /* sort_keys[0] is always hitcount */ | ||
391 | |||
392 | return ret; | ||
393 | } | ||
394 | |||
395 | static void destroy_hist_data(struct hist_trigger_data *hist_data) | ||
396 | { | ||
397 | destroy_hist_trigger_attrs(hist_data->attrs); | ||
398 | destroy_hist_fields(hist_data); | ||
399 | tracing_map_destroy(hist_data->map); | ||
400 | kfree(hist_data); | ||
401 | } | ||
402 | |||
403 | static int create_tracing_map_fields(struct hist_trigger_data *hist_data) | ||
404 | { | ||
405 | struct tracing_map *map = hist_data->map; | ||
406 | struct ftrace_event_field *field; | ||
407 | struct hist_field *hist_field; | ||
408 | unsigned int i, idx; | ||
409 | |||
410 | for_each_hist_field(i, hist_data) { | ||
411 | hist_field = hist_data->fields[i]; | ||
412 | if (hist_field->flags & HIST_FIELD_FL_KEY) { | ||
413 | tracing_map_cmp_fn_t cmp_fn; | ||
414 | |||
415 | field = hist_field->field; | ||
416 | |||
417 | if (is_string_field(field)) | ||
418 | cmp_fn = tracing_map_cmp_string; | ||
419 | else | ||
420 | cmp_fn = tracing_map_cmp_num(field->size, | ||
421 | field->is_signed); | ||
422 | idx = tracing_map_add_key_field(map, 0, cmp_fn); | ||
423 | } else | ||
424 | idx = tracing_map_add_sum_field(map); | ||
425 | |||
426 | if (idx < 0) | ||
427 | return idx; | ||
428 | } | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static struct hist_trigger_data * | ||
434 | create_hist_data(unsigned int map_bits, | ||
435 | struct hist_trigger_attrs *attrs, | ||
436 | struct trace_event_file *file) | ||
437 | { | ||
438 | struct hist_trigger_data *hist_data; | ||
439 | int ret = 0; | ||
440 | |||
441 | hist_data = kzalloc(sizeof(*hist_data), GFP_KERNEL); | ||
442 | if (!hist_data) | ||
443 | return ERR_PTR(-ENOMEM); | ||
444 | |||
445 | hist_data->attrs = attrs; | ||
446 | |||
447 | ret = create_hist_fields(hist_data, file); | ||
448 | if (ret) | ||
449 | goto free; | ||
450 | |||
451 | ret = create_sort_keys(hist_data); | ||
452 | if (ret) | ||
453 | goto free; | ||
454 | |||
455 | hist_data->map = tracing_map_create(map_bits, hist_data->key_size, | ||
456 | NULL, hist_data); | ||
457 | if (IS_ERR(hist_data->map)) { | ||
458 | ret = PTR_ERR(hist_data->map); | ||
459 | hist_data->map = NULL; | ||
460 | goto free; | ||
461 | } | ||
462 | |||
463 | ret = create_tracing_map_fields(hist_data); | ||
464 | if (ret) | ||
465 | goto free; | ||
466 | |||
467 | ret = tracing_map_init(hist_data->map); | ||
468 | if (ret) | ||
469 | goto free; | ||
470 | |||
471 | hist_data->event_file = file; | ||
472 | out: | ||
473 | return hist_data; | ||
474 | free: | ||
475 | hist_data->attrs = NULL; | ||
476 | |||
477 | destroy_hist_data(hist_data); | ||
478 | |||
479 | hist_data = ERR_PTR(ret); | ||
480 | |||
481 | goto out; | ||
482 | } | ||
483 | |||
484 | static void hist_trigger_elt_update(struct hist_trigger_data *hist_data, | ||
485 | struct tracing_map_elt *elt, | ||
486 | void *rec) | ||
487 | { | ||
488 | struct hist_field *hist_field; | ||
489 | unsigned int i; | ||
490 | u64 hist_val; | ||
491 | |||
492 | for_each_hist_val_field(i, hist_data) { | ||
493 | hist_field = hist_data->fields[i]; | ||
494 | hist_val = hist_field->fn(hist_field, rec); | ||
495 | tracing_map_update_sum(elt, i, hist_val); | ||
496 | } | ||
497 | } | ||
498 | |||
499 | static void event_hist_trigger(struct event_trigger_data *data, void *rec) | ||
500 | { | ||
501 | struct hist_trigger_data *hist_data = data->private_data; | ||
502 | struct hist_field *key_field; | ||
503 | struct tracing_map_elt *elt; | ||
504 | u64 field_contents; | ||
505 | void *key = NULL; | ||
506 | unsigned int i; | ||
507 | |||
508 | for_each_hist_key_field(i, hist_data) { | ||
509 | key_field = hist_data->fields[i]; | ||
510 | |||
511 | field_contents = key_field->fn(key_field, rec); | ||
512 | if (key_field->flags & HIST_FIELD_FL_STRING) | ||
513 | key = (void *)(unsigned long)field_contents; | ||
514 | else | ||
515 | key = (void *)&field_contents; | ||
516 | } | ||
517 | |||
518 | elt = tracing_map_insert(hist_data->map, key); | ||
519 | if (elt) | ||
520 | hist_trigger_elt_update(hist_data, elt, rec); | ||
521 | } | ||
522 | |||
523 | static void | ||
524 | hist_trigger_entry_print(struct seq_file *m, | ||
525 | struct hist_trigger_data *hist_data, void *key, | ||
526 | struct tracing_map_elt *elt) | ||
527 | { | ||
528 | struct hist_field *key_field; | ||
529 | unsigned int i; | ||
530 | u64 uval; | ||
531 | |||
532 | seq_puts(m, "{ "); | ||
533 | |||
534 | for_each_hist_key_field(i, hist_data) { | ||
535 | key_field = hist_data->fields[i]; | ||
536 | |||
537 | if (i > hist_data->n_vals) | ||
538 | seq_puts(m, ", "); | ||
539 | |||
540 | if (key_field->flags & HIST_FIELD_FL_STRING) { | ||
541 | seq_printf(m, "%s: %-50s", key_field->field->name, | ||
542 | (char *)key); | ||
543 | } else { | ||
544 | uval = *(u64 *)key; | ||
545 | seq_printf(m, "%s: %10llu", | ||
546 | key_field->field->name, uval); | ||
547 | } | ||
548 | } | ||
549 | |||
550 | seq_puts(m, " }"); | ||
551 | |||
552 | seq_printf(m, " hitcount: %10llu", | ||
553 | tracing_map_read_sum(elt, HITCOUNT_IDX)); | ||
554 | |||
555 | seq_puts(m, "\n"); | ||
556 | } | ||
557 | |||
558 | static int print_entries(struct seq_file *m, | ||
559 | struct hist_trigger_data *hist_data) | ||
560 | { | ||
561 | struct tracing_map_sort_entry **sort_entries = NULL; | ||
562 | struct tracing_map *map = hist_data->map; | ||
563 | unsigned int i, n_entries; | ||
564 | |||
565 | n_entries = tracing_map_sort_entries(map, hist_data->sort_keys, | ||
566 | hist_data->n_sort_keys, | ||
567 | &sort_entries); | ||
568 | if (n_entries < 0) | ||
569 | return n_entries; | ||
570 | |||
571 | for (i = 0; i < n_entries; i++) | ||
572 | hist_trigger_entry_print(m, hist_data, | ||
573 | sort_entries[i]->key, | ||
574 | sort_entries[i]->elt); | ||
575 | |||
576 | tracing_map_destroy_sort_entries(sort_entries, n_entries); | ||
577 | |||
578 | return n_entries; | ||
579 | } | ||
580 | |||
581 | static int hist_show(struct seq_file *m, void *v) | ||
582 | { | ||
583 | struct event_trigger_data *test, *data = NULL; | ||
584 | struct trace_event_file *event_file; | ||
585 | struct hist_trigger_data *hist_data; | ||
586 | int n_entries, ret = 0; | ||
587 | |||
588 | mutex_lock(&event_mutex); | ||
589 | |||
590 | event_file = event_file_data(m->private); | ||
591 | if (unlikely(!event_file)) { | ||
592 | ret = -ENODEV; | ||
593 | goto out_unlock; | ||
594 | } | ||
595 | |||
596 | list_for_each_entry_rcu(test, &event_file->triggers, list) { | ||
597 | if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { | ||
598 | data = test; | ||
599 | break; | ||
600 | } | ||
601 | } | ||
602 | if (!data) | ||
603 | goto out_unlock; | ||
604 | |||
605 | seq_puts(m, "# event histogram\n#\n# trigger info: "); | ||
606 | data->ops->print(m, data->ops, data); | ||
607 | seq_puts(m, "\n"); | ||
608 | |||
609 | hist_data = data->private_data; | ||
610 | n_entries = print_entries(m, hist_data); | ||
611 | if (n_entries < 0) { | ||
612 | ret = n_entries; | ||
613 | n_entries = 0; | ||
614 | } | ||
615 | |||
616 | seq_printf(m, "\nTotals:\n Hits: %llu\n Entries: %u\n Dropped: %llu\n", | ||
617 | (u64)atomic64_read(&hist_data->map->hits), | ||
618 | n_entries, (u64)atomic64_read(&hist_data->map->drops)); | ||
619 | out_unlock: | ||
620 | mutex_unlock(&event_mutex); | ||
621 | |||
622 | return ret; | ||
623 | } | ||
624 | |||
625 | static int event_hist_open(struct inode *inode, struct file *file) | ||
626 | { | ||
627 | return single_open(file, hist_show, file); | ||
628 | } | ||
629 | |||
630 | const struct file_operations event_hist_fops = { | ||
631 | .open = event_hist_open, | ||
632 | .read = seq_read, | ||
633 | .llseek = seq_lseek, | ||
634 | .release = single_release, | ||
635 | }; | ||
636 | |||
637 | static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) | ||
638 | { | ||
639 | seq_printf(m, "%s", hist_field->field->name); | ||
640 | } | ||
641 | |||
642 | static int event_hist_trigger_print(struct seq_file *m, | ||
643 | struct event_trigger_ops *ops, | ||
644 | struct event_trigger_data *data) | ||
645 | { | ||
646 | struct hist_trigger_data *hist_data = data->private_data; | ||
647 | struct hist_field *key_field; | ||
648 | unsigned int i; | ||
649 | |||
650 | seq_puts(m, "hist:keys="); | ||
651 | |||
652 | for_each_hist_key_field(i, hist_data) { | ||
653 | key_field = hist_data->fields[i]; | ||
654 | |||
655 | if (i > hist_data->n_vals) | ||
656 | seq_puts(m, ","); | ||
657 | |||
658 | hist_field_print(m, key_field); | ||
659 | } | ||
660 | |||
661 | seq_puts(m, ":vals="); | ||
662 | seq_puts(m, "hitcount"); | ||
663 | |||
664 | seq_puts(m, ":sort="); | ||
665 | seq_puts(m, "hitcount"); | ||
666 | |||
667 | seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); | ||
668 | |||
669 | if (data->filter_str) | ||
670 | seq_printf(m, " if %s", data->filter_str); | ||
671 | |||
672 | seq_puts(m, " [active]"); | ||
673 | |||
674 | seq_putc(m, '\n'); | ||
675 | |||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | static void event_hist_trigger_free(struct event_trigger_ops *ops, | ||
680 | struct event_trigger_data *data) | ||
681 | { | ||
682 | struct hist_trigger_data *hist_data = data->private_data; | ||
683 | |||
684 | if (WARN_ON_ONCE(data->ref <= 0)) | ||
685 | return; | ||
686 | |||
687 | data->ref--; | ||
688 | if (!data->ref) { | ||
689 | trigger_data_free(data); | ||
690 | destroy_hist_data(hist_data); | ||
691 | } | ||
692 | } | ||
693 | |||
694 | static struct event_trigger_ops event_hist_trigger_ops = { | ||
695 | .func = event_hist_trigger, | ||
696 | .print = event_hist_trigger_print, | ||
697 | .init = event_trigger_init, | ||
698 | .free = event_hist_trigger_free, | ||
699 | }; | ||
700 | |||
701 | static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd, | ||
702 | char *param) | ||
703 | { | ||
704 | return &event_hist_trigger_ops; | ||
705 | } | ||
706 | |||
707 | static int hist_register_trigger(char *glob, struct event_trigger_ops *ops, | ||
708 | struct event_trigger_data *data, | ||
709 | struct trace_event_file *file) | ||
710 | { | ||
711 | struct event_trigger_data *test; | ||
712 | int ret = 0; | ||
713 | |||
714 | list_for_each_entry_rcu(test, &file->triggers, list) { | ||
715 | if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { | ||
716 | ret = -EEXIST; | ||
717 | goto out; | ||
718 | } | ||
719 | } | ||
720 | |||
721 | if (data->ops->init) { | ||
722 | ret = data->ops->init(data->ops, data); | ||
723 | if (ret < 0) | ||
724 | goto out; | ||
725 | } | ||
726 | |||
727 | list_add_rcu(&data->list, &file->triggers); | ||
728 | ret++; | ||
729 | |||
730 | update_cond_flag(file); | ||
731 | if (trace_event_trigger_enable_disable(file, 1) < 0) { | ||
732 | list_del_rcu(&data->list); | ||
733 | update_cond_flag(file); | ||
734 | ret--; | ||
735 | } | ||
736 | out: | ||
737 | return ret; | ||
738 | } | ||
739 | |||
740 | static int event_hist_trigger_func(struct event_command *cmd_ops, | ||
741 | struct trace_event_file *file, | ||
742 | char *glob, char *cmd, char *param) | ||
743 | { | ||
744 | unsigned int hist_trigger_bits = TRACING_MAP_BITS_DEFAULT; | ||
745 | struct event_trigger_data *trigger_data; | ||
746 | struct hist_trigger_attrs *attrs; | ||
747 | struct event_trigger_ops *trigger_ops; | ||
748 | struct hist_trigger_data *hist_data; | ||
749 | char *trigger; | ||
750 | int ret = 0; | ||
751 | |||
752 | if (!param) | ||
753 | return -EINVAL; | ||
754 | |||
755 | /* separate the trigger from the filter (k:v [if filter]) */ | ||
756 | trigger = strsep(¶m, " \t"); | ||
757 | if (!trigger) | ||
758 | return -EINVAL; | ||
759 | |||
760 | attrs = parse_hist_trigger_attrs(trigger); | ||
761 | if (IS_ERR(attrs)) | ||
762 | return PTR_ERR(attrs); | ||
763 | |||
764 | if (attrs->map_bits) | ||
765 | hist_trigger_bits = attrs->map_bits; | ||
766 | |||
767 | hist_data = create_hist_data(hist_trigger_bits, attrs, file); | ||
768 | if (IS_ERR(hist_data)) { | ||
769 | destroy_hist_trigger_attrs(attrs); | ||
770 | return PTR_ERR(hist_data); | ||
771 | } | ||
772 | |||
773 | trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger); | ||
774 | |||
775 | ret = -ENOMEM; | ||
776 | trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); | ||
777 | if (!trigger_data) | ||
778 | goto out_free; | ||
779 | |||
780 | trigger_data->count = -1; | ||
781 | trigger_data->ops = trigger_ops; | ||
782 | trigger_data->cmd_ops = cmd_ops; | ||
783 | |||
784 | INIT_LIST_HEAD(&trigger_data->list); | ||
785 | RCU_INIT_POINTER(trigger_data->filter, NULL); | ||
786 | |||
787 | trigger_data->private_data = hist_data; | ||
788 | |||
789 | if (glob[0] == '!') { | ||
790 | cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); | ||
791 | ret = 0; | ||
792 | goto out_free; | ||
793 | } | ||
794 | |||
795 | if (!param) /* if param is non-empty, it's supposed to be a filter */ | ||
796 | goto out_reg; | ||
797 | |||
798 | if (!cmd_ops->set_filter) | ||
799 | goto out_reg; | ||
800 | |||
801 | ret = cmd_ops->set_filter(param, trigger_data, file); | ||
802 | if (ret < 0) | ||
803 | goto out_free; | ||
804 | out_reg: | ||
805 | ret = cmd_ops->reg(glob, trigger_ops, trigger_data, file); | ||
806 | /* | ||
807 | * The above returns on success the # of triggers registered, | ||
808 | * but if it didn't register any it returns zero. Consider no | ||
809 | * triggers registered a failure too. | ||
810 | */ | ||
811 | if (!ret) { | ||
812 | ret = -ENOENT; | ||
813 | goto out_free; | ||
814 | } else if (ret < 0) | ||
815 | goto out_free; | ||
816 | /* Just return zero, not the number of registered triggers */ | ||
817 | ret = 0; | ||
818 | out: | ||
819 | return ret; | ||
820 | out_free: | ||
821 | if (cmd_ops->set_filter) | ||
822 | cmd_ops->set_filter(NULL, trigger_data, NULL); | ||
823 | |||
824 | kfree(trigger_data); | ||
825 | |||
826 | destroy_hist_data(hist_data); | ||
827 | goto out; | ||
828 | } | ||
829 | |||
830 | static struct event_command trigger_hist_cmd = { | ||
831 | .name = "hist", | ||
832 | .trigger_type = ETT_EVENT_HIST, | ||
833 | .flags = EVENT_CMD_FL_NEEDS_REC, | ||
834 | .func = event_hist_trigger_func, | ||
835 | .reg = hist_register_trigger, | ||
836 | .unreg = unregister_trigger, | ||
837 | .get_trigger_ops = event_hist_get_trigger_ops, | ||
838 | .set_filter = set_trigger_filter, | ||
839 | }; | ||
840 | |||
841 | __init int register_trigger_hist_cmd(void) | ||
842 | { | ||
843 | int ret; | ||
844 | |||
845 | ret = register_event_command(&trigger_hist_cmd); | ||
846 | WARN_ON(ret < 0); | ||
847 | |||
848 | return ret; | ||
849 | } | ||
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index d67992f3bb0e..d29092afe005 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c | |||
@@ -1447,6 +1447,7 @@ __init int register_trigger_cmds(void) | |||
1447 | register_trigger_snapshot_cmd(); | 1447 | register_trigger_snapshot_cmd(); |
1448 | register_trigger_stacktrace_cmd(); | 1448 | register_trigger_stacktrace_cmd(); |
1449 | register_trigger_enable_disable_cmds(); | 1449 | register_trigger_enable_disable_cmds(); |
1450 | register_trigger_hist_cmd(); | ||
1450 | 1451 | ||
1451 | return 0; | 1452 | return 0; |
1452 | } | 1453 | } |