#ifndef PERF_UTIL_KWORK_H#define PERF_UTIL_KWORK_H #include "perf.h"#include "util/tool.h"#include "util/time-utils.h" #include <stdlib.h>#include <linux/bitmap.h>#include <linux/list.h>#include <linux/rbtree.h>#include <linux/types.h> struct perf_sample;struct perf_session; enum kwork_class_type { KWORK_CLASS_IRQ, KWORK_CLASS_SOFTIRQ, KWORK_CLASS_WORKQUEUE, KWORK_CLASS_SCHED, KWORK_CLASS_MAX,}; enum kwork_report_type { KWORK_REPORT_RUNTIME, KWORK_REPORT_LATENCY, KWORK_REPORT_TIMEHIST, KWORK_REPORT_TOP,}; enum kwork_trace_type { KWORK_TRACE_RAISE, KWORK_TRACE_ENTRY, KWORK_TRACE_EXIT, KWORK_TRACE_MAX,}; /* * data structure: * * +==================+ +============+ +======================+ * | class | | work | | atom | * +==================+ +============+ +======================+ * +------------+ | +-----+ | | +------+ | | +-------+ +-----+ | * | perf_kwork | +-> | irq | --------|+-> | eth0 | --+-> | raise | - | ... | --+ +-----------+ * +-----+------+ || +-----+ ||| +------+ ||| +-------+ +-----+ | | | | * | || ||| ||| | +-> | atom_page | * | || ||| ||| +-------+ +-----+ | | | * | class_list ||| |+-> | entry | - | ... | ----> | | * | || ||| ||| +-------+ +-----+ | | | * | || ||| ||| | +-> | | * | || ||| ||| +-------+ +-----+ | | | | * | || ||| |+-> | exit | - | ... | --+ +-----+-----+ * | || ||| | | +-------+ +-----+ | | * | || ||| | | | | * | || ||| +-----+ | | | | * | || |+-> | ... | | | | | * | || | | +-----+ | | | | * | || | | | | | | * | || +---------+ | | +-----+ | | +-------+ +-----+ | | * | +-> | softirq | -------> | RCU | ---+-> | raise | - | ... | --+ +-----+-----+ * | || +---------+ | | +-----+ ||| +-------+ +-----+ | | | | * | || | | ||| | +-> | atom_page | * | || | | ||| +-------+ +-----+ | | | * | || | | |+-> | entry | - | ... | ----> | | * | || | | ||| +-------+ +-----+ | | | * | || | | ||| | +-> | | * | || | | ||| +-------+ +-----+ | | | | * | || | | |+-> | exit | - | ... | --+ +-----+-----+ * | || | | | | +-------+ +-----+ | | * | || | | | | | | * | || +-----------+ | | +-----+ | | | | * | +-> | workqueue | -----> | ... | | | | | * | | +-----------+ | | +-----+ | | | | * | +==================+ +============+ +======================+ | * | | * +----> atom_page_list ---------------------------------------------------------+ * */ struct kwork_atom { struct list_head list; u64 time; struct kwork_atom *prev; void *page_addr; unsigned long bit_inpage;}; #define NR_ATOM_PER_PAGE 128struct kwork_atom_page { struct list_head list; struct kwork_atom atoms[NR_ATOM_PER_PAGE]; DECLARE_BITMAP(bitmap, NR_ATOM_PER_PAGE);}; struct perf_kwork;struct kwork_class;struct kwork_work { /* * class field */ struct rb_node node; struct kwork_class *class; /* * work field */ u64 id; int cpu; char *name; /* * atom field */ u64 nr_atoms; struct list_head atom_list[KWORK_TRACE_MAX]; /* * runtime report */ u64 max_runtime; u64 max_runtime_start; u64 max_runtime_end; u64 total_runtime; /* * latency report */ u64 max_latency; u64 max_latency_start; u64 max_latency_end; u64 total_latency; /* * top report */ u32 cpu_usage; u32 tgid; bool is_kthread;}; struct kwork_class { struct list_head list; const char *name; enum kwork_class_type type; unsigned int nr_tracepoints; const struct evsel_str_handler *tp_handlers; struct rb_root_cached work_root; int (*class_init)(struct kwork_class *class, struct perf_session *session); void (*work_init)(struct perf_kwork *kwork, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type, struct perf_sample *sample, struct machine *machine); void (*work_name)(struct kwork_work *work, char *buf, int len);}; static inline void work_exit(struct kwork_work *work){ if (work) { free(work->name); work->name = NULL; }} struct trace_kwork_handler { int (*raise_event)(struct perf_kwork *kwork, struct kwork_class *class, struct perf_sample *sample, struct machine *machine); int (*entry_event)(struct perf_kwork *kwork, struct kwork_class *class, struct perf_sample *sample, struct machine *machine); int (*exit_event)(struct perf_kwork *kwork, struct kwork_class *class, struct perf_sample *sample, struct machine *machine); int (*sched_switch_event)(struct perf_kwork *kwork, struct kwork_class *class, struct perf_sample *sample, struct machine *machine);}; struct __top_cpus_runtime { u64 load; u64 idle; u64 irq; u64 softirq; u64 total;}; struct kwork_top_stat { DECLARE_BITMAP(all_cpus_bitmap, MAX_NR_CPUS); struct __top_cpus_runtime *cpus_runtime; unsigned int nr_skipped_cpu;}; struct perf_kwork { /* * metadata */ struct perf_tool tool; struct list_head class_list; struct list_head atom_page_list; struct list_head sort_list, cmp_id; struct rb_root_cached sorted_work_root; const struct trace_kwork_handler *tp_handler; /* * profile filters */ const char *profile_name; const char *cpu_list; DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); const char *time_str; struct perf_time_interval ptime; /* * options for command */ bool force; const char *event_list_str; enum kwork_report_type report; /* * options for subcommand */ bool summary; const char *sort_order; bool show_callchain; unsigned int max_stack; bool use_bpf; /* * statistics */ u64 timestart; u64 timeend; unsigned long nr_events; unsigned long nr_lost_chunks; unsigned long nr_lost_events; u64 all_runtime; u64 all_count; u64 nr_skipped_events[KWORK_TRACE_MAX + 1]; /* * perf kwork top data */ struct kwork_top_stat top_stat; /* Add work callback. */ struct kwork_work *(*add_work)(struct perf_kwork *kwork, struct kwork_class *class, struct kwork_work *key); }; #ifdef HAVE_BPF_SKEL int perf_kwork__trace_prepare_bpf(struct perf_kwork *kwork);int perf_kwork__report_read_bpf(struct perf_kwork *kwork);void perf_kwork__report_cleanup_bpf(void); void perf_kwork__trace_start(void);void perf_kwork__trace_finish(void); int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork);int perf_kwork__top_read_bpf(struct perf_kwork *kwork);void perf_kwork__top_cleanup_bpf(void); void perf_kwork__top_start(void);void perf_kwork__top_finish(void); #else /* !HAVE_BPF_SKEL */ static inline intperf_kwork__trace_prepare_bpf(struct perf_kwork *kwork __maybe_unused){ return -1;} static inline intperf_kwork__report_read_bpf(struct perf_kwork *kwork __maybe_unused){ return -1;} static inline void perf_kwork__report_cleanup_bpf(void) {} static inline void perf_kwork__trace_start(void) {}static inline void perf_kwork__trace_finish(void) {} static inline intperf_kwork__top_prepare_bpf(struct perf_kwork *kwork __maybe_unused){ return -1;} static inline intperf_kwork__top_read_bpf(struct perf_kwork *kwork __maybe_unused){ return -1;} static inline void perf_kwork__top_cleanup_bpf(void) {} static inline void perf_kwork__top_start(void) {}static inline void perf_kwork__top_finish(void) {} #endif /* HAVE_BPF_SKEL */ #endif /* PERF_UTIL_KWORK_H */
#ifndef PERF_UTIL_KWORK_H
#define PERF_UTIL_KWORK_H
#include "perf.h"
#include "util/tool.h"
#include "util/time-utils.h"
#include <stdlib.h>
#include <linux/bitmap.h>
#include <linux/list.h>
#include <linux/rbtree.h>
#include <linux/types.h>
struct perf_sample;
struct perf_session;
enum kwork_class_type {
KWORK_CLASS_IRQ,
KWORK_CLASS_SOFTIRQ,
KWORK_CLASS_WORKQUEUE,
KWORK_CLASS_SCHED,
KWORK_CLASS_MAX,
};
enum kwork_report_type {
KWORK_REPORT_RUNTIME,
KWORK_REPORT_LATENCY,
KWORK_REPORT_TIMEHIST,
KWORK_REPORT_TOP,
};
enum kwork_trace_type {
KWORK_TRACE_RAISE,
KWORK_TRACE_ENTRY,
KWORK_TRACE_EXIT,
KWORK_TRACE_MAX,
};
/*
* data structure:
*
* +==================+ +============+ +======================+
* | class | | work | | atom |
* +==================+ +============+ +======================+
* +------------+ | +-----+ | | +------+ | | +-------+ +-----+ |
* | perf_kwork | +-> | irq | --------|+-> | eth0 | --+-> | raise | - | ... | --+ +-----------+
* +-----+------+ || +-----+ ||| +------+ ||| +-------+ +-----+ | | | |
* | || ||| ||| | +-> | atom_page |
* | || ||| ||| +-------+ +-----+ | | |
* | class_list ||| |+-> | entry | - | ... | ----> | |
* | || ||| ||| +-------+ +-----+ | | |
* | || ||| ||| | +-> | |
* | || ||| ||| +-------+ +-----+ | | | |
* | || ||| |+-> | exit | - | ... | --+ +-----+-----+
* | || ||| | | +-------+ +-----+ | |
* | || ||| | | | |
* | || ||| +-----+ | | | |
* | || |+-> | ... | | | | |
* | || | | +-----+ | | | |
* | || | | | | | |
* | || +---------+ | | +-----+ | | +-------+ +-----+ | |
* | +-> | softirq | -------> | RCU | ---+-> | raise | - | ... | --+ +-----+-----+
* | || +---------+ | | +-----+ ||| +-------+ +-----+ | | | |
* | || | | ||| | +-> | atom_page |
* | || | | ||| +-------+ +-----+ | | |
* | || | | |+-> | entry | - | ... | ----> | |
* | || | | ||| +-------+ +-----+ | | |
* | || | | ||| | +-> | |
* | || | | ||| +-------+ +-----+ | | | |
* | || | | |+-> | exit | - | ... | --+ +-----+-----+
* | || | | | | +-------+ +-----+ | |
* | || | | | | | |
* | || +-----------+ | | +-----+ | | | |
* | +-> | workqueue | -----> | ... | | | | |
* | | +-----------+ | | +-----+ | | | |
* | +==================+ +============+ +======================+ |
* | |
* +----> atom_page_list ---------------------------------------------------------+
*
*/
struct kwork_atom {
struct list_head list;
u64 time;
struct kwork_atom *prev;
void *page_addr;
unsigned long bit_inpage;
};
#define NR_ATOM_PER_PAGE 128
struct kwork_atom_page {
struct list_head list;
struct kwork_atom atoms[NR_ATOM_PER_PAGE];
DECLARE_BITMAP(bitmap, NR_ATOM_PER_PAGE);
};
struct perf_kwork;
struct kwork_class;
struct kwork_work {
/*
* class field
*/
struct rb_node node;
struct kwork_class *class;
/*
* work field
*/
u64 id;
int cpu;
char *name;
/*
* atom field
*/
u64 nr_atoms;
struct list_head atom_list[KWORK_TRACE_MAX];
/*
* runtime report
*/
u64 max_runtime;
u64 max_runtime_start;
u64 max_runtime_end;
u64 total_runtime;
/*
* latency report
*/
u64 max_latency;
u64 max_latency_start;
u64 max_latency_end;
u64 total_latency;
/*
* top report
*/
u32 cpu_usage;
u32 tgid;
bool is_kthread;
};
struct kwork_class {
struct list_head list;
const char *name;
enum kwork_class_type type;
unsigned int nr_tracepoints;
const struct evsel_str_handler *tp_handlers;
struct rb_root_cached work_root;
int (*class_init)(struct kwork_class *class,
struct perf_session *session);
void (*work_init)(struct perf_kwork *kwork,
struct kwork_class *class,
struct kwork_work *work,
enum kwork_trace_type src_type,
struct perf_sample *sample,
struct machine *machine);
void (*work_name)(struct kwork_work *work,
char *buf, int len);
};
static inline void work_exit(struct kwork_work *work)
{
if (work) {
free(work->name);
work->name = NULL;
}
}
struct trace_kwork_handler {
int (*raise_event)(struct perf_kwork *kwork,
struct kwork_class *class,
struct perf_sample *sample, struct machine *machine);
int (*entry_event)(struct perf_kwork *kwork,
struct kwork_class *class,
struct perf_sample *sample, struct machine *machine);
int (*exit_event)(struct perf_kwork *kwork,
struct kwork_class *class,
struct perf_sample *sample, struct machine *machine);
int (*sched_switch_event)(struct perf_kwork *kwork,
struct kwork_class *class,
struct perf_sample *sample, struct machine *machine);
};
struct __top_cpus_runtime {
u64 load;
u64 idle;
u64 irq;
u64 softirq;
u64 total;
};
struct kwork_top_stat {
DECLARE_BITMAP(all_cpus_bitmap, MAX_NR_CPUS);
struct __top_cpus_runtime *cpus_runtime;
unsigned int nr_skipped_cpu;
};
struct perf_kwork {
/*
* metadata
*/
struct perf_tool tool;
struct list_head class_list;
struct list_head atom_page_list;
struct list_head sort_list, cmp_id;
struct rb_root_cached sorted_work_root;
const struct trace_kwork_handler *tp_handler;
/*
* profile filters
*/
const char *profile_name;
const char *cpu_list;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
const char *time_str;
struct perf_time_interval ptime;
/*
* options for command
*/
bool force;
const char *event_list_str;
enum kwork_report_type report;
/*
* options for subcommand
*/
bool summary;
const char *sort_order;
bool show_callchain;
unsigned int max_stack;
bool use_bpf;
/*
* statistics
*/
u64 timestart;
u64 timeend;
unsigned long nr_events;
unsigned long nr_lost_chunks;
unsigned long nr_lost_events;
u64 all_runtime;
u64 all_count;
u64 nr_skipped_events[KWORK_TRACE_MAX + 1];
/*
* perf kwork top data
*/
struct kwork_top_stat top_stat;
/* Add work callback. */
struct kwork_work *(*add_work)(struct perf_kwork *kwork,
struct kwork_class *class,
struct kwork_work *key);
};
#ifdef HAVE_BPF_SKEL
int perf_kwork__trace_prepare_bpf(struct perf_kwork *kwork);
int perf_kwork__report_read_bpf(struct perf_kwork *kwork);
void perf_kwork__report_cleanup_bpf(void);
void perf_kwork__trace_start(void);
void perf_kwork__trace_finish(void);
int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork);
int perf_kwork__top_read_bpf(struct perf_kwork *kwork);
void perf_kwork__top_cleanup_bpf(void);
void perf_kwork__top_start(void);
void perf_kwork__top_finish(void);
#else /* !HAVE_BPF_SKEL */
static inline int
perf_kwork__trace_prepare_bpf(struct perf_kwork *kwork __maybe_unused)
{
return -1;
}
static inline int
perf_kwork__report_read_bpf(struct perf_kwork *kwork __maybe_unused)
{
return -1;
}
static inline void perf_kwork__report_cleanup_bpf(void) {}
static inline void perf_kwork__trace_start(void) {}
static inline void perf_kwork__trace_finish(void) {}
static inline int
perf_kwork__top_prepare_bpf(struct perf_kwork *kwork __maybe_unused)
{
return -1;
}
static inline int
perf_kwork__top_read_bpf(struct perf_kwork *kwork __maybe_unused)
{
return -1;
}
static inline void perf_kwork__top_cleanup_bpf(void) {}
static inline void perf_kwork__top_start(void) {}
static inline void perf_kwork__top_finish(void) {}
#endif /* HAVE_BPF_SKEL */
#endif /* PERF_UTIL_KWORK_H */