perf kwork: Add sched record support
authorYang Jihong <yangjihong1@huawei.com>
Sat, 12 Aug 2023 08:49:08 +0000 (08:49 +0000)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 12 Sep 2023 20:31:59 +0000 (17:31 -0300)
The kwork_class type of sched is added to support recording and parsing of
sched_switch events.

As follows:

  # perf kwork -h

   Usage: perf kwork [<options>] {record|report|latency|timehist}

      -D, --dump-raw-trace  dump raw trace in ASCII
      -f, --force           don't complain, do it
      -k, --kwork <kwork>   list of kwork to profile (irq, softirq, workqueue, sched, etc)
      -v, --verbose         be more verbose (show symbol address, etc)

  # perf kwork -k sched record true
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.083 MB perf.data (47 samples) ]
  # perf evlist
  sched:sched_switch
  dummy:HG
  # Tip: use 'perf evlist --trace-fields' to show fields for tracepoint events

Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Yang Jihong <yangjihong1@huawei.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Sandipan Das <sandipan.das@amd.com>
Link: https://lore.kernel.org/r/20230812084917.169338-8-yangjihong1@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-kwork.txt
tools/perf/builtin-kwork.c
tools/perf/util/kwork.h

index 482d6c52e2edf1f29aeabcdd664aaf58655c21e4..2092ab916ea9fa581e99951da2f0c23377438443 100644 (file)
@@ -66,7 +66,7 @@ OPTIONS
 
 -k::
 --kwork::
-       List of kwork to profile (irq, softirq, workqueue, etc)
+       List of kwork to profile (irq, softirq, workqueue, sched, etc)
 
 -v::
 --verbose::
index e859c34b23f3003c5621fbdfe87faa1bdef0c0ca..4e2b9103b9e022e4ab4bb47db1cb832e84930c26 100644 (file)
@@ -1084,10 +1084,77 @@ static struct kwork_class kwork_workqueue = {
        .work_name      = workqueue_work_name,
 };
 
+static struct kwork_class kwork_sched;
+static int process_sched_switch_event(struct perf_tool *tool,
+                                     struct evsel *evsel,
+                                     struct perf_sample *sample,
+                                     struct machine *machine)
+{
+       struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
+
+       if (kwork->tp_handler->sched_switch_event)
+               return kwork->tp_handler->sched_switch_event(kwork, &kwork_sched,
+                                                            evsel, sample, machine);
+       return 0;
+}
+
+const struct evsel_str_handler sched_tp_handlers[] = {
+       { "sched:sched_switch",  process_sched_switch_event, },
+};
+
+static int sched_class_init(struct kwork_class *class,
+                           struct perf_session *session)
+{
+       if (perf_session__set_tracepoints_handlers(session,
+                                                  sched_tp_handlers)) {
+               pr_err("Failed to set sched tracepoints handlers\n");
+               return -1;
+       }
+
+       class->work_root = RB_ROOT_CACHED;
+       return 0;
+}
+
+static void sched_work_init(struct perf_kwork *kwork __maybe_unused,
+                           struct kwork_class *class,
+                           struct kwork_work *work,
+                           enum kwork_trace_type src_type,
+                           struct evsel *evsel,
+                           struct perf_sample *sample,
+                           struct machine *machine __maybe_unused)
+{
+       work->class = class;
+       work->cpu = sample->cpu;
+
+       if (src_type == KWORK_TRACE_EXIT) {
+               work->id = evsel__intval(evsel, sample, "prev_pid");
+               work->name = strdup(evsel__strval(evsel, sample, "prev_comm"));
+       } else if (src_type == KWORK_TRACE_ENTRY) {
+               work->id = evsel__intval(evsel, sample, "next_pid");
+               work->name = strdup(evsel__strval(evsel, sample, "next_comm"));
+       }
+}
+
+static void sched_work_name(struct kwork_work *work, char *buf, int len)
+{
+       snprintf(buf, len, "%s", work->name);
+}
+
+static struct kwork_class kwork_sched = {
+       .name           = "sched",
+       .type           = KWORK_CLASS_SCHED,
+       .nr_tracepoints = ARRAY_SIZE(sched_tp_handlers),
+       .tp_handlers    = sched_tp_handlers,
+       .class_init     = sched_class_init,
+       .work_init      = sched_work_init,
+       .work_name      = sched_work_name,
+};
+
 static struct kwork_class *kwork_class_supported_list[KWORK_CLASS_MAX] = {
        [KWORK_CLASS_IRQ]       = &kwork_irq,
        [KWORK_CLASS_SOFTIRQ]   = &kwork_softirq,
        [KWORK_CLASS_WORKQUEUE] = &kwork_workqueue,
+       [KWORK_CLASS_SCHED]     = &kwork_sched,
 };
 
 static void print_separator(int len)
@@ -1740,7 +1807,7 @@ int cmd_kwork(int argc, const char **argv)
        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 (irq, softirq, workqueue, etc)"),
+                  "list of kwork to profile (irq, softirq, workqueue, sched, etc)"),
        OPT_BOOLEAN('f', "force", &kwork.force, "don't complain, do it"),
        OPT_END()
        };
index 736c7a08fb198f0f5874ed283d96cb08bd7abafb..f8e9cdd1371abd3e17b84b5762f745f239683498 100644 (file)
@@ -16,6 +16,7 @@ enum kwork_class_type {
        KWORK_CLASS_IRQ,
        KWORK_CLASS_SOFTIRQ,
        KWORK_CLASS_WORKQUEUE,
+       KWORK_CLASS_SCHED,
        KWORK_CLASS_MAX,
 };
 
@@ -167,6 +168,10 @@ struct trace_kwork_handler {
        int (*exit_event)(struct perf_kwork *kwork,
                          struct kwork_class *class, struct evsel *evsel,
                          struct perf_sample *sample, struct machine *machine);
+
+       int (*sched_switch_event)(struct perf_kwork *kwork,
+                                 struct kwork_class *class, struct evsel *evsel,
+                                 struct perf_sample *sample, struct machine *machine);
 };
 
 struct perf_kwork {