Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * builtin-kwork.c
0004  *
0005  * Copyright (c) 2022  Huawei Inc,  Yang Jihong <yangjihong1@huawei.com>
0006  */
0007 
0008 #include "builtin.h"
0009 
0010 #include "util/data.h"
0011 #include "util/kwork.h"
0012 #include "util/debug.h"
0013 #include "util/symbol.h"
0014 #include "util/thread.h"
0015 #include "util/string2.h"
0016 #include "util/callchain.h"
0017 #include "util/evsel_fprintf.h"
0018 
0019 #include <subcmd/pager.h>
0020 #include <subcmd/parse-options.h>
0021 
0022 #include <errno.h>
0023 #include <inttypes.h>
0024 #include <linux/err.h>
0025 #include <linux/time64.h>
0026 #include <linux/zalloc.h>
0027 
0028 /*
0029  * report header elements width
0030  */
0031 #define PRINT_CPU_WIDTH 4
0032 #define PRINT_COUNT_WIDTH 9
0033 #define PRINT_RUNTIME_WIDTH 10
0034 #define PRINT_LATENCY_WIDTH 10
0035 #define PRINT_TIMESTAMP_WIDTH 17
0036 #define PRINT_KWORK_NAME_WIDTH 30
0037 #define RPINT_DECIMAL_WIDTH 3
0038 #define PRINT_BRACKETPAIR_WIDTH 2
0039 #define PRINT_TIME_UNIT_SEC_WIDTH 2
0040 #define PRINT_TIME_UNIT_MESC_WIDTH 3
0041 #define PRINT_RUNTIME_HEADER_WIDTH (PRINT_RUNTIME_WIDTH + PRINT_TIME_UNIT_MESC_WIDTH)
0042 #define PRINT_LATENCY_HEADER_WIDTH (PRINT_LATENCY_WIDTH + PRINT_TIME_UNIT_MESC_WIDTH)
0043 #define PRINT_TIMEHIST_CPU_WIDTH (PRINT_CPU_WIDTH + PRINT_BRACKETPAIR_WIDTH)
0044 #define PRINT_TIMESTAMP_HEADER_WIDTH (PRINT_TIMESTAMP_WIDTH + PRINT_TIME_UNIT_SEC_WIDTH)
0045 
0046 struct sort_dimension {
0047     const char      *name;
0048     int             (*cmp)(struct kwork_work *l, struct kwork_work *r);
0049     struct          list_head list;
0050 };
0051 
0052 static int id_cmp(struct kwork_work *l, struct kwork_work *r)
0053 {
0054     if (l->cpu > r->cpu)
0055         return 1;
0056     if (l->cpu < r->cpu)
0057         return -1;
0058 
0059     if (l->id > r->id)
0060         return 1;
0061     if (l->id < r->id)
0062         return -1;
0063 
0064     return 0;
0065 }
0066 
0067 static int count_cmp(struct kwork_work *l, struct kwork_work *r)
0068 {
0069     if (l->nr_atoms > r->nr_atoms)
0070         return 1;
0071     if (l->nr_atoms < r->nr_atoms)
0072         return -1;
0073 
0074     return 0;
0075 }
0076 
0077 static int runtime_cmp(struct kwork_work *l, struct kwork_work *r)
0078 {
0079     if (l->total_runtime > r->total_runtime)
0080         return 1;
0081     if (l->total_runtime < r->total_runtime)
0082         return -1;
0083 
0084     return 0;
0085 }
0086 
0087 static int max_runtime_cmp(struct kwork_work *l, struct kwork_work *r)
0088 {
0089     if (l->max_runtime > r->max_runtime)
0090         return 1;
0091     if (l->max_runtime < r->max_runtime)
0092         return -1;
0093 
0094     return 0;
0095 }
0096 
0097 static int avg_latency_cmp(struct kwork_work *l, struct kwork_work *r)
0098 {
0099     u64 avgl, avgr;
0100 
0101     if (!r->nr_atoms)
0102         return 1;
0103     if (!l->nr_atoms)
0104         return -1;
0105 
0106     avgl = l->total_latency / l->nr_atoms;
0107     avgr = r->total_latency / r->nr_atoms;
0108 
0109     if (avgl > avgr)
0110         return 1;
0111     if (avgl < avgr)
0112         return -1;
0113 
0114     return 0;
0115 }
0116 
0117 static int max_latency_cmp(struct kwork_work *l, struct kwork_work *r)
0118 {
0119     if (l->max_latency > r->max_latency)
0120         return 1;
0121     if (l->max_latency < r->max_latency)
0122         return -1;
0123 
0124     return 0;
0125 }
0126 
0127 static int sort_dimension__add(struct perf_kwork *kwork __maybe_unused,
0128                    const char *tok, struct list_head *list)
0129 {
0130     size_t i;
0131     static struct sort_dimension max_sort_dimension = {
0132         .name = "max",
0133         .cmp  = max_runtime_cmp,
0134     };
0135     static struct sort_dimension id_sort_dimension = {
0136         .name = "id",
0137         .cmp  = id_cmp,
0138     };
0139     static struct sort_dimension runtime_sort_dimension = {
0140         .name = "runtime",
0141         .cmp  = runtime_cmp,
0142     };
0143     static struct sort_dimension count_sort_dimension = {
0144         .name = "count",
0145         .cmp  = count_cmp,
0146     };
0147     static struct sort_dimension avg_sort_dimension = {
0148         .name = "avg",
0149         .cmp  = avg_latency_cmp,
0150     };
0151     struct sort_dimension *available_sorts[] = {
0152         &id_sort_dimension,
0153         &max_sort_dimension,
0154         &count_sort_dimension,
0155         &runtime_sort_dimension,
0156         &avg_sort_dimension,
0157     };
0158 
0159     if (kwork->report == KWORK_REPORT_LATENCY)
0160         max_sort_dimension.cmp = max_latency_cmp;
0161 
0162     for (i = 0; i < ARRAY_SIZE(available_sorts); i++) {
0163         if (!strcmp(available_sorts[i]->name, tok)) {
0164             list_add_tail(&available_sorts[i]->list, list);
0165             return 0;
0166         }
0167     }
0168 
0169     return -1;
0170 }
0171 
0172 static void setup_sorting(struct perf_kwork *kwork,
0173               const struct option *options,
0174               const char * const usage_msg[])
0175 {
0176     char *tmp, *tok, *str = strdup(kwork->sort_order);
0177 
0178     for (tok = strtok_r(str, ", ", &tmp);
0179          tok; tok = strtok_r(NULL, ", ", &tmp)) {
0180         if (sort_dimension__add(kwork, tok, &kwork->sort_list) < 0)
0181             usage_with_options_msg(usage_msg, options,
0182                            "Unknown --sort key: `%s'", tok);
0183     }
0184 
0185     pr_debug("Sort order: %s\n", kwork->sort_order);
0186     free(str);
0187 }
0188 
0189 static struct kwork_atom *atom_new(struct perf_kwork *kwork,
0190                    struct perf_sample *sample)
0191 {
0192     unsigned long i;
0193     struct kwork_atom_page *page;
0194     struct kwork_atom *atom = NULL;
0195 
0196     list_for_each_entry(page, &kwork->atom_page_list, list) {
0197         if (!bitmap_full(page->bitmap, NR_ATOM_PER_PAGE)) {
0198             i = find_first_zero_bit(page->bitmap, NR_ATOM_PER_PAGE);
0199             BUG_ON(i >= NR_ATOM_PER_PAGE);
0200             atom = &page->atoms[i];
0201             goto found_atom;
0202         }
0203     }
0204 
0205     /*
0206      * new page
0207      */
0208     page = zalloc(sizeof(*page));
0209     if (page == NULL) {
0210         pr_err("Failed to zalloc kwork atom page\n");
0211         return NULL;
0212     }
0213 
0214     i = 0;
0215     atom = &page->atoms[0];
0216     list_add_tail(&page->list, &kwork->atom_page_list);
0217 
0218 found_atom:
0219     set_bit(i, page->bitmap);
0220     atom->time = sample->time;
0221     atom->prev = NULL;
0222     atom->page_addr = page;
0223     atom->bit_inpage = i;
0224     return atom;
0225 }
0226 
0227 static void atom_free(struct kwork_atom *atom)
0228 {
0229     if (atom->prev != NULL)
0230         atom_free(atom->prev);
0231 
0232     clear_bit(atom->bit_inpage,
0233           ((struct kwork_atom_page *)atom->page_addr)->bitmap);
0234 }
0235 
0236 static void atom_del(struct kwork_atom *atom)
0237 {
0238     list_del(&atom->list);
0239     atom_free(atom);
0240 }
0241 
0242 static int work_cmp(struct list_head *list,
0243             struct kwork_work *l, struct kwork_work *r)
0244 {
0245     int ret = 0;
0246     struct sort_dimension *sort;
0247 
0248     BUG_ON(list_empty(list));
0249 
0250     list_for_each_entry(sort, list, list) {
0251         ret = sort->cmp(l, r);
0252         if (ret)
0253             return ret;
0254     }
0255 
0256     return ret;
0257 }
0258 
0259 static struct kwork_work *work_search(struct rb_root_cached *root,
0260                       struct kwork_work *key,
0261                       struct list_head *sort_list)
0262 {
0263     int cmp;
0264     struct kwork_work *work;
0265     struct rb_node *node = root->rb_root.rb_node;
0266 
0267     while (node) {
0268         work = container_of(node, struct kwork_work, node);
0269         cmp = work_cmp(sort_list, key, work);
0270         if (cmp > 0)
0271             node = node->rb_left;
0272         else if (cmp < 0)
0273             node = node->rb_right;
0274         else {
0275             if (work->name == NULL)
0276                 work->name = key->name;
0277             return work;
0278         }
0279     }
0280     return NULL;
0281 }
0282 
0283 static void work_insert(struct rb_root_cached *root,
0284             struct kwork_work *key, struct list_head *sort_list)
0285 {
0286     int cmp;
0287     bool leftmost = true;
0288     struct kwork_work *cur;
0289     struct rb_node **new = &(root->rb_root.rb_node), *parent = NULL;
0290 
0291     while (*new) {
0292         cur = container_of(*new, struct kwork_work, node);
0293         parent = *new;
0294         cmp = work_cmp(sort_list, key, cur);
0295 
0296         if (cmp > 0)
0297             new = &((*new)->rb_left);
0298         else {
0299             new = &((*new)->rb_right);
0300             leftmost = false;
0301         }
0302     }
0303 
0304     rb_link_node(&key->node, parent, new);
0305     rb_insert_color_cached(&key->node, root, leftmost);
0306 }
0307 
0308 static struct kwork_work *work_new(struct kwork_work *key)
0309 {
0310     int i;
0311     struct kwork_work *work = zalloc(sizeof(*work));
0312 
0313     if (work == NULL) {
0314         pr_err("Failed to zalloc kwork work\n");
0315         return NULL;
0316     }
0317 
0318     for (i = 0; i < KWORK_TRACE_MAX; i++)
0319         INIT_LIST_HEAD(&work->atom_list[i]);
0320 
0321     work->id = key->id;
0322     work->cpu = key->cpu;
0323     work->name = key->name;
0324     work->class = key->class;
0325     return work;
0326 }
0327 
0328 static struct kwork_work *work_findnew(struct rb_root_cached *root,
0329                        struct kwork_work *key,
0330                        struct list_head *sort_list)
0331 {
0332     struct kwork_work *work = work_search(root, key, sort_list);
0333 
0334     if (work != NULL)
0335         return work;
0336 
0337     work = work_new(key);
0338     if (work)
0339         work_insert(root, work, sort_list);
0340 
0341     return work;
0342 }
0343 
0344 static void profile_update_timespan(struct perf_kwork *kwork,
0345                     struct perf_sample *sample)
0346 {
0347     if (!kwork->summary)
0348         return;
0349 
0350     if ((kwork->timestart == 0) || (kwork->timestart > sample->time))
0351         kwork->timestart = sample->time;
0352 
0353     if (kwork->timeend < sample->time)
0354         kwork->timeend = sample->time;
0355 }
0356 
0357 static bool profile_event_match(struct perf_kwork *kwork,
0358                 struct kwork_work *work,
0359                 struct perf_sample *sample)
0360 {
0361     int cpu = work->cpu;
0362     u64 time = sample->time;
0363     struct perf_time_interval *ptime = &kwork->ptime;
0364 
0365     if ((kwork->cpu_list != NULL) && !test_bit(cpu, kwork->cpu_bitmap))
0366         return false;
0367 
0368     if (((ptime->start != 0) && (ptime->start > time)) ||
0369         ((ptime->end != 0) && (ptime->end < time)))
0370         return false;
0371 
0372     if ((kwork->profile_name != NULL) &&
0373         (work->name != NULL) &&
0374         (strcmp(work->name, kwork->profile_name) != 0))
0375         return false;
0376 
0377     profile_update_timespan(kwork, sample);
0378     return true;
0379 }
0380 
0381 static int work_push_atom(struct perf_kwork *kwork,
0382               struct kwork_class *class,
0383               enum kwork_trace_type src_type,
0384               enum kwork_trace_type dst_type,
0385               struct evsel *evsel,
0386               struct perf_sample *sample,
0387               struct machine *machine,
0388               struct kwork_work **ret_work)
0389 {
0390     struct kwork_atom *atom, *dst_atom;
0391     struct kwork_work *work, key;
0392 
0393     BUG_ON(class->work_init == NULL);
0394     class->work_init(class, &key, evsel, sample, machine);
0395 
0396     atom = atom_new(kwork, sample);
0397     if (atom == NULL)
0398         return -1;
0399 
0400     work = work_findnew(&class->work_root, &key, &kwork->cmp_id);
0401     if (work == NULL) {
0402         free(atom);
0403         return -1;
0404     }
0405 
0406     if (!profile_event_match(kwork, work, sample))
0407         return 0;
0408 
0409     if (dst_type < KWORK_TRACE_MAX) {
0410         dst_atom = list_last_entry_or_null(&work->atom_list[dst_type],
0411                            struct kwork_atom, list);
0412         if (dst_atom != NULL) {
0413             atom->prev = dst_atom;
0414             list_del(&dst_atom->list);
0415         }
0416     }
0417 
0418     if (ret_work != NULL)
0419         *ret_work = work;
0420 
0421     list_add_tail(&atom->list, &work->atom_list[src_type]);
0422 
0423     return 0;
0424 }
0425 
0426 static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork,
0427                     struct kwork_class *class,
0428                     enum kwork_trace_type src_type,
0429                     enum kwork_trace_type dst_type,
0430                     struct evsel *evsel,
0431                     struct perf_sample *sample,
0432                     struct machine *machine,
0433                     struct kwork_work **ret_work)
0434 {
0435     struct kwork_atom *atom, *src_atom;
0436     struct kwork_work *work, key;
0437 
0438     BUG_ON(class->work_init == NULL);
0439     class->work_init(class, &key, evsel, sample, machine);
0440 
0441     work = work_findnew(&class->work_root, &key, &kwork->cmp_id);
0442     if (ret_work != NULL)
0443         *ret_work = work;
0444 
0445     if (work == NULL)
0446         return NULL;
0447 
0448     if (!profile_event_match(kwork, work, sample))
0449         return NULL;
0450 
0451     atom = list_last_entry_or_null(&work->atom_list[dst_type],
0452                        struct kwork_atom, list);
0453     if (atom != NULL)
0454         return atom;
0455 
0456     src_atom = atom_new(kwork, sample);
0457     if (src_atom != NULL)
0458         list_add_tail(&src_atom->list, &work->atom_list[src_type]);
0459     else {
0460         if (ret_work != NULL)
0461             *ret_work = NULL;
0462     }
0463 
0464     return NULL;
0465 }
0466 
0467 static void report_update_exit_event(struct kwork_work *work,
0468                      struct kwork_atom *atom,
0469                      struct perf_sample *sample)
0470 {
0471     u64 delta;
0472     u64 exit_time = sample->time;
0473     u64 entry_time = atom->time;
0474 
0475     if ((entry_time != 0) && (exit_time >= entry_time)) {
0476         delta = exit_time - entry_time;
0477         if ((delta > work->max_runtime) ||
0478             (work->max_runtime == 0)) {
0479             work->max_runtime = delta;
0480             work->max_runtime_start = entry_time;
0481             work->max_runtime_end = exit_time;
0482         }
0483         work->total_runtime += delta;
0484         work->nr_atoms++;
0485     }
0486 }
0487 
0488 static int report_entry_event(struct perf_kwork *kwork,
0489                   struct kwork_class *class,
0490                   struct evsel *evsel,
0491                   struct perf_sample *sample,
0492                   struct machine *machine)
0493 {
0494     return work_push_atom(kwork, class, KWORK_TRACE_ENTRY,
0495                   KWORK_TRACE_MAX, evsel, sample,
0496                   machine, NULL);
0497 }
0498 
0499 static int report_exit_event(struct perf_kwork *kwork,
0500                  struct kwork_class *class,
0501                  struct evsel *evsel,
0502                  struct perf_sample *sample,
0503                  struct machine *machine)
0504 {
0505     struct kwork_atom *atom = NULL;
0506     struct kwork_work *work = NULL;
0507 
0508     atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT,
0509                  KWORK_TRACE_ENTRY, evsel, sample,
0510                  machine, &work);
0511     if (work == NULL)
0512         return -1;
0513 
0514     if (atom != NULL) {
0515         report_update_exit_event(work, atom, sample);
0516         atom_del(atom);
0517     }
0518 
0519     return 0;
0520 }
0521 
0522 static void latency_update_entry_event(struct kwork_work *work,
0523                        struct kwork_atom *atom,
0524                        struct perf_sample *sample)
0525 {
0526     u64 delta;
0527     u64 entry_time = sample->time;
0528     u64 raise_time = atom->time;
0529 
0530     if ((raise_time != 0) && (entry_time >= raise_time)) {
0531         delta = entry_time - raise_time;
0532         if ((delta > work->max_latency) ||
0533             (work->max_latency == 0)) {
0534             work->max_latency = delta;
0535             work->max_latency_start = raise_time;
0536             work->max_latency_end = entry_time;
0537         }
0538         work->total_latency += delta;
0539         work->nr_atoms++;
0540     }
0541 }
0542 
0543 static int latency_raise_event(struct perf_kwork *kwork,
0544                    struct kwork_class *class,
0545                    struct evsel *evsel,
0546                    struct perf_sample *sample,
0547                    struct machine *machine)
0548 {
0549     return work_push_atom(kwork, class, KWORK_TRACE_RAISE,
0550                   KWORK_TRACE_MAX, evsel, sample,
0551                   machine, NULL);
0552 }
0553 
0554 static int latency_entry_event(struct perf_kwork *kwork,
0555                    struct kwork_class *class,
0556                    struct evsel *evsel,
0557                    struct perf_sample *sample,
0558                    struct machine *machine)
0559 {
0560     struct kwork_atom *atom = NULL;
0561     struct kwork_work *work = NULL;
0562 
0563     atom = work_pop_atom(kwork, class, KWORK_TRACE_ENTRY,
0564                  KWORK_TRACE_RAISE, evsel, sample,
0565                  machine, &work);
0566     if (work == NULL)
0567         return -1;
0568 
0569     if (atom != NULL) {
0570         latency_update_entry_event(work, atom, sample);
0571         atom_del(atom);
0572     }
0573 
0574     return 0;
0575 }
0576 
0577 static void timehist_save_callchain(struct perf_kwork *kwork,
0578                     struct perf_sample *sample,
0579                     struct evsel *evsel,
0580                     struct machine *machine)
0581 {
0582     struct symbol *sym;
0583     struct thread *thread;
0584     struct callchain_cursor_node *node;
0585     struct callchain_cursor *cursor = &callchain_cursor;
0586 
0587     if (!kwork->show_callchain || sample->callchain == NULL)
0588         return;
0589 
0590     /* want main thread for process - has maps */
0591     thread = machine__findnew_thread(machine, sample->pid, sample->pid);
0592     if (thread == NULL) {
0593         pr_debug("Failed to get thread for pid %d\n", sample->pid);
0594         return;
0595     }
0596 
0597     if (thread__resolve_callchain(thread, cursor, evsel, sample,
0598                       NULL, NULL, kwork->max_stack + 2) != 0) {
0599         pr_debug("Failed to resolve callchain, skipping\n");
0600         goto out_put;
0601     }
0602 
0603     callchain_cursor_commit(cursor);
0604 
0605     while (true) {
0606         node = callchain_cursor_current(cursor);
0607         if (node == NULL)
0608             break;
0609 
0610         sym = node->ms.sym;
0611         if (sym) {
0612             if (!strcmp(sym->name, "__softirqentry_text_start") ||
0613                 !strcmp(sym->name, "__do_softirq"))
0614                 sym->ignore = 1;
0615         }
0616 
0617         callchain_cursor_advance(cursor);
0618     }
0619 
0620 out_put:
0621     thread__put(thread);
0622 }
0623 
0624 static void timehist_print_event(struct perf_kwork *kwork,
0625                  struct kwork_work *work,
0626                  struct kwork_atom *atom,
0627                  struct perf_sample *sample,
0628                  struct addr_location *al)
0629 {
0630     char entrytime[32], exittime[32];
0631     char kwork_name[PRINT_KWORK_NAME_WIDTH];
0632 
0633     /*
0634      * runtime start
0635      */
0636     timestamp__scnprintf_usec(atom->time,
0637                   entrytime, sizeof(entrytime));
0638     printf(" %*s ", PRINT_TIMESTAMP_WIDTH, entrytime);
0639 
0640     /*
0641      * runtime end
0642      */
0643     timestamp__scnprintf_usec(sample->time,
0644                   exittime, sizeof(exittime));
0645     printf(" %*s ", PRINT_TIMESTAMP_WIDTH, exittime);
0646 
0647     /*
0648      * cpu
0649      */
0650     printf(" [%0*d] ", PRINT_CPU_WIDTH, work->cpu);
0651 
0652     /*
0653      * kwork name
0654      */
0655     if (work->class && work->class->work_name) {
0656         work->class->work_name(work, kwork_name,
0657                        PRINT_KWORK_NAME_WIDTH);
0658         printf(" %-*s ", PRINT_KWORK_NAME_WIDTH, kwork_name);
0659     } else
0660         printf(" %-*s ", PRINT_KWORK_NAME_WIDTH, "");
0661 
0662     /*
0663      *runtime
0664      */
0665     printf(" %*.*f ",
0666            PRINT_RUNTIME_WIDTH, RPINT_DECIMAL_WIDTH,
0667            (double)(sample->time - atom->time) / NSEC_PER_MSEC);
0668 
0669     /*
0670      * delaytime
0671      */
0672     if (atom->prev != NULL)
0673         printf(" %*.*f ", PRINT_LATENCY_WIDTH, RPINT_DECIMAL_WIDTH,
0674                (double)(atom->time - atom->prev->time) / NSEC_PER_MSEC);
0675     else
0676         printf(" %*s ", PRINT_LATENCY_WIDTH, " ");
0677 
0678     /*
0679      * callchain
0680      */
0681     if (kwork->show_callchain) {
0682         printf(" ");
0683         sample__fprintf_sym(sample, al, 0,
0684                     EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
0685                     EVSEL__PRINT_CALLCHAIN_ARROW |
0686                     EVSEL__PRINT_SKIP_IGNORED,
0687                     &callchain_cursor, symbol_conf.bt_stop_list,
0688                     stdout);
0689     }
0690 
0691     printf("\n");
0692 }
0693 
0694 static int timehist_raise_event(struct perf_kwork *kwork,
0695                 struct kwork_class *class,
0696                 struct evsel *evsel,
0697                 struct perf_sample *sample,
0698                 struct machine *machine)
0699 {
0700     return work_push_atom(kwork, class, KWORK_TRACE_RAISE,
0701                   KWORK_TRACE_MAX, evsel, sample,
0702                   machine, NULL);
0703 }
0704 
0705 static int timehist_entry_event(struct perf_kwork *kwork,
0706                 struct kwork_class *class,
0707                 struct evsel *evsel,
0708                 struct perf_sample *sample,
0709                 struct machine *machine)
0710 {
0711     int ret;
0712     struct kwork_work *work = NULL;
0713 
0714     ret = work_push_atom(kwork, class, KWORK_TRACE_ENTRY,
0715                  KWORK_TRACE_RAISE, evsel, sample,
0716                  machine, &work);
0717     if (ret)
0718         return ret;
0719 
0720     if (work != NULL)
0721         timehist_save_callchain(kwork, sample, evsel, machine);
0722 
0723     return 0;
0724 }
0725 
0726 static int timehist_exit_event(struct perf_kwork *kwork,
0727                    struct kwork_class *class,
0728                    struct evsel *evsel,
0729                    struct perf_sample *sample,
0730                    struct machine *machine)
0731 {
0732     struct kwork_atom *atom = NULL;
0733     struct kwork_work *work = NULL;
0734     struct addr_location al;
0735 
0736     if (machine__resolve(machine, &al, sample) < 0) {
0737         pr_debug("Problem processing event, skipping it\n");
0738         return -1;
0739     }
0740 
0741     atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT,
0742                  KWORK_TRACE_ENTRY, evsel, sample,
0743                  machine, &work);
0744     if (work == NULL)
0745         return -1;
0746 
0747     if (atom != NULL) {
0748         work->nr_atoms++;
0749         timehist_print_event(kwork, work, atom, sample, &al);
0750         atom_del(atom);
0751     }
0752 
0753     return 0;
0754 }
0755 
0756 static struct kwork_class kwork_irq;
0757 static int process_irq_handler_entry_event(struct perf_tool *tool,
0758                        struct evsel *evsel,
0759                        struct perf_sample *sample,
0760                        struct machine *machine)
0761 {
0762     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0763 
0764     if (kwork->tp_handler->entry_event)
0765         return kwork->tp_handler->entry_event(kwork, &kwork_irq,
0766                               evsel, sample, machine);
0767     return 0;
0768 }
0769 
0770 static int process_irq_handler_exit_event(struct perf_tool *tool,
0771                       struct evsel *evsel,
0772                       struct perf_sample *sample,
0773                       struct machine *machine)
0774 {
0775     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0776 
0777     if (kwork->tp_handler->exit_event)
0778         return kwork->tp_handler->exit_event(kwork, &kwork_irq,
0779                              evsel, sample, machine);
0780     return 0;
0781 }
0782 
0783 const struct evsel_str_handler irq_tp_handlers[] = {
0784     { "irq:irq_handler_entry", process_irq_handler_entry_event, },
0785     { "irq:irq_handler_exit",  process_irq_handler_exit_event,  },
0786 };
0787 
0788 static int irq_class_init(struct kwork_class *class,
0789               struct perf_session *session)
0790 {
0791     if (perf_session__set_tracepoints_handlers(session, irq_tp_handlers)) {
0792         pr_err("Failed to set irq tracepoints handlers\n");
0793         return -1;
0794     }
0795 
0796     class->work_root = RB_ROOT_CACHED;
0797     return 0;
0798 }
0799 
0800 static void irq_work_init(struct kwork_class *class,
0801               struct kwork_work *work,
0802               struct evsel *evsel,
0803               struct perf_sample *sample,
0804               struct machine *machine __maybe_unused)
0805 {
0806     work->class = class;
0807     work->cpu = sample->cpu;
0808     work->id = evsel__intval(evsel, sample, "irq");
0809     work->name = evsel__strval(evsel, sample, "name");
0810 }
0811 
0812 static void irq_work_name(struct kwork_work *work, char *buf, int len)
0813 {
0814     snprintf(buf, len, "%s:%" PRIu64 "", work->name, work->id);
0815 }
0816 
0817 static struct kwork_class kwork_irq = {
0818     .name           = "irq",
0819     .type           = KWORK_CLASS_IRQ,
0820     .nr_tracepoints = 2,
0821     .tp_handlers    = irq_tp_handlers,
0822     .class_init     = irq_class_init,
0823     .work_init      = irq_work_init,
0824     .work_name      = irq_work_name,
0825 };
0826 
0827 static struct kwork_class kwork_softirq;
0828 static int process_softirq_raise_event(struct perf_tool *tool,
0829                        struct evsel *evsel,
0830                        struct perf_sample *sample,
0831                        struct machine *machine)
0832 {
0833     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0834 
0835     if (kwork->tp_handler->raise_event)
0836         return kwork->tp_handler->raise_event(kwork, &kwork_softirq,
0837                               evsel, sample, machine);
0838 
0839     return 0;
0840 }
0841 
0842 static int process_softirq_entry_event(struct perf_tool *tool,
0843                        struct evsel *evsel,
0844                        struct perf_sample *sample,
0845                        struct machine *machine)
0846 {
0847     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0848 
0849     if (kwork->tp_handler->entry_event)
0850         return kwork->tp_handler->entry_event(kwork, &kwork_softirq,
0851                               evsel, sample, machine);
0852 
0853     return 0;
0854 }
0855 
0856 static int process_softirq_exit_event(struct perf_tool *tool,
0857                       struct evsel *evsel,
0858                       struct perf_sample *sample,
0859                       struct machine *machine)
0860 {
0861     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0862 
0863     if (kwork->tp_handler->exit_event)
0864         return kwork->tp_handler->exit_event(kwork, &kwork_softirq,
0865                              evsel, sample, machine);
0866 
0867     return 0;
0868 }
0869 
0870 const struct evsel_str_handler softirq_tp_handlers[] = {
0871     { "irq:softirq_raise", process_softirq_raise_event, },
0872     { "irq:softirq_entry", process_softirq_entry_event, },
0873     { "irq:softirq_exit",  process_softirq_exit_event,  },
0874 };
0875 
0876 static int softirq_class_init(struct kwork_class *class,
0877                   struct perf_session *session)
0878 {
0879     if (perf_session__set_tracepoints_handlers(session,
0880                            softirq_tp_handlers)) {
0881         pr_err("Failed to set softirq tracepoints handlers\n");
0882         return -1;
0883     }
0884 
0885     class->work_root = RB_ROOT_CACHED;
0886     return 0;
0887 }
0888 
0889 static char *evsel__softirq_name(struct evsel *evsel, u64 num)
0890 {
0891     char *name = NULL;
0892     bool found = false;
0893     struct tep_print_flag_sym *sym = NULL;
0894     struct tep_print_arg *args = evsel->tp_format->print_fmt.args;
0895 
0896     if ((args == NULL) || (args->next == NULL))
0897         return NULL;
0898 
0899     /* skip softirq field: "REC->vec" */
0900     for (sym = args->next->symbol.symbols; sym != NULL; sym = sym->next) {
0901         if ((eval_flag(sym->value) == (unsigned long long)num) &&
0902             (strlen(sym->str) != 0)) {
0903             found = true;
0904             break;
0905         }
0906     }
0907 
0908     if (!found)
0909         return NULL;
0910 
0911     name = strdup(sym->str);
0912     if (name == NULL) {
0913         pr_err("Failed to copy symbol name\n");
0914         return NULL;
0915     }
0916     return name;
0917 }
0918 
0919 static void softirq_work_init(struct kwork_class *class,
0920                   struct kwork_work *work,
0921                   struct evsel *evsel,
0922                   struct perf_sample *sample,
0923                   struct machine *machine __maybe_unused)
0924 {
0925     u64 num = evsel__intval(evsel, sample, "vec");
0926 
0927     work->id = num;
0928     work->class = class;
0929     work->cpu = sample->cpu;
0930     work->name = evsel__softirq_name(evsel, num);
0931 }
0932 
0933 static void softirq_work_name(struct kwork_work *work, char *buf, int len)
0934 {
0935     snprintf(buf, len, "(s)%s:%" PRIu64 "", work->name, work->id);
0936 }
0937 
0938 static struct kwork_class kwork_softirq = {
0939     .name           = "softirq",
0940     .type           = KWORK_CLASS_SOFTIRQ,
0941     .nr_tracepoints = 3,
0942     .tp_handlers    = softirq_tp_handlers,
0943     .class_init     = softirq_class_init,
0944     .work_init      = softirq_work_init,
0945     .work_name      = softirq_work_name,
0946 };
0947 
0948 static struct kwork_class kwork_workqueue;
0949 static int process_workqueue_activate_work_event(struct perf_tool *tool,
0950                          struct evsel *evsel,
0951                          struct perf_sample *sample,
0952                          struct machine *machine)
0953 {
0954     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0955 
0956     if (kwork->tp_handler->raise_event)
0957         return kwork->tp_handler->raise_event(kwork, &kwork_workqueue,
0958                             evsel, sample, machine);
0959 
0960     return 0;
0961 }
0962 
0963 static int process_workqueue_execute_start_event(struct perf_tool *tool,
0964                          struct evsel *evsel,
0965                          struct perf_sample *sample,
0966                          struct machine *machine)
0967 {
0968     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0969 
0970     if (kwork->tp_handler->entry_event)
0971         return kwork->tp_handler->entry_event(kwork, &kwork_workqueue,
0972                             evsel, sample, machine);
0973 
0974     return 0;
0975 }
0976 
0977 static int process_workqueue_execute_end_event(struct perf_tool *tool,
0978                            struct evsel *evsel,
0979                            struct perf_sample *sample,
0980                            struct machine *machine)
0981 {
0982     struct perf_kwork *kwork = container_of(tool, struct perf_kwork, tool);
0983 
0984     if (kwork->tp_handler->exit_event)
0985         return kwork->tp_handler->exit_event(kwork, &kwork_workqueue,
0986                            evsel, sample, machine);
0987 
0988     return 0;
0989 }
0990 
0991 const struct evsel_str_handler workqueue_tp_handlers[] = {
0992     { "workqueue:workqueue_activate_work", process_workqueue_activate_work_event, },
0993     { "workqueue:workqueue_execute_start", process_workqueue_execute_start_event, },
0994     { "workqueue:workqueue_execute_end",   process_workqueue_execute_end_event,   },
0995 };
0996 
0997 static int workqueue_class_init(struct kwork_class *class,
0998                 struct perf_session *session)
0999 {
1000     if (perf_session__set_tracepoints_handlers(session,
1001                            workqueue_tp_handlers)) {
1002         pr_err("Failed to set workqueue tracepoints handlers\n");
1003         return -1;
1004     }
1005 
1006     class->work_root = RB_ROOT_CACHED;
1007     return 0;
1008 }
1009 
1010 static void workqueue_work_init(struct kwork_class *class,
1011                 struct kwork_work *work,
1012                 struct evsel *evsel,
1013                 struct perf_sample *sample,
1014                 struct machine *machine)
1015 {
1016     char *modp = NULL;
1017     unsigned long long function_addr = evsel__intval(evsel,
1018                              sample, "function");
1019 
1020     work->class = class;
1021     work->cpu = sample->cpu;
1022     work->id = evsel__intval(evsel, sample, "work");
1023     work->name = function_addr == 0 ? NULL :
1024         machine__resolve_kernel_addr(machine, &function_addr, &modp);
1025 }
1026 
1027 static void workqueue_work_name(struct kwork_work *work, char *buf, int len)
1028 {
1029     if (work->name != NULL)
1030         snprintf(buf, len, "(w)%s", work->name);
1031     else
1032         snprintf(buf, len, "(w)0x%" PRIx64, work->id);
1033 }
1034 
1035 static struct kwork_class kwork_workqueue = {
1036     .name           = "workqueue",
1037     .type           = KWORK_CLASS_WORKQUEUE,
1038     .nr_tracepoints = 3,
1039     .tp_handlers    = workqueue_tp_handlers,
1040     .class_init     = workqueue_class_init,
1041     .work_init      = workqueue_work_init,
1042     .work_name      = workqueue_work_name,
1043 };
1044 
1045 static struct kwork_class *kwork_class_supported_list[KWORK_CLASS_MAX] = {
1046     [KWORK_CLASS_IRQ]       = &kwork_irq,
1047     [KWORK_CLASS_SOFTIRQ]   = &kwork_softirq,
1048     [KWORK_CLASS_WORKQUEUE] = &kwork_workqueue,
1049 };
1050 
1051 static void print_separator(int len)
1052 {
1053     printf(" %.*s\n", len, graph_dotted_line);
1054 }
1055 
1056 static int report_print_work(struct perf_kwork *kwork, struct kwork_work *work)
1057 {
1058     int ret = 0;
1059     char kwork_name[PRINT_KWORK_NAME_WIDTH];
1060     char max_runtime_start[32], max_runtime_end[32];
1061     char max_latency_start[32], max_latency_end[32];
1062 
1063     printf(" ");
1064 
1065     /*
1066      * kwork name
1067      */
1068     if (work->class && work->class->work_name) {
1069         work->class->work_name(work, kwork_name,
1070                        PRINT_KWORK_NAME_WIDTH);
1071         ret += printf(" %-*s |", PRINT_KWORK_NAME_WIDTH, kwork_name);
1072     } else {
1073         ret += printf(" %-*s |", PRINT_KWORK_NAME_WIDTH, "");
1074     }
1075 
1076     /*
1077      * cpu
1078      */
1079     ret += printf(" %0*d |", PRINT_CPU_WIDTH, work->cpu);
1080 
1081     /*
1082      * total runtime
1083      */
1084     if (kwork->report == KWORK_REPORT_RUNTIME) {
1085         ret += printf(" %*.*f ms |",
1086                   PRINT_RUNTIME_WIDTH, RPINT_DECIMAL_WIDTH,
1087                   (double)work->total_runtime / NSEC_PER_MSEC);
1088     } else if (kwork->report == KWORK_REPORT_LATENCY) { // avg delay
1089         ret += printf(" %*.*f ms |",
1090                   PRINT_LATENCY_WIDTH, RPINT_DECIMAL_WIDTH,
1091                   (double)work->total_latency /
1092                   work->nr_atoms / NSEC_PER_MSEC);
1093     }
1094 
1095     /*
1096      * count
1097      */
1098     ret += printf(" %*" PRIu64 " |", PRINT_COUNT_WIDTH, work->nr_atoms);
1099 
1100     /*
1101      * max runtime, max runtime start, max runtime end
1102      */
1103     if (kwork->report == KWORK_REPORT_RUNTIME) {
1104         timestamp__scnprintf_usec(work->max_runtime_start,
1105                       max_runtime_start,
1106                       sizeof(max_runtime_start));
1107         timestamp__scnprintf_usec(work->max_runtime_end,
1108                       max_runtime_end,
1109                       sizeof(max_runtime_end));
1110         ret += printf(" %*.*f ms | %*s s | %*s s |",
1111                   PRINT_RUNTIME_WIDTH, RPINT_DECIMAL_WIDTH,
1112                   (double)work->max_runtime / NSEC_PER_MSEC,
1113                   PRINT_TIMESTAMP_WIDTH, max_runtime_start,
1114                   PRINT_TIMESTAMP_WIDTH, max_runtime_end);
1115     }
1116     /*
1117      * max delay, max delay start, max delay end
1118      */
1119     else if (kwork->report == KWORK_REPORT_LATENCY) {
1120         timestamp__scnprintf_usec(work->max_latency_start,
1121                       max_latency_start,
1122                       sizeof(max_latency_start));
1123         timestamp__scnprintf_usec(work->max_latency_end,
1124                       max_latency_end,
1125                       sizeof(max_latency_end));
1126         ret += printf(" %*.*f ms | %*s s | %*s s |",
1127                   PRINT_LATENCY_WIDTH, RPINT_DECIMAL_WIDTH,
1128                   (double)work->max_latency / NSEC_PER_MSEC,
1129                   PRINT_TIMESTAMP_WIDTH, max_latency_start,
1130                   PRINT_TIMESTAMP_WIDTH, max_latency_end);
1131     }
1132 
1133     printf("\n");
1134     return ret;
1135 }
1136 
1137 static int report_print_header(struct perf_kwork *kwork)
1138 {
1139     int ret;
1140 
1141     printf("\n ");
1142     ret = printf(" %-*s | %-*s |",
1143              PRINT_KWORK_NAME_WIDTH, "Kwork Name",
1144              PRINT_CPU_WIDTH, "Cpu");
1145 
1146     if (kwork->report == KWORK_REPORT_RUNTIME) {
1147         ret += printf(" %-*s |",
1148                   PRINT_RUNTIME_HEADER_WIDTH, "Total Runtime");
1149     } else if (kwork->report == KWORK_REPORT_LATENCY) {
1150         ret += printf(" %-*s |",
1151                   PRINT_LATENCY_HEADER_WIDTH, "Avg delay");
1152     }
1153 
1154     ret += printf(" %-*s |", PRINT_COUNT_WIDTH, "Count");
1155 
1156     if (kwork->report == KWORK_REPORT_RUNTIME) {
1157         ret += printf(" %-*s | %-*s | %-*s |",
1158                   PRINT_RUNTIME_HEADER_WIDTH, "Max runtime",
1159                   PRINT_TIMESTAMP_HEADER_WIDTH, "Max runtime start",
1160                   PRINT_TIMESTAMP_HEADER_WIDTH, "Max runtime end");
1161     } else if (kwork->report == KWORK_REPORT_LATENCY) {
1162         ret += printf(" %-*s | %-*s | %-*s |",
1163                   PRINT_LATENCY_HEADER_WIDTH, "Max delay",
1164                   PRINT_TIMESTAMP_HEADER_WIDTH, "Max delay start",
1165                   PRINT_TIMESTAMP_HEADER_WIDTH, "Max delay end");
1166     }
1167 
1168     printf("\n");
1169     print_separator(ret);
1170     return ret;
1171 }
1172 
1173 static void timehist_print_header(void)
1174 {
1175     /*
1176      * header row
1177      */
1178     printf(" %-*s  %-*s  %-*s  %-*s  %-*s  %-*s\n",
1179            PRINT_TIMESTAMP_WIDTH, "Runtime start",
1180            PRINT_TIMESTAMP_WIDTH, "Runtime end",
1181            PRINT_TIMEHIST_CPU_WIDTH, "Cpu",
1182            PRINT_KWORK_NAME_WIDTH, "Kwork name",
1183            PRINT_RUNTIME_WIDTH, "Runtime",
1184            PRINT_RUNTIME_WIDTH, "Delaytime");
1185 
1186     /*
1187      * units row
1188      */
1189     printf(" %-*s  %-*s  %-*s  %-*s  %-*s  %-*s\n",
1190            PRINT_TIMESTAMP_WIDTH, "",
1191            PRINT_TIMESTAMP_WIDTH, "",
1192            PRINT_TIMEHIST_CPU_WIDTH, "",
1193            PRINT_KWORK_NAME_WIDTH, "(TYPE)NAME:NUM",
1194            PRINT_RUNTIME_WIDTH, "(msec)",
1195            PRINT_RUNTIME_WIDTH, "(msec)");
1196 
1197     /*
1198      * separator
1199      */
1200     printf(" %.*s  %.*s  %.*s  %.*s  %.*s  %.*s\n",
1201            PRINT_TIMESTAMP_WIDTH, graph_dotted_line,
1202            PRINT_TIMESTAMP_WIDTH, graph_dotted_line,
1203            PRINT_TIMEHIST_CPU_WIDTH, graph_dotted_line,
1204            PRINT_KWORK_NAME_WIDTH, graph_dotted_line,
1205            PRINT_RUNTIME_WIDTH, graph_dotted_line,
1206            PRINT_RUNTIME_WIDTH, graph_dotted_line);
1207 }
1208 
1209 static void print_summary(struct perf_kwork *kwork)
1210 {
1211     u64 time = kwork->timeend - kwork->timestart;
1212 
1213     printf("  Total count            : %9" PRIu64 "\n", kwork->all_count);
1214     printf("  Total runtime   (msec) : %9.3f (%.3f%% load average)\n",
1215            (double)kwork->all_runtime / NSEC_PER_MSEC,
1216            time == 0 ? 0 : (double)kwork->all_runtime / time);
1217     printf("  Total time span (msec) : %9.3f\n",
1218            (double)time / NSEC_PER_MSEC);
1219 }
1220 
1221 static unsigned long long nr_list_entry(struct list_head *head)
1222 {
1223     struct list_head *pos;
1224     unsigned long long n = 0;
1225 
1226     list_for_each(pos, head)
1227         n++;
1228 
1229     return n;
1230 }
1231 
1232 static void print_skipped_events(struct perf_kwork *kwork)
1233 {
1234     int i;
1235     const char *const kwork_event_str[] = {
1236         [KWORK_TRACE_RAISE] = "raise",
1237         [KWORK_TRACE_ENTRY] = "entry",
1238         [KWORK_TRACE_EXIT]  = "exit",
1239     };
1240 
1241     if ((kwork->nr_skipped_events[KWORK_TRACE_MAX] != 0) &&
1242         (kwork->nr_events != 0)) {
1243         printf("  INFO: %.3f%% skipped events (%" PRIu64 " including ",
1244                (double)kwork->nr_skipped_events[KWORK_TRACE_MAX] /
1245                (double)kwork->nr_events * 100.0,
1246                kwork->nr_skipped_events[KWORK_TRACE_MAX]);
1247 
1248         for (i = 0; i < KWORK_TRACE_MAX; i++) {
1249             printf("%" PRIu64 " %s%s",
1250                    kwork->nr_skipped_events[i],
1251                    kwork_event_str[i],
1252                    (i == KWORK_TRACE_MAX - 1) ? ")\n" : ", ");
1253         }
1254     }
1255 
1256     if (verbose > 0)
1257         printf("  INFO: use %lld atom pages\n",
1258                nr_list_entry(&kwork->atom_page_list));
1259 }
1260 
1261 static void print_bad_events(struct perf_kwork *kwork)
1262 {
1263     if ((kwork->nr_lost_events != 0) && (kwork->nr_events != 0)) {
1264         printf("  INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n",
1265                (double)kwork->nr_lost_events /
1266                (double)kwork->nr_events * 100.0,
1267                kwork->nr_lost_events, kwork->nr_events,
1268                kwork->nr_lost_chunks);
1269     }
1270 }
1271 
1272 static void work_sort(struct perf_kwork *kwork, struct kwork_class *class)
1273 {
1274     struct rb_node *node;
1275     struct kwork_work *data;
1276     struct rb_root_cached *root = &class->work_root;
1277 
1278     pr_debug("Sorting %s ...\n", class->name);
1279     for (;;) {
1280         node = rb_first_cached(root);
1281         if (!node)
1282             break;
1283 
1284         rb_erase_cached(node, root);
1285         data = rb_entry(node, struct kwork_work, node);
1286         work_insert(&kwork->sorted_work_root,
1287                    data, &kwork->sort_list);
1288     }
1289 }
1290 
1291 static void perf_kwork__sort(struct perf_kwork *kwork)
1292 {
1293     struct kwork_class *class;
1294 
1295     list_for_each_entry(class, &kwork->class_list, list)
1296         work_sort(kwork, class);
1297 }
1298 
1299 static int perf_kwork__check_config(struct perf_kwork *kwork,
1300                     struct perf_session *session)
1301 {
1302     int ret;
1303     struct evsel *evsel;
1304     struct kwork_class *class;
1305 
1306     static struct trace_kwork_handler report_ops = {
1307         .entry_event = report_entry_event,
1308         .exit_event  = report_exit_event,
1309     };
1310     static struct trace_kwork_handler latency_ops = {
1311         .raise_event = latency_raise_event,
1312         .entry_event = latency_entry_event,
1313     };
1314     static struct trace_kwork_handler timehist_ops = {
1315         .raise_event = timehist_raise_event,
1316         .entry_event = timehist_entry_event,
1317         .exit_event  = timehist_exit_event,
1318     };
1319 
1320     switch (kwork->report) {
1321     case KWORK_REPORT_RUNTIME:
1322         kwork->tp_handler = &report_ops;
1323         break;
1324     case KWORK_REPORT_LATENCY:
1325         kwork->tp_handler = &latency_ops;
1326         break;
1327     case KWORK_REPORT_TIMEHIST:
1328         kwork->tp_handler = &timehist_ops;
1329         break;
1330     default:
1331         pr_debug("Invalid report type %d\n", kwork->report);
1332         return -1;
1333     }
1334 
1335     list_for_each_entry(class, &kwork->class_list, list)
1336         if ((class->class_init != NULL) &&
1337             (class->class_init(class, session) != 0))
1338             return -1;
1339 
1340     if (kwork->cpu_list != NULL) {
1341         ret = perf_session__cpu_bitmap(session,
1342                            kwork->cpu_list,
1343                            kwork->cpu_bitmap);
1344         if (ret < 0) {
1345             pr_err("Invalid cpu bitmap\n");
1346             return -1;
1347         }
1348     }
1349 
1350     if (kwork->time_str != NULL) {
1351         ret = perf_time__parse_str(&kwork->ptime, kwork->time_str);
1352         if (ret != 0) {
1353             pr_err("Invalid time span\n");
1354             return -1;
1355         }
1356     }
1357 
1358     list_for_each_entry(evsel, &session->evlist->core.entries, core.node) {
1359         if (kwork->show_callchain && !evsel__has_callchain(evsel)) {
1360             pr_debug("Samples do not have callchains\n");
1361             kwork->show_callchain = 0;
1362             symbol_conf.use_callchain = 0;
1363         }
1364     }
1365 
1366     return 0;
1367 }
1368 
1369 static int perf_kwork__read_events(struct perf_kwork *kwork)
1370 {
1371     int ret = -1;
1372     struct perf_session *session = NULL;
1373 
1374     struct perf_data data = {
1375         .path  = input_name,
1376         .mode  = PERF_DATA_MODE_READ,
1377         .force = kwork->force,
1378     };
1379 
1380     session = perf_session__new(&data, &kwork->tool);
1381     if (IS_ERR(session)) {
1382         pr_debug("Error creating perf session\n");
1383         return PTR_ERR(session);
1384     }
1385 
1386     symbol__init(&session->header.env);
1387 
1388     if (perf_kwork__check_config(kwork, session) != 0)
1389         goto out_delete;
1390 
1391     if (session->tevent.pevent &&
1392         tep_set_function_resolver(session->tevent.pevent,
1393                       machine__resolve_kernel_addr,
1394                       &session->machines.host) < 0) {
1395         pr_err("Failed to set libtraceevent function resolver\n");
1396         goto out_delete;
1397     }
1398 
1399     if (kwork->report == KWORK_REPORT_TIMEHIST)
1400         timehist_print_header();
1401 
1402     ret = perf_session__process_events(session);
1403     if (ret) {
1404         pr_debug("Failed to process events, error %d\n", ret);
1405         goto out_delete;
1406     }
1407 
1408     kwork->nr_events      = session->evlist->stats.nr_events[0];
1409     kwork->nr_lost_events = session->evlist->stats.total_lost;
1410     kwork->nr_lost_chunks = session->evlist->stats.nr_events[PERF_RECORD_LOST];
1411 
1412 out_delete:
1413     perf_session__delete(session);
1414     return ret;
1415 }
1416 
1417 static void process_skipped_events(struct perf_kwork *kwork,
1418                    struct kwork_work *work)
1419 {
1420     int i;
1421     unsigned long long count;
1422 
1423     for (i = 0; i < KWORK_TRACE_MAX; i++) {
1424         count = nr_list_entry(&work->atom_list[i]);
1425         kwork->nr_skipped_events[i] += count;
1426         kwork->nr_skipped_events[KWORK_TRACE_MAX] += count;
1427     }
1428 }
1429 
1430 struct kwork_work *perf_kwork_add_work(struct perf_kwork *kwork,
1431                        struct kwork_class *class,
1432                        struct kwork_work *key)
1433 {
1434     struct kwork_work *work = NULL;
1435 
1436     work = work_new(key);
1437     if (work == NULL)
1438         return NULL;
1439 
1440     work_insert(&class->work_root, work, &kwork->cmp_id);
1441     return work;
1442 }
1443 
1444 static void sig_handler(int sig)
1445 {
1446     /*
1447      * Simply capture termination signal so that
1448      * the program can continue after pause returns
1449      */
1450     pr_debug("Captuer signal %d\n", sig);
1451 }
1452 
1453 static int perf_kwork__report_bpf(struct perf_kwork *kwork)
1454 {
1455     int ret;
1456 
1457     signal(SIGINT, sig_handler);
1458     signal(SIGTERM, sig_handler);
1459 
1460     ret = perf_kwork__trace_prepare_bpf(kwork);
1461     if (ret)
1462         return -1;
1463 
1464     printf("Starting trace, Hit <Ctrl+C> to stop and report\n");
1465 
1466     perf_kwork__trace_start();
1467 
1468     /*
1469      * a simple pause, wait here for stop signal
1470      */
1471     pause();
1472 
1473     perf_kwork__trace_finish();
1474 
1475     perf_kwork__report_read_bpf(kwork);
1476 
1477     perf_kwork__report_cleanup_bpf();
1478 
1479     return 0;
1480 }
1481 
1482 static int perf_kwork__report(struct perf_kwork *kwork)
1483 {
1484     int ret;
1485     struct rb_node *next;
1486     struct kwork_work *work;
1487 
1488     if (kwork->use_bpf)
1489         ret = perf_kwork__report_bpf(kwork);
1490     else
1491         ret = perf_kwork__read_events(kwork);
1492 
1493     if (ret != 0)
1494         return -1;
1495 
1496     perf_kwork__sort(kwork);
1497 
1498     setup_pager();
1499 
1500     ret = report_print_header(kwork);
1501     next = rb_first_cached(&kwork->sorted_work_root);
1502     while (next) {
1503         work = rb_entry(next, struct kwork_work, node);
1504         process_skipped_events(kwork, work);
1505 
1506         if (work->nr_atoms != 0) {
1507             report_print_work(kwork, work);
1508             if (kwork->summary) {
1509                 kwork->all_runtime += work->total_runtime;
1510                 kwork->all_count += work->nr_atoms;
1511             }
1512         }
1513         next = rb_next(next);
1514     }
1515     print_separator(ret);
1516 
1517     if (kwork->summary) {
1518         print_summary(kwork);
1519         print_separator(ret);
1520     }
1521 
1522     print_bad_events(kwork);
1523     print_skipped_events(kwork);
1524     printf("\n");
1525 
1526     return 0;
1527 }
1528 
1529 typedef int (*tracepoint_handler)(struct perf_tool *tool,
1530                   struct evsel *evsel,
1531                   struct perf_sample *sample,
1532                   struct machine *machine);
1533 
1534 static int perf_kwork__process_tracepoint_sample(struct perf_tool *tool,
1535                          union perf_event *event __maybe_unused,
1536                          struct perf_sample *sample,
1537                          struct evsel *evsel,
1538                          struct machine *machine)
1539 {
1540     int err = 0;
1541 
1542     if (evsel->handler != NULL) {
1543         tracepoint_handler f = evsel->handler;
1544 
1545         err = f(tool, evsel, sample, machine);
1546     }
1547 
1548     return err;
1549 }
1550 
1551 static int perf_kwork__timehist(struct perf_kwork *kwork)
1552 {
1553     /*
1554      * event handlers for timehist option
1555      */
1556     kwork->tool.comm     = perf_event__process_comm;
1557     kwork->tool.exit     = perf_event__process_exit;
1558     kwork->tool.fork     = perf_event__process_fork;
1559     kwork->tool.attr     = perf_event__process_attr;
1560     kwork->tool.tracing_data = perf_event__process_tracing_data;
1561     kwork->tool.build_id     = perf_event__process_build_id;
1562     kwork->tool.ordered_events = true;
1563     kwork->tool.ordering_requires_timestamps = true;
1564     symbol_conf.use_callchain = kwork->show_callchain;
1565 
1566     if (symbol__validate_sym_arguments()) {
1567         pr_err("Failed to validate sym arguments\n");
1568         return -1;
1569     }
1570 
1571     setup_pager();
1572 
1573     return perf_kwork__read_events(kwork);
1574 }
1575 
1576 static void setup_event_list(struct perf_kwork *kwork,
1577                  const struct option *options,
1578                  const char * const usage_msg[])
1579 {
1580     int i;
1581     struct kwork_class *class;
1582     char *tmp, *tok, *str;
1583 
1584     if (kwork->event_list_str == NULL)
1585         goto null_event_list_str;
1586 
1587     str = strdup(kwork->event_list_str);
1588     for (tok = strtok_r(str, ", ", &tmp);
1589          tok; tok = strtok_r(NULL, ", ", &tmp)) {
1590         for (i = 0; i < KWORK_CLASS_MAX; i++) {
1591             class = kwork_class_supported_list[i];
1592             if (strcmp(tok, class->name) == 0) {
1593                 list_add_tail(&class->list, &kwork->class_list);
1594                 break;
1595             }
1596         }
1597         if (i == KWORK_CLASS_MAX) {
1598             usage_with_options_msg(usage_msg, options,
1599                            "Unknown --event key: `%s'", tok);
1600         }
1601     }
1602     free(str);
1603 
1604 null_event_list_str:
1605     /*
1606      * config all kwork events if not specified
1607      */
1608     if (list_empty(&kwork->class_list)) {
1609         for (i = 0; i < KWORK_CLASS_MAX; i++) {
1610             list_add_tail(&kwork_class_supported_list[i]->list,
1611                       &kwork->class_list);
1612         }
1613     }
1614 
1615     pr_debug("Config event list:");
1616     list_for_each_entry(class, &kwork->class_list, list)
1617         pr_debug(" %s", class->name);
1618     pr_debug("\n");
1619 }
1620 
1621 static int perf_kwork__record(struct perf_kwork *kwork,
1622                   int argc, const char **argv)
1623 {
1624     const char **rec_argv;
1625     unsigned int rec_argc, i, j;
1626     struct kwork_class *class;
1627 
1628     const char *const record_args[] = {
1629         "record",
1630         "-a",
1631         "-R",
1632         "-m", "1024",
1633         "-c", "1",
1634     };
1635 
1636     rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1637 
1638     list_for_each_entry(class, &kwork->class_list, list)
1639         rec_argc += 2 * class->nr_tracepoints;
1640 
1641     rec_argv = calloc(rec_argc + 1, sizeof(char *));
1642     if (rec_argv == NULL)
1643         return -ENOMEM;
1644 
1645     for (i = 0; i < ARRAY_SIZE(record_args); i++)
1646         rec_argv[i] = strdup(record_args[i]);
1647 
1648     list_for_each_entry(class, &kwork->class_list, list) {
1649         for (j = 0; j < class->nr_tracepoints; j++) {
1650             rec_argv[i++] = strdup("-e");
1651             rec_argv[i++] = strdup(class->tp_handlers[j].name);
1652         }
1653     }
1654 
1655     for (j = 1; j < (unsigned int)argc; j++, i++)
1656         rec_argv[i] = argv[j];
1657 
1658     BUG_ON(i != rec_argc);
1659 
1660     pr_debug("record comm: ");
1661     for (j = 0; j < rec_argc; j++)
1662         pr_debug("%s ", rec_argv[j]);
1663     pr_debug("\n");
1664 
1665     return cmd_record(i, rec_argv);
1666 }
1667 
1668 int cmd_kwork(int argc, const char **argv)
1669 {
1670     static struct perf_kwork kwork = {
1671         .class_list          = LIST_HEAD_INIT(kwork.class_list),
1672         .tool = {
1673             .mmap    = perf_event__process_mmap,
1674             .mmap2   = perf_event__process_mmap2,
1675             .sample  = perf_kwork__process_tracepoint_sample,
1676         },
1677         .atom_page_list      = LIST_HEAD_INIT(kwork.atom_page_list),
1678         .sort_list           = LIST_HEAD_INIT(kwork.sort_list),
1679         .cmp_id              = LIST_HEAD_INIT(kwork.cmp_id),
1680         .sorted_work_root    = RB_ROOT_CACHED,
1681         .tp_handler          = NULL,
1682         .profile_name        = NULL,
1683         .cpu_list            = NULL,
1684         .time_str            = NULL,
1685         .force               = false,
1686         .event_list_str      = NULL,
1687         .summary             = false,
1688         .sort_order          = NULL,
1689         .show_callchain      = false,
1690         .max_stack           = 5,
1691         .timestart           = 0,
1692         .timeend             = 0,
1693         .nr_events           = 0,
1694         .nr_lost_chunks      = 0,
1695         .nr_lost_events      = 0,
1696         .all_runtime         = 0,
1697         .all_count           = 0,
1698         .nr_skipped_events   = { 0 },
1699     };
1700     static const char default_report_sort_order[] = "runtime, max, count";
1701     static const char default_latency_sort_order[] = "avg, max, count";
1702     const struct option kwork_options[] = {
1703     OPT_INCR('v', "verbose", &verbose,
1704          "be more verbose (show symbol address, etc)"),
1705     OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1706             "dump raw trace in ASCII"),
1707     OPT_STRING('k', "kwork", &kwork.event_list_str, "kwork",
1708            "list of kwork to profile (irq, softirq, workqueue, etc)"),
1709     OPT_BOOLEAN('f', "force", &kwork.force, "don't complain, do it"),
1710     OPT_END()
1711     };
1712     const struct option report_options[] = {
1713     OPT_STRING('s', "sort", &kwork.sort_order, "key[,key2...]",
1714            "sort by key(s): runtime, max, count"),
1715     OPT_STRING('C', "cpu", &kwork.cpu_list, "cpu",
1716            "list of cpus to profile"),
1717     OPT_STRING('n', "name", &kwork.profile_name, "name",
1718            "event name to profile"),
1719     OPT_STRING(0, "time", &kwork.time_str, "str",
1720            "Time span for analysis (start,stop)"),
1721     OPT_STRING('i', "input", &input_name, "file",
1722            "input file name"),
1723     OPT_BOOLEAN('S', "with-summary", &kwork.summary,
1724             "Show summary with statistics"),
1725 #ifdef HAVE_BPF_SKEL
1726     OPT_BOOLEAN('b', "use-bpf", &kwork.use_bpf,
1727             "Use BPF to measure kwork runtime"),
1728 #endif
1729     OPT_PARENT(kwork_options)
1730     };
1731     const struct option latency_options[] = {
1732     OPT_STRING('s', "sort", &kwork.sort_order, "key[,key2...]",
1733            "sort by key(s): avg, max, count"),
1734     OPT_STRING('C', "cpu", &kwork.cpu_list, "cpu",
1735            "list of cpus to profile"),
1736     OPT_STRING('n', "name", &kwork.profile_name, "name",
1737            "event name to profile"),
1738     OPT_STRING(0, "time", &kwork.time_str, "str",
1739            "Time span for analysis (start,stop)"),
1740     OPT_STRING('i', "input", &input_name, "file",
1741            "input file name"),
1742 #ifdef HAVE_BPF_SKEL
1743     OPT_BOOLEAN('b', "use-bpf", &kwork.use_bpf,
1744             "Use BPF to measure kwork latency"),
1745 #endif
1746     OPT_PARENT(kwork_options)
1747     };
1748     const struct option timehist_options[] = {
1749     OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
1750            "file", "vmlinux pathname"),
1751     OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
1752            "file", "kallsyms pathname"),
1753     OPT_BOOLEAN('g', "call-graph", &kwork.show_callchain,
1754             "Display call chains if present"),
1755     OPT_UINTEGER(0, "max-stack", &kwork.max_stack,
1756            "Maximum number of functions to display backtrace."),
1757     OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
1758             "Look for files with symbols relative to this directory"),
1759     OPT_STRING(0, "time", &kwork.time_str, "str",
1760            "Time span for analysis (start,stop)"),
1761     OPT_STRING('C', "cpu", &kwork.cpu_list, "cpu",
1762            "list of cpus to profile"),
1763     OPT_STRING('n', "name", &kwork.profile_name, "name",
1764            "event name to profile"),
1765     OPT_STRING('i', "input", &input_name, "file",
1766            "input file name"),
1767     OPT_PARENT(kwork_options)
1768     };
1769     const char *kwork_usage[] = {
1770         NULL,
1771         NULL
1772     };
1773     const char * const report_usage[] = {
1774         "perf kwork report [<options>]",
1775         NULL
1776     };
1777     const char * const latency_usage[] = {
1778         "perf kwork latency [<options>]",
1779         NULL
1780     };
1781     const char * const timehist_usage[] = {
1782         "perf kwork timehist [<options>]",
1783         NULL
1784     };
1785     const char *const kwork_subcommands[] = {
1786         "record", "report", "latency", "timehist", NULL
1787     };
1788 
1789     argc = parse_options_subcommand(argc, argv, kwork_options,
1790                     kwork_subcommands, kwork_usage,
1791                     PARSE_OPT_STOP_AT_NON_OPTION);
1792     if (!argc)
1793         usage_with_options(kwork_usage, kwork_options);
1794 
1795     setup_event_list(&kwork, kwork_options, kwork_usage);
1796     sort_dimension__add(&kwork, "id", &kwork.cmp_id);
1797 
1798     if (strlen(argv[0]) > 2 && strstarts("record", argv[0]))
1799         return perf_kwork__record(&kwork, argc, argv);
1800     else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {
1801         kwork.sort_order = default_report_sort_order;
1802         if (argc > 1) {
1803             argc = parse_options(argc, argv, report_options, report_usage, 0);
1804             if (argc)
1805                 usage_with_options(report_usage, report_options);
1806         }
1807         kwork.report = KWORK_REPORT_RUNTIME;
1808         setup_sorting(&kwork, report_options, report_usage);
1809         return perf_kwork__report(&kwork);
1810     } else if (strlen(argv[0]) > 2 && strstarts("latency", argv[0])) {
1811         kwork.sort_order = default_latency_sort_order;
1812         if (argc > 1) {
1813             argc = parse_options(argc, argv, latency_options, latency_usage, 0);
1814             if (argc)
1815                 usage_with_options(latency_usage, latency_options);
1816         }
1817         kwork.report = KWORK_REPORT_LATENCY;
1818         setup_sorting(&kwork, latency_options, latency_usage);
1819         return perf_kwork__report(&kwork);
1820     } else if (strlen(argv[0]) > 2 && strstarts("timehist", argv[0])) {
1821         if (argc > 1) {
1822             argc = parse_options(argc, argv, timehist_options, timehist_usage, 0);
1823             if (argc)
1824                 usage_with_options(timehist_usage, timehist_options);
1825         }
1826         kwork.report = KWORK_REPORT_TIMEHIST;
1827         return perf_kwork__timehist(&kwork);
1828     } else
1829         usage_with_options(kwork_usage, kwork_options);
1830 
1831     return 0;
1832 }