diff options
author | Yang Jihong <yangjihong1@huawei.com> | 2022-07-09 03:50:17 +0200 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2022-07-26 21:01:24 +0200 |
commit | 0f70d8e9db4f250f694a3befe88501027b1dc88e (patch) | |
tree | 39f62962ff51cf96000b283490ed1316c096e921 /tools/perf/builtin-kwork.c | |
parent | perf data: Add missing unistd.h header needed for pid_t (diff) | |
download | linux-0f70d8e9db4f250f694a3befe88501027b1dc88e.tar.xz linux-0f70d8e9db4f250f694a3befe88501027b1dc88e.zip |
perf kwork: New tool to trace time properties of kernel work (such as softirq, and workqueue)
The 'perf kwork' tool is used to trace time properties of kernel work
(such as irq, softirq, and workqueue), including runtime, latency, and
timehist, using the infrastructure in the perf tools to allow tracing
extra targets.
This is the first commit to reuse the 'perf record' framework code to
implement a simple record function, kwork is not supported currently.
Test cases:
# perf
usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS]
The most commonly used perf commands are:
<SNIP>
iostat Show I/O performance metrics
kallsyms Searches running kernel for symbols
kmem Tool to trace/measure kernel memory properties
kvm Tool to trace/measure kvm guest os
kwork Tool to trace/measure kernel work properties (latencies)
list List all symbolic event types
lock Analyze lock events
mem Profile memory accesses
record Run a command and record its profile into perf.data
<SNIP>
See 'perf help COMMAND' for more information on a specific command.
# perf kwork
Usage: perf kwork [<options>] {record}
-D, --dump-raw-trace dump raw trace in ASCII
-f, --force don't complain, do it
-k, --kwork <kwork> list of kwork to profile
-v, --verbose be more verbose (show symbol address, etc)
# perf kwork record -- sleep 1
[ perf record: Woken up 0 times to write data ]
[ perf record: Captured and wrote 1.787 MB perf.data ]
Signed-off-by: Yang Jihong <yangjihong1@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Clarke <pc@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220709015033.38326-2-yangjihong1@huawei.com
[ Add {} for multiline if blocks ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-kwork.c')
-rw-r--r-- | tools/perf/builtin-kwork.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c new file mode 100644 index 000000000000..3e8d5fa47674 --- /dev/null +++ b/tools/perf/builtin-kwork.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * builtin-kwork.c + * + * Copyright (c) 2022 Huawei Inc, Yang Jihong <yangjihong1@huawei.com> + */ + +#include "builtin.h" + +#include "util/data.h" +#include "util/kwork.h" +#include "util/debug.h" +#include "util/symbol.h" +#include "util/thread.h" +#include "util/string2.h" +#include "util/callchain.h" +#include "util/evsel_fprintf.h" + +#include <subcmd/pager.h> +#include <subcmd/parse-options.h> + +#include <errno.h> +#include <inttypes.h> +#include <linux/err.h> +#include <linux/time64.h> +#include <linux/zalloc.h> + +static struct kwork_class *kwork_class_supported_list[KWORK_CLASS_MAX] = { +}; + +static void setup_event_list(struct perf_kwork *kwork, + const struct option *options, + const char * const usage_msg[]) +{ + int i; + struct kwork_class *class; + char *tmp, *tok, *str; + + if (kwork->event_list_str == NULL) + goto null_event_list_str; + + str = strdup(kwork->event_list_str); + for (tok = strtok_r(str, ", ", &tmp); + tok; tok = strtok_r(NULL, ", ", &tmp)) { + for (i = 0; i < KWORK_CLASS_MAX; i++) { + class = kwork_class_supported_list[i]; + if (strcmp(tok, class->name) == 0) { + list_add_tail(&class->list, &kwork->class_list); + break; + } + } + if (i == KWORK_CLASS_MAX) { + usage_with_options_msg(usage_msg, options, + "Unknown --event key: `%s'", tok); + } + } + free(str); + +null_event_list_str: + /* + * config all kwork events if not specified + */ + if (list_empty(&kwork->class_list)) { + for (i = 0; i < KWORK_CLASS_MAX; i++) { + list_add_tail(&kwork_class_supported_list[i]->list, + &kwork->class_list); + } + } + + pr_debug("Config event list:"); + list_for_each_entry(class, &kwork->class_list, list) + pr_debug(" %s", class->name); + pr_debug("\n"); +} + +static int perf_kwork__record(struct perf_kwork *kwork, + int argc, const char **argv) +{ + const char **rec_argv; + unsigned int rec_argc, i, j; + struct kwork_class *class; + + const char *const record_args[] = { + "record", + "-a", + "-R", + "-m", "1024", + "-c", "1", + }; + + rec_argc = ARRAY_SIZE(record_args) + argc - 1; + + list_for_each_entry(class, &kwork->class_list, list) + rec_argc += 2 * class->nr_tracepoints; + + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + if (rec_argv == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(record_args); i++) + rec_argv[i] = strdup(record_args[i]); + + list_for_each_entry(class, &kwork->class_list, list) { + for (j = 0; j < class->nr_tracepoints; j++) { + rec_argv[i++] = strdup("-e"); + rec_argv[i++] = strdup(class->tp_handlers[j].name); + } + } + + for (j = 1; j < (unsigned int)argc; j++, i++) + rec_argv[i] = argv[j]; + + BUG_ON(i != rec_argc); + + pr_debug("record comm: "); + for (j = 0; j < rec_argc; j++) + pr_debug("%s ", rec_argv[j]); + pr_debug("\n"); + + return cmd_record(i, rec_argv); +} + +int cmd_kwork(int argc, const char **argv) +{ + static struct perf_kwork kwork = { + .class_list = LIST_HEAD_INIT(kwork.class_list), + .force = false, + .event_list_str = NULL, + }; + const struct option kwork_options[] = { + OPT_INCR('v', "verbose", &verbose, + "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, + "dump raw trace in ASCII"), + OPT_STRING('k', "kwork", &kwork.event_list_str, "kwork", + "list of kwork to profile"), + OPT_BOOLEAN('f', "force", &kwork.force, "don't complain, do it"), + OPT_END() + }; + const char *kwork_usage[] = { + NULL, + NULL + }; + const char *const kwork_subcommands[] = { + "record", NULL + }; + + argc = parse_options_subcommand(argc, argv, kwork_options, + kwork_subcommands, kwork_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (!argc) + usage_with_options(kwork_usage, kwork_options); + + setup_event_list(&kwork, kwork_options, kwork_usage); + + if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) + return perf_kwork__record(&kwork, argc, argv); + else + usage_with_options(kwork_usage, kwork_options); + + return 0; +} |