diff options
author | Tom Zanussi <tom.zanussi@linux.intel.com> | 2013-10-24 09:59:25 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-12-20 18:40:24 -0500 |
commit | 2a2df321158817811c5dc206dce808e0aa9f6d89 (patch) | |
tree | fd93466f3bf01a224e55f488eb199befb382beca /kernel/trace/trace_events_trigger.c | |
parent | 85f2b08268c014e290b600ba49fa85530600eaa1 (diff) |
tracing: Add 'traceon' and 'traceoff' event trigger commands
Add 'traceon' and 'traceoff' event_command commands. traceon and
traceoff event triggers are added by the user via these commands in a
similar way and using practically the same syntax as the analagous
'traceon' and 'traceoff' ftrace function commands, but instead of
writing to the set_ftrace_filter file, the traceon and traceoff
triggers are written to the per-event 'trigger' files:
echo 'traceon' > .../tracing/events/somesys/someevent/trigger
echo 'traceoff' > .../tracing/events/somesys/someevent/trigger
The above command will turn tracing on or off whenever someevent is
hit.
This also adds a 'count' version that limits the number of times the
command will be invoked:
echo 'traceon:N' > .../tracing/events/somesys/someevent/trigger
echo 'traceoff:N' > .../tracing/events/somesys/someevent/trigger
Where N is the number of times the command will be invoked.
The above commands will will turn tracing on or off whenever someevent
is hit, but only N times.
Some common register/unregister_trigger() implementations of the
event_command reg()/unreg() callbacks are also provided, which add and
remove trigger instances to the per-event list of triggers, and
arm/disarm them as appropriate. event_trigger_callback() is a
general-purpose event_command func() implementation that orchestrates
command parsing and registration for most normal commands.
Most event commands will use these, but some will override and
possibly reuse them.
The event_trigger_init(), event_trigger_free(), and
event_trigger_print() functions are meant to be common implementations
of the event_trigger_ops init(), free(), and print() ops,
respectively.
Most trigger_ops implementations will use these, but some will
override and possibly reuse them.
Link: http://lkml.kernel.org/r/00a52816703b98d2072947478dd6e2d70cde5197.1382622043.git.tom.zanussi@linux.intel.com
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace/trace_events_trigger.c')
-rw-r--r-- | kernel/trace/trace_events_trigger.c | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 60a6a6d66dc0..4ea72ee169a7 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c | |||
@@ -28,6 +28,13 @@ | |||
28 | static LIST_HEAD(trigger_commands); | 28 | static LIST_HEAD(trigger_commands); |
29 | static DEFINE_MUTEX(trigger_cmd_mutex); | 29 | static DEFINE_MUTEX(trigger_cmd_mutex); |
30 | 30 | ||
31 | static void | ||
32 | trigger_data_free(struct event_trigger_data *data) | ||
33 | { | ||
34 | synchronize_sched(); /* make sure current triggers exit before free */ | ||
35 | kfree(data); | ||
36 | } | ||
37 | |||
31 | /** | 38 | /** |
32 | * event_triggers_call - Call triggers associated with a trace event | 39 | * event_triggers_call - Call triggers associated with a trace event |
33 | * @file: The ftrace_event_file associated with the event | 40 | * @file: The ftrace_event_file associated with the event |
@@ -224,6 +231,129 @@ const struct file_operations event_trigger_fops = { | |||
224 | .release = event_trigger_release, | 231 | .release = event_trigger_release, |
225 | }; | 232 | }; |
226 | 233 | ||
234 | /* | ||
235 | * Currently we only register event commands from __init, so mark this | ||
236 | * __init too. | ||
237 | */ | ||
238 | static __init int register_event_command(struct event_command *cmd) | ||
239 | { | ||
240 | struct event_command *p; | ||
241 | int ret = 0; | ||
242 | |||
243 | mutex_lock(&trigger_cmd_mutex); | ||
244 | list_for_each_entry(p, &trigger_commands, list) { | ||
245 | if (strcmp(cmd->name, p->name) == 0) { | ||
246 | ret = -EBUSY; | ||
247 | goto out_unlock; | ||
248 | } | ||
249 | } | ||
250 | list_add(&cmd->list, &trigger_commands); | ||
251 | out_unlock: | ||
252 | mutex_unlock(&trigger_cmd_mutex); | ||
253 | |||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * Currently we only unregister event commands from __init, so mark | ||
259 | * this __init too. | ||
260 | */ | ||
261 | static __init int unregister_event_command(struct event_command *cmd) | ||
262 | { | ||
263 | struct event_command *p, *n; | ||
264 | int ret = -ENODEV; | ||
265 | |||
266 | mutex_lock(&trigger_cmd_mutex); | ||
267 | list_for_each_entry_safe(p, n, &trigger_commands, list) { | ||
268 | if (strcmp(cmd->name, p->name) == 0) { | ||
269 | ret = 0; | ||
270 | list_del_init(&p->list); | ||
271 | goto out_unlock; | ||
272 | } | ||
273 | } | ||
274 | out_unlock: | ||
275 | mutex_unlock(&trigger_cmd_mutex); | ||
276 | |||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | /** | ||
281 | * event_trigger_print - Generic event_trigger_ops @print implementation | ||
282 | * @name: The name of the event trigger | ||
283 | * @m: The seq_file being printed to | ||
284 | * @data: Trigger-specific data | ||
285 | * @filter_str: filter_str to print, if present | ||
286 | * | ||
287 | * Common implementation for event triggers to print themselves. | ||
288 | * | ||
289 | * Usually wrapped by a function that simply sets the @name of the | ||
290 | * trigger command and then invokes this. | ||
291 | * | ||
292 | * Return: 0 on success, errno otherwise | ||
293 | */ | ||
294 | static int | ||
295 | event_trigger_print(const char *name, struct seq_file *m, | ||
296 | void *data, char *filter_str) | ||
297 | { | ||
298 | long count = (long)data; | ||
299 | |||
300 | seq_printf(m, "%s", name); | ||
301 | |||
302 | if (count == -1) | ||
303 | seq_puts(m, ":unlimited"); | ||
304 | else | ||
305 | seq_printf(m, ":count=%ld", count); | ||
306 | |||
307 | if (filter_str) | ||
308 | seq_printf(m, " if %s\n", filter_str); | ||
309 | else | ||
310 | seq_puts(m, "\n"); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * event_trigger_init - Generic event_trigger_ops @init implementation | ||
317 | * @ops: The trigger ops associated with the trigger | ||
318 | * @data: Trigger-specific data | ||
319 | * | ||
320 | * Common implementation of event trigger initialization. | ||
321 | * | ||
322 | * Usually used directly as the @init method in event trigger | ||
323 | * implementations. | ||
324 | * | ||
325 | * Return: 0 on success, errno otherwise | ||
326 | */ | ||
327 | static int | ||
328 | event_trigger_init(struct event_trigger_ops *ops, | ||
329 | struct event_trigger_data *data) | ||
330 | { | ||
331 | data->ref++; | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /** | ||
336 | * event_trigger_free - Generic event_trigger_ops @free implementation | ||
337 | * @ops: The trigger ops associated with the trigger | ||
338 | * @data: Trigger-specific data | ||
339 | * | ||
340 | * Common implementation of event trigger de-initialization. | ||
341 | * | ||
342 | * Usually used directly as the @free method in event trigger | ||
343 | * implementations. | ||
344 | */ | ||
345 | static void | ||
346 | event_trigger_free(struct event_trigger_ops *ops, | ||
347 | struct event_trigger_data *data) | ||
348 | { | ||
349 | if (WARN_ON_ONCE(data->ref <= 0)) | ||
350 | return; | ||
351 | |||
352 | data->ref--; | ||
353 | if (!data->ref) | ||
354 | trigger_data_free(data); | ||
355 | } | ||
356 | |||
227 | static int trace_event_trigger_enable_disable(struct ftrace_event_file *file, | 357 | static int trace_event_trigger_enable_disable(struct ftrace_event_file *file, |
228 | int trigger_enable) | 358 | int trigger_enable) |
229 | { | 359 | { |
@@ -272,7 +402,323 @@ clear_event_triggers(struct trace_array *tr) | |||
272 | } | 402 | } |
273 | } | 403 | } |
274 | 404 | ||
405 | /** | ||
406 | * register_trigger - Generic event_command @reg implementation | ||
407 | * @glob: The raw string used to register the trigger | ||
408 | * @ops: The trigger ops associated with the trigger | ||
409 | * @data: Trigger-specific data to associate with the trigger | ||
410 | * @file: The ftrace_event_file associated with the event | ||
411 | * | ||
412 | * Common implementation for event trigger registration. | ||
413 | * | ||
414 | * Usually used directly as the @reg method in event command | ||
415 | * implementations. | ||
416 | * | ||
417 | * Return: 0 on success, errno otherwise | ||
418 | */ | ||
419 | static int register_trigger(char *glob, struct event_trigger_ops *ops, | ||
420 | struct event_trigger_data *data, | ||
421 | struct ftrace_event_file *file) | ||
422 | { | ||
423 | struct event_trigger_data *test; | ||
424 | int ret = 0; | ||
425 | |||
426 | list_for_each_entry_rcu(test, &file->triggers, list) { | ||
427 | if (test->cmd_ops->trigger_type == data->cmd_ops->trigger_type) { | ||
428 | ret = -EEXIST; | ||
429 | goto out; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | if (data->ops->init) { | ||
434 | ret = data->ops->init(data->ops, data); | ||
435 | if (ret < 0) | ||
436 | goto out; | ||
437 | } | ||
438 | |||
439 | list_add_rcu(&data->list, &file->triggers); | ||
440 | ret++; | ||
441 | |||
442 | if (trace_event_trigger_enable_disable(file, 1) < 0) { | ||
443 | list_del_rcu(&data->list); | ||
444 | ret--; | ||
445 | } | ||
446 | out: | ||
447 | return ret; | ||
448 | } | ||
449 | |||
450 | /** | ||
451 | * unregister_trigger - Generic event_command @unreg implementation | ||
452 | * @glob: The raw string used to register the trigger | ||
453 | * @ops: The trigger ops associated with the trigger | ||
454 | * @test: Trigger-specific data used to find the trigger to remove | ||
455 | * @file: The ftrace_event_file associated with the event | ||
456 | * | ||
457 | * Common implementation for event trigger unregistration. | ||
458 | * | ||
459 | * Usually used directly as the @unreg method in event command | ||
460 | * implementations. | ||
461 | */ | ||
462 | static void unregister_trigger(char *glob, struct event_trigger_ops *ops, | ||
463 | struct event_trigger_data *test, | ||
464 | struct ftrace_event_file *file) | ||
465 | { | ||
466 | struct event_trigger_data *data; | ||
467 | bool unregistered = false; | ||
468 | |||
469 | list_for_each_entry_rcu(data, &file->triggers, list) { | ||
470 | if (data->cmd_ops->trigger_type == test->cmd_ops->trigger_type) { | ||
471 | unregistered = true; | ||
472 | list_del_rcu(&data->list); | ||
473 | trace_event_trigger_enable_disable(file, 0); | ||
474 | break; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | if (unregistered && data->ops->free) | ||
479 | data->ops->free(data->ops, data); | ||
480 | } | ||
481 | |||
482 | /** | ||
483 | * event_trigger_callback - Generic event_command @func implementation | ||
484 | * @cmd_ops: The command ops, used for trigger registration | ||
485 | * @file: The ftrace_event_file associated with the event | ||
486 | * @glob: The raw string used to register the trigger | ||
487 | * @cmd: The cmd portion of the string used to register the trigger | ||
488 | * @param: The params portion of the string used to register the trigger | ||
489 | * | ||
490 | * Common implementation for event command parsing and trigger | ||
491 | * instantiation. | ||
492 | * | ||
493 | * Usually used directly as the @func method in event command | ||
494 | * implementations. | ||
495 | * | ||
496 | * Return: 0 on success, errno otherwise | ||
497 | */ | ||
498 | static int | ||
499 | event_trigger_callback(struct event_command *cmd_ops, | ||
500 | struct ftrace_event_file *file, | ||
501 | char *glob, char *cmd, char *param) | ||
502 | { | ||
503 | struct event_trigger_data *trigger_data; | ||
504 | struct event_trigger_ops *trigger_ops; | ||
505 | char *trigger = NULL; | ||
506 | char *number; | ||
507 | int ret; | ||
508 | |||
509 | /* separate the trigger from the filter (t:n [if filter]) */ | ||
510 | if (param && isdigit(param[0])) | ||
511 | trigger = strsep(¶m, " \t"); | ||
512 | |||
513 | trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger); | ||
514 | |||
515 | ret = -ENOMEM; | ||
516 | trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); | ||
517 | if (!trigger_data) | ||
518 | goto out; | ||
519 | |||
520 | trigger_data->count = -1; | ||
521 | trigger_data->ops = trigger_ops; | ||
522 | trigger_data->cmd_ops = cmd_ops; | ||
523 | INIT_LIST_HEAD(&trigger_data->list); | ||
524 | |||
525 | if (glob[0] == '!') { | ||
526 | cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); | ||
527 | kfree(trigger_data); | ||
528 | ret = 0; | ||
529 | goto out; | ||
530 | } | ||
531 | |||
532 | if (trigger) { | ||
533 | number = strsep(&trigger, ":"); | ||
534 | |||
535 | ret = -EINVAL; | ||
536 | if (!strlen(number)) | ||
537 | goto out_free; | ||
538 | |||
539 | /* | ||
540 | * We use the callback data field (which is a pointer) | ||
541 | * as our counter. | ||
542 | */ | ||
543 | ret = kstrtoul(number, 0, &trigger_data->count); | ||
544 | if (ret) | ||
545 | goto out_free; | ||
546 | } | ||
547 | |||
548 | if (!param) /* if param is non-empty, it's supposed to be a filter */ | ||
549 | goto out_reg; | ||
550 | |||
551 | if (!cmd_ops->set_filter) | ||
552 | goto out_reg; | ||
553 | |||
554 | ret = cmd_ops->set_filter(param, trigger_data, file); | ||
555 | if (ret < 0) | ||
556 | goto out_free; | ||
557 | |||
558 | out_reg: | ||
559 | ret = cmd_ops->reg(glob, trigger_ops, trigger_data, file); | ||
560 | /* | ||
561 | * The above returns on success the # of functions enabled, | ||
562 | * but if it didn't find any functions it returns zero. | ||
563 | * Consider no functions a failure too. | ||
564 | */ | ||
565 | if (!ret) { | ||
566 | ret = -ENOENT; | ||
567 | goto out_free; | ||
568 | } else if (ret < 0) | ||
569 | goto out_free; | ||
570 | ret = 0; | ||
571 | out: | ||
572 | return ret; | ||
573 | |||
574 | out_free: | ||
575 | kfree(trigger_data); | ||
576 | goto out; | ||
577 | } | ||
578 | |||
579 | static void | ||
580 | traceon_trigger(struct event_trigger_data *data) | ||
581 | { | ||
582 | if (tracing_is_on()) | ||
583 | return; | ||
584 | |||
585 | tracing_on(); | ||
586 | } | ||
587 | |||
588 | static void | ||
589 | traceon_count_trigger(struct event_trigger_data *data) | ||
590 | { | ||
591 | if (!data->count) | ||
592 | return; | ||
593 | |||
594 | if (data->count != -1) | ||
595 | (data->count)--; | ||
596 | |||
597 | traceon_trigger(data); | ||
598 | } | ||
599 | |||
600 | static void | ||
601 | traceoff_trigger(struct event_trigger_data *data) | ||
602 | { | ||
603 | if (!tracing_is_on()) | ||
604 | return; | ||
605 | |||
606 | tracing_off(); | ||
607 | } | ||
608 | |||
609 | static void | ||
610 | traceoff_count_trigger(struct event_trigger_data *data) | ||
611 | { | ||
612 | if (!data->count) | ||
613 | return; | ||
614 | |||
615 | if (data->count != -1) | ||
616 | (data->count)--; | ||
617 | |||
618 | traceoff_trigger(data); | ||
619 | } | ||
620 | |||
621 | static int | ||
622 | traceon_trigger_print(struct seq_file *m, struct event_trigger_ops *ops, | ||
623 | struct event_trigger_data *data) | ||
624 | { | ||
625 | return event_trigger_print("traceon", m, (void *)data->count, | ||
626 | data->filter_str); | ||
627 | } | ||
628 | |||
629 | static int | ||
630 | traceoff_trigger_print(struct seq_file *m, struct event_trigger_ops *ops, | ||
631 | struct event_trigger_data *data) | ||
632 | { | ||
633 | return event_trigger_print("traceoff", m, (void *)data->count, | ||
634 | data->filter_str); | ||
635 | } | ||
636 | |||
637 | static struct event_trigger_ops traceon_trigger_ops = { | ||
638 | .func = traceon_trigger, | ||
639 | .print = traceon_trigger_print, | ||
640 | .init = event_trigger_init, | ||
641 | .free = event_trigger_free, | ||
642 | }; | ||
643 | |||
644 | static struct event_trigger_ops traceon_count_trigger_ops = { | ||
645 | .func = traceon_count_trigger, | ||
646 | .print = traceon_trigger_print, | ||
647 | .init = event_trigger_init, | ||
648 | .free = event_trigger_free, | ||
649 | }; | ||
650 | |||
651 | static struct event_trigger_ops traceoff_trigger_ops = { | ||
652 | .func = traceoff_trigger, | ||
653 | .print = traceoff_trigger_print, | ||
654 | .init = event_trigger_init, | ||
655 | .free = event_trigger_free, | ||
656 | }; | ||
657 | |||
658 | static struct event_trigger_ops traceoff_count_trigger_ops = { | ||
659 | .func = traceoff_count_trigger, | ||
660 | .print = traceoff_trigger_print, | ||
661 | .init = event_trigger_init, | ||
662 | .free = event_trigger_free, | ||
663 | }; | ||
664 | |||
665 | static struct event_trigger_ops * | ||
666 | onoff_get_trigger_ops(char *cmd, char *param) | ||
667 | { | ||
668 | struct event_trigger_ops *ops; | ||
669 | |||
670 | /* we register both traceon and traceoff to this callback */ | ||
671 | if (strcmp(cmd, "traceon") == 0) | ||
672 | ops = param ? &traceon_count_trigger_ops : | ||
673 | &traceon_trigger_ops; | ||
674 | else | ||
675 | ops = param ? &traceoff_count_trigger_ops : | ||
676 | &traceoff_trigger_ops; | ||
677 | |||
678 | return ops; | ||
679 | } | ||
680 | |||
681 | static struct event_command trigger_traceon_cmd = { | ||
682 | .name = "traceon", | ||
683 | .trigger_type = ETT_TRACE_ONOFF, | ||
684 | .func = event_trigger_callback, | ||
685 | .reg = register_trigger, | ||
686 | .unreg = unregister_trigger, | ||
687 | .get_trigger_ops = onoff_get_trigger_ops, | ||
688 | }; | ||
689 | |||
690 | static struct event_command trigger_traceoff_cmd = { | ||
691 | .name = "traceoff", | ||
692 | .trigger_type = ETT_TRACE_ONOFF, | ||
693 | .func = event_trigger_callback, | ||
694 | .reg = register_trigger, | ||
695 | .unreg = unregister_trigger, | ||
696 | .get_trigger_ops = onoff_get_trigger_ops, | ||
697 | }; | ||
698 | |||
699 | static __init void unregister_trigger_traceon_traceoff_cmds(void) | ||
700 | { | ||
701 | unregister_event_command(&trigger_traceon_cmd); | ||
702 | unregister_event_command(&trigger_traceoff_cmd); | ||
703 | } | ||
704 | |||
705 | static __init int register_trigger_traceon_traceoff_cmds(void) | ||
706 | { | ||
707 | int ret; | ||
708 | |||
709 | ret = register_event_command(&trigger_traceon_cmd); | ||
710 | if (WARN_ON(ret < 0)) | ||
711 | return ret; | ||
712 | ret = register_event_command(&trigger_traceoff_cmd); | ||
713 | if (WARN_ON(ret < 0)) | ||
714 | unregister_trigger_traceon_traceoff_cmds(); | ||
715 | |||
716 | return ret; | ||
717 | } | ||
718 | |||
275 | __init int register_trigger_cmds(void) | 719 | __init int register_trigger_cmds(void) |
276 | { | 720 | { |
721 | register_trigger_traceon_traceoff_cmds(); | ||
722 | |||
277 | return 0; | 723 | return 0; |
278 | } | 724 | } |