diff --git a/lisa/_assets/kmodules/lisa/features.h b/lisa/_assets/kmodules/lisa/features.h index 31e322f41f113a9051edc4ebbe57bdf5c19a355f..ce53620842abe0d0866e3d08949cd243e71ac0f1 100644 --- a/lisa/_assets/kmodules/lisa/features.h +++ b/lisa/_assets/kmodules/lisa/features.h @@ -24,7 +24,7 @@ * they can depend on each other easily. */ struct feature { - char *name; + const char *name; void *data; u64 enabled; struct mutex *lock; diff --git a/lisa/_assets/kmodules/lisa/ftrace_events.h b/lisa/_assets/kmodules/lisa/ftrace_events.h index fefa34bc25e59e427d393fb475d4f889a2a169df..6309c32ba349177b02fed39f66145a86499d86af 100644 --- a/lisa/_assets/kmodules/lisa/ftrace_events.h +++ b/lisa/_assets/kmodules/lisa/ftrace_events.h @@ -28,9 +28,10 @@ #endif #if HAS_KERNEL_FEATURE(CFS_PELT) +// DEPRECATED: use lisa__sched_pelt_cfs_tg instead TRACE_EVENT(lisa__sched_pelt_cfs, - TP_PROTO(int cpu, char *path, const struct sched_avg *avg), + TP_PROTO(int cpu, const char *path, const struct sched_avg *avg), TP_ARGS(cpu, path, avg), @@ -131,6 +132,7 @@ DEFINE_EVENT(lisa__sched_pelt_rq_template, lisa__sched_pelt_irq, TP_ARGS(cpu, avg)); #endif +// DEPRECATED: Use lisa__sched_pelt_cfs_task and lisa__sched_pelt_cfs_tg instead #if HAS_KERNEL_FEATURE(SE_PELT) TRACE_EVENT(lisa__sched_pelt_se, @@ -179,6 +181,96 @@ TRACE_EVENT(lisa__sched_pelt_se, ); #endif +#if HAS_KERNEL_FEATURE(SE_PELT) +TRACE_EVENT(lisa__sched_pelt_cfs_task, + + TP_PROTO(int cpu, int pid, const char *comm, const struct sched_avg *avg), + + TP_ARGS(cpu, pid, comm, avg), + + TP_STRUCT__entry( + __field( unsigned long long, update_time ) + __field( unsigned long, load ) +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + __field( unsigned long, RBL_LOAD_ENTRY ) +#endif + __field( unsigned long, util ) + __field( int, cpu ) + __field( int, pid ) + __array( char, comm, TASK_COMM_LEN ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + strlcpy(__entry->comm, comm, TASK_COMM_LEN); + __entry->pid = pid; + __entry->load = avg->load_avg; +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + __entry->RBL_LOAD_ENTRY = avg->RBL_LOAD_MEMBER; +#endif + __entry->util = avg->util_avg; + __entry->update_time = avg->last_update_time; + ), + + TP_printk( + "cpu=%d comm=%s pid=%d load=%lu util=%lu update_time=%llu" +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + " " RBL_LOAD_STR "=%lu" +#endif + , + __entry->cpu, __entry->comm, __entry->pid, + __entry->load, __entry->util, __entry->update_time +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + ,__entry->RBL_LOAD_ENTRY +#endif + ) +); +#endif + +#if HAS_KERNEL_FEATURE(SE_PELT) +TRACE_EVENT(lisa__sched_pelt_cfs_tg, + + TP_PROTO(int cpu, const char *path, const struct sched_avg *avg), + + TP_ARGS(cpu, path, avg), + + TP_STRUCT__entry( + __field( unsigned long long, update_time ) + __field( unsigned long, load ) +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + __field( unsigned long, RBL_LOAD_ENTRY ) +#endif + __field( unsigned long, util ) + __field( int, cpu ) + __array( char, path, PATH_SIZE ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + strlcpy(__entry->path, path, PATH_SIZE); + __entry->load = avg->load_avg; +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + __entry->RBL_LOAD_ENTRY = avg->RBL_LOAD_MEMBER; +#endif + __entry->util = avg->util_avg; + __entry->update_time = avg->last_update_time; + ), + + TP_printk( + "cpu=%d path=%s load=%lu util=%lu update_time=%llu" +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + " " RBL_LOAD_STR "=%lu" +#endif + , + __entry->cpu, __entry->path, + __entry->load, __entry->util, __entry->update_time +#if HAS_KERNEL_FEATURE(SCHED_AVG_RBL) + ,__entry->RBL_LOAD_ENTRY +#endif + ) +); +#endif + #if HAS_KERNEL_FEATURE(SCHED_OVERUTILIZED) TRACE_EVENT(lisa__sched_overutilized, @@ -224,6 +316,7 @@ TRACE_EVENT(lisa__sched_update_nr_running, ); #endif +// DEPRECATED: use lisa__sched_util_est_cfs_task or lisa__sched_util_est_cfs_tg instead #if HAS_KERNEL_FEATURE(SE_UTIL_EST) TRACE_EVENT(lisa__sched_util_est_se, @@ -240,11 +333,42 @@ TRACE_EVENT(lisa__sched_util_est_se, __field( int, pid ) __array( char, path, PATH_SIZE ) __array( char, comm, TASK_COMM_LEN ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + strlcpy(__entry->path, path, PATH_SIZE); + strlcpy(__entry->comm, comm, TASK_COMM_LEN); + __entry->pid = pid; + __entry->enqueued = avg->util_est.enqueued & ~UTIL_AVG_UNCHANGED; + __entry->ewma = avg->util_est.ewma; + __entry->util = avg->util_avg; + ), + + TP_printk("cpu=%d path=%s comm=%s pid=%d enqueued=%u ewma=%u util=%lu", + __entry->cpu, __entry->path, __entry->comm, __entry->pid, + __entry->enqueued, __entry->ewma, __entry->util) + ); +#endif + +#if HAS_KERNEL_FEATURE(SE_UTIL_EST) +TRACE_EVENT(lisa__sched_util_est_cfs_task, + + TP_PROTO(int cpu, int pid, const char *comm, const struct sched_avg *avg), + + TP_ARGS(cpu, pid, comm, avg), + + TP_STRUCT__entry( + __field( unsigned long, util ) + __field( unsigned int, enqueued ) + __field( unsigned int, ewma ) + __field( int, cpu ) + __field( int, pid ) + __array( char, comm, TASK_COMM_LEN ) ), TP_fast_assign( __entry->cpu = cpu; - strlcpy(__entry->path, path, PATH_SIZE); strlcpy(__entry->comm, comm, TASK_COMM_LEN); __entry->pid = pid; __entry->enqueued = avg->util_est.enqueued & ~UTIL_AVG_UNCHANGED; @@ -252,16 +376,46 @@ TRACE_EVENT(lisa__sched_util_est_se, __entry->util = avg->util_avg; ), - TP_printk("cpu=%d path=%s comm=%s pid=%d enqueued=%u ewma=%u util=%lu", - __entry->cpu, __entry->path, __entry->comm, __entry->pid, + TP_printk("cpu=%d comm=%s pid=%d enqueued=%u ewma=%u util=%lu", + __entry->cpu, __entry->comm, __entry->pid, + __entry->enqueued, __entry->ewma, __entry->util) + ); +#endif + +#if HAS_KERNEL_FEATURE(SE_UTIL_EST) +TRACE_EVENT(lisa__sched_util_est_cfs_tg, + + TP_PROTO(int cpu, const char *path, const struct sched_avg *avg), + + TP_ARGS(cpu, path, avg), + + TP_STRUCT__entry( + __field( unsigned long, util ) + __field( unsigned int, enqueued ) + __field( unsigned int, ewma ) + __field( int, cpu ) + __array( char, path, PATH_SIZE ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + strlcpy(__entry->path, path, PATH_SIZE); + __entry->enqueued = avg->util_est.enqueued & ~UTIL_AVG_UNCHANGED; + __entry->ewma = avg->util_est.ewma; + __entry->util = avg->util_avg; + ), + + TP_printk("cpu=%d path=%s enqueued=%u ewma=%u util=%lu", + __entry->cpu, __entry->path, __entry->enqueued, __entry->ewma, __entry->util) ); #endif #if HAS_KERNEL_FEATURE(CFS_UTIL_EST) +// DEPRECATED: Use lisa__sched_util_est_cfs_tg instead TRACE_EVENT(lisa__sched_util_est_cfs, - TP_PROTO(int cpu, char *path, const struct sched_avg *avg), + TP_PROTO(int cpu, const char *path, const struct sched_avg *avg), TP_ARGS(cpu, path, avg), @@ -288,19 +442,15 @@ TRACE_EVENT(lisa__sched_util_est_cfs, #endif #if HAS_KERNEL_FEATURE(SE_UCLAMP) -TRACE_EVENT_CONDITION(lisa__uclamp_util_se, +TRACE_EVENT(lisa__uclamp_cfs_task, - TP_PROTO(bool is_task, const struct task_struct *p, const struct rq *rq), + TP_PROTO(const struct task_struct *p, const struct rq* rq), - TP_ARGS(is_task, p, rq), - - TP_CONDITION(is_task), + TP_ARGS(p, rq), TP_STRUCT__entry( __field(unsigned long, util_avg ) __field(unsigned long, uclamp_avg ) - __field(unsigned long, uclamp_min ) - __field(unsigned long, uclamp_max ) __field( int, cpu ) __field( pid_t, pid ) __array( char, comm, TASK_COMM_LEN ) @@ -312,42 +462,23 @@ TRACE_EVENT_CONDITION(lisa__uclamp_util_se, __entry->cpu = rq ? rq_cpu(rq) : -1; __entry->util_avg = p->se.avg.util_avg; __entry->uclamp_avg = uclamp_rq_util_with(rq, p->se.avg.util_avg); + ), -# if HAS_KERNEL_FEATURE(RQ_UCLAMP) - __entry->uclamp_min = rq->uclamp[UCLAMP_MIN].value; - __entry->uclamp_max = rq->uclamp[UCLAMP_MAX].value; -# endif - ), - - TP_printk("pid=%d comm=%s cpu=%d util_avg=%lu uclamp_avg=%lu" -# if HAS_KERNEL_FEATURE(RQ_UCLAMP) - " uclamp_min=%lu uclamp_max=%lu" -# endif - , - __entry->pid, __entry->comm, __entry->cpu, - __entry->util_avg, __entry->uclamp_avg -# if HAS_KERNEL_FEATURE(RQ_UCLAMP) - ,__entry->uclamp_min, __entry->uclamp_max -# endif - ) + TP_printk("pid=%d comm=%s cpu=%d util_avg=%lu uclamp_avg=%lu", + __entry->pid, __entry->comm, __entry->cpu, + __entry->util_avg, __entry->uclamp_avg + ) ); -#else -#define trace_lisa__uclamp_util_se(is_task, p, rq) while(false) {} -#define trace_lisa__uclamp_util_se_enabled() (false) #endif #if HAS_KERNEL_FEATURE(RQ_UCLAMP) -TRACE_EVENT_CONDITION(lisa__uclamp_util_cfs, - - TP_PROTO(bool is_root, const struct rq *rq, const struct cfs_rq *cfs_rq), +TRACE_EVENT(lisa__uclamp_rq, - TP_ARGS(is_root, rq, cfs_rq), + TP_PROTO(const struct rq *rq), - TP_CONDITION(is_root), + TP_ARGS(rq), TP_STRUCT__entry( - __field(unsigned long, util_avg ) - __field(unsigned long, uclamp_avg ) __field(unsigned long, uclamp_min ) __field(unsigned long, uclamp_max ) __field( int, cpu ) @@ -355,20 +486,14 @@ TRACE_EVENT_CONDITION(lisa__uclamp_util_cfs, TP_fast_assign( __entry->cpu = rq ? rq_cpu(rq) : -1; - __entry->util_avg = cfs_rq->avg.util_avg; - __entry->uclamp_avg = uclamp_rq_util_with(rq, cfs_rq->avg.util_avg); __entry->uclamp_min = rq->uclamp[UCLAMP_MIN].value; __entry->uclamp_max = rq->uclamp[UCLAMP_MAX].value; ), - TP_printk("cpu=%d util_avg=%lu uclamp_avg=%lu " - "uclamp_min=%lu uclamp_max=%lu", - __entry->cpu, __entry->util_avg, __entry->uclamp_avg, - __entry->uclamp_min, __entry->uclamp_max) + TP_printk("cpu=%d uclamp_min=%lu uclamp_max=%lu", + __entry->cpu, __entry->uclamp_min, __entry->uclamp_max + ) ); -#else -#define trace_lisa__uclamp_util_cfs(is_root, cpu, cfs_rq) while(false) {} -#define trace_lisa__uclamp_util_cfs_enabled() (false) #endif #if HAS_KERNEL_FEATURE(RQ_CAPACITY) diff --git a/lisa/_assets/kmodules/lisa/sched_helpers.h b/lisa/_assets/kmodules/lisa/sched_helpers.h index 2e4cb771e9d4248cf422074077c9a28278432e6f..60098038c62402f8ead34eef9c16a94e83cbb73b 100644 --- a/lisa/_assets/kmodules/lisa/sched_helpers.h +++ b/lisa/_assets/kmodules/lisa/sched_helpers.h @@ -141,6 +141,11 @@ static inline const struct cfs_rq *get_se_cfs_rq(const struct sched_entity *se) #if HAS_TYPE(struct, cfs_rq) +static inline int cfs_rq_cpu(const struct cfs_rq *cfs_rq) +{ + return cpu_of(rq_of(cfs_rq)); +} + static inline const struct sched_avg *cfs_rq_avg(const struct cfs_rq *cfs_rq) { # if HAS_KERNEL_FEATURE(CFS_PELT) @@ -150,6 +155,36 @@ static inline const struct sched_avg *cfs_rq_avg(const struct cfs_rq *cfs_rq) # endif } + +# if HAS_MEMBER(struct, cfs_rq, rq) && HAS_MEMBER(struct, rq, cfs) +static inline bool cfs_rq_is_root(const struct cfs_rq *cfs_rq) +{ + return (&cfs_rq->rq->cfs == cfs_rq); +} +# endif + +static inline const struct sched_avg *cfs_tg_avg(const struct cfs_rq *cfs_rq) +{ +# if defined(CONFIG_FAIR_GROUP_SCHED) && HAS_MEMBER(struct, cfs_rq, tg) && HAS_MEMBER(struct, task_group, cfs_rq) && HAS_MEMBER(struct, task_group, se) + if (!cfs_rq || cfs_rq_is_root(cfs_rq)) { + // For the root cfs_rq, we simply do not have any associated + // taskgroup + return NULL; + } else { + const struct task_group *tg = cfs_rq->tg; + if (tg) { + int cpu = cfs_rq_cpu(cfs_rq); + + // Check the cfs_rq for that CPU on the task_group is + // the one we expect + BUG_ON(tg->cfs_rq[cpu] != cfs_rq); + return (const struct sched_avg *)&tg->se[cpu]->avg; + } + } +# endif + return NULL; +} + static inline char *cfs_rq_path(const struct cfs_rq *cfs_rq, char *str, int len) { if (!cfs_rq) { @@ -163,11 +198,6 @@ static inline char *cfs_rq_path(const struct cfs_rq *cfs_rq, char *str, int len) return str; } -static inline int cfs_rq_cpu(const struct cfs_rq *cfs_rq) -{ - return cpu_of(rq_of(cfs_rq)); -} - #endif #if HAS_TYPE(struct, rq) @@ -203,6 +233,14 @@ static inline int rq_cpu(const struct rq *rq) return cpu_of(rq); } + +static inline int se_cpu(const struct sched_entity *se) +{ + const struct cfs_rq *cfs_rq = get_se_cfs_rq(se); + int cpu = cfs_rq ? cfs_rq_cpu(cfs_rq) : -1; + return cpu; +} + static inline int rq_cpu_capacity(const struct rq *rq) { return diff --git a/lisa/_assets/kmodules/lisa/tp.c b/lisa/_assets/kmodules/lisa/tp.c index 9d369eee076054341330cc924cc81ed6aaa8068c..8d49b56d492e39603d3af6a19ba55c457f5866a6 100644 --- a/lisa/_assets/kmodules/lisa/tp.c +++ b/lisa/_assets/kmodules/lisa/tp.c @@ -12,8 +12,8 @@ #include "tp.h" #if HAS_KERNEL_FEATURE(CFS_PELT) -static inline void _trace_cfs(struct cfs_rq *cfs_rq, - void (*trace_event)(int, char*, +static inline void _deprecated_trace_cfs_rq(const struct cfs_rq *cfs_rq, + void (*trace_event)(int, const char*, const struct sched_avg*)) { if (cfs_rq) { @@ -27,41 +27,87 @@ static inline void _trace_cfs(struct cfs_rq *cfs_rq, } #endif +static inline void _deprecated_trace_se(const struct sched_entity *se, void (*trace_event)(int cpu, const char *path, const char *comm, int pid, const struct sched_avg *avg)) +{ + if (se) { + const char *path = "(null)"; + const char *comm = "(null)"; + int pid = -1; + int cpu = se_cpu(se); + + if (entity_is_task(se)) { + struct task_struct *p = container_of(se, struct task_struct, se); + comm = p->comm; + pid = p->pid; + } else { + const struct cfs_rq *gcfs_rq = get_group_cfs_rq(se); + char _path[PATH_SIZE]; + cfs_rq_path(gcfs_rq, _path, PATH_SIZE); + path = _path; + } + return trace_event(cpu, path, comm, pid, &se->avg); + } +} -#if HAS_KERNEL_FEATURE(SE_PELT) -static inline void _trace_se(struct sched_entity *se, - void (*trace_event)(int, const char*, const char*, int, - const struct sched_avg*)) +typedef void (*trace_cfs_task)(int cpu, int pid, const char* comm, const struct sched_avg *avg); +static inline void _trace_cfs_task(struct sched_entity *se, trace_cfs_task trace_task) { - const struct cfs_rq *gcfs_rq = get_group_cfs_rq(se); - const struct cfs_rq *cfs_rq = get_se_cfs_rq(se); - char path[PATH_SIZE]; + if (se && entity_is_task(se)) { + int cpu = se_cpu(se); + BUG_ON(cpu < 0); + const struct task_struct *p = container_of(se, struct task_struct, se); + return trace_task(cpu, p->pid, p->comm, &se->avg); + } +} - cfs_rq_path(gcfs_rq, path, PATH_SIZE); - int cpu = cfs_rq ? cfs_rq_cpu(cfs_rq) : -1; +typedef void (*trace_cfs_tg)(int cpu, const char* path, const struct sched_avg *avg); +static inline void _trace_cfs_tg(struct sched_entity *se, trace_cfs_tg trace_tg) +{ + if (se && !entity_is_task(se)) { + int cpu = se_cpu(se); - struct task_struct *p = gcfs_rq ? NULL : container_of(se, struct task_struct, se); - char *comm = p ? p->comm : "(null)"; - pid_t pid = p ? p->pid : -1; + const struct cfs_rq *gcfs_rq = get_group_cfs_rq(se); + char path[PATH_SIZE]; + cfs_rq_path(gcfs_rq, path, PATH_SIZE); - trace_event(cpu, path, comm, pid, &se->avg); + return trace_tg(cpu, path, &se->avg); + } +} + +static inline void _trace_cfs_tg_rq(const struct cfs_rq *cfs_rq, trace_cfs_tg trace_tg) +{ + if (cfs_rq) { + int cpu = cfs_rq_cpu(cfs_rq); + + char path[PATH_SIZE]; + cfs_rq_path(cfs_rq, path, PATH_SIZE); + const struct sched_avg *avg = cfs_tg_avg(cfs_rq); + + if (avg) + trace_tg(cpu, path, avg); + } } -#endif #if HAS_KERNEL_FEATURE(CFS_PELT) static void sched_pelt_cfs_probe(void *feature, struct cfs_rq *cfs_rq) { - _trace_cfs(cfs_rq, trace_lisa__sched_pelt_cfs); + _deprecated_trace_cfs_rq(cfs_rq, trace_lisa__sched_pelt_cfs); } -DEFINE_TP_EVENT_FEATURE(lisa__sched_pelt_cfs, TP_PROBES(TP_PROBE("pelt_cfs_tp", sched_pelt_cfs_probe))); +DEFINE_TP_DEPRECATED_EVENT_FEATURE("use lisa__sched_pelt_cfs_tg instead", lisa__sched_pelt_cfs, TP_PROBES(TP_PROBE("pelt_cfs_tp", sched_pelt_cfs_probe))); #endif #if HAS_KERNEL_FEATURE(RQ_UCLAMP) -static void uclamp_util_cfs_probe(void *feature, struct cfs_rq *cfs_rq) { - bool __maybe_unused is_root_rq = ((struct cfs_rq *)&rq_of(cfs_rq)->cfs == cfs_rq); - trace_lisa__uclamp_util_cfs(is_root_rq, rq_of(cfs_rq), cfs_rq); +static void uclamp_rq_probe(void *feature, struct cfs_rq *cfs_rq) { + if (cfs_rq && cfs_rq_is_root(cfs_rq)) { + const struct rq *rq = rq_of(cfs_rq); + trace_lisa__uclamp_rq(rq); + } } -DEFINE_TP_EVENT_FEATURE(lisa__uclamp_util_cfs, TP_PROBES(TP_PROBE("pelt_cfs_tp", uclamp_util_cfs_probe))); + +// TODO: what we really need is an enqueue/dequeue tracepoint, but we don't have +// any. So instead we attach to both the CFS and the RT tracepoints since +// currently uclamp is only available for CFS and RT tasks +DEFINE_TP_EVENT_FEATURE(lisa__uclamp_rq, TP_PROBES(TP_PROBE("pelt_cfs_tp", uclamp_rq_probe), TP_PROBE("pelt_rt_tp", uclamp_rq_probe))); #endif #if HAS_KERNEL_FEATURE(RT_PELT) @@ -113,30 +159,46 @@ DEFINE_TP_EVENT_FEATURE(lisa__sched_pelt_irq, TP_PROBES(TP_PROBE("pelt_irq_tp", #endif #if HAS_KERNEL_FEATURE(SE_PELT) -static void sched_pelt_se_probe(void *feature, struct sched_entity *se) +static void deprecated_sched_pelt_se_probe(void *feature, struct sched_entity *se) { - _trace_se(se, trace_lisa__sched_pelt_se); + _deprecated_trace_se(se, trace_lisa__sched_pelt_se); } -DEFINE_TP_EVENT_FEATURE(lisa__sched_pelt_se, TP_PROBES(TP_PROBE("pelt_se_tp", sched_pelt_se_probe))); +DEFINE_TP_DEPRECATED_EVENT_FEATURE("use lisa__sched_pelt_cfs_task for tasks and lisa__sched_pelt_cfs_tg for taskgroups", lisa__sched_pelt_se, TP_PROBES(TP_PROBE("pelt_se_tp", deprecated_sched_pelt_se_probe))); #endif -#if HAS_KERNEL_FEATURE(SE_UCLAMP) -static void uclamp_util_se_probe(void *feature, struct sched_entity *se) +#if HAS_KERNEL_FEATURE(SE_PELT) +static void sched_pelt_cfs_task_probe(void *feature, struct sched_entity *se) { + _trace_cfs_task(se, trace_lisa__sched_pelt_cfs_task); +} +static void sched_pelt_cfs_tg_probe(void *feature, struct sched_entity *se) +{ + _trace_cfs_tg(se, trace_lisa__sched_pelt_cfs_tg); +} +static void sched_pelt_cfs_tg_rq_probe(void *feature, const struct cfs_rq *cfs_rq) +{ + _trace_cfs_tg_rq(cfs_rq, trace_lisa__sched_pelt_cfs_tg); +} +DEFINE_TP_EVENT_FEATURE(lisa__sched_pelt_cfs_task, TP_PROBES(TP_PROBE("pelt_se_tp", sched_pelt_cfs_task_probe))); +DEFINE_TP_EVENT_FEATURE(lisa__sched_pelt_cfs_tg, TP_PROBES(TP_PROBE("pelt_se_tp", sched_pelt_cfs_tg_probe), TP_PROBE("pelt_cfs_tp", sched_pelt_cfs_tg_rq_probe))); +#endif - const struct cfs_rq __maybe_unused *cfs_rq = get_se_cfs_rq(se); - - trace_lisa__uclamp_util_se(entity_is_task(se), - container_of(se, struct task_struct, se), - rq_of(cfs_rq)); +#if HAS_KERNEL_FEATURE(SE_UCLAMP) +static void uclamp_cfs_task_probe(void *feature, struct sched_entity *se) +{ + if (se && entity_is_task(se)) + trace_lisa__uclamp_cfs_task( + container_of(se, struct task_struct, se), + rq_of(get_se_cfs_rq(se)) + ); } -DEFINE_TP_EVENT_FEATURE(lisa__uclamp_util_se, TP_PROBES(TP_PROBE("pelt_se_tp", uclamp_util_se_probe))); +DEFINE_TP_EVENT_FEATURE(lisa__uclamp_cfs_task, TP_PROBES(TP_PROBE("pelt_se_tp", uclamp_cfs_task_probe))); #endif #if HAS_KERNEL_FEATURE(SCHED_OVERUTILIZED) static void sched_overutilized_probe(void *feature, struct root_domain *rd, bool overutilized) { - if (trace_lisa__sched_overutilized_enabled()) { + if (rd && trace_lisa__sched_overutilized_enabled()) { char span[SPAN_SIZE]; cpumap_print_to_pagebuf(false, span, rd_span(rd)); @@ -163,17 +225,35 @@ DEFINE_TP_EVENT_FEATURE(lisa__sched_update_nr_running, TP_PROBES(TP_PROBE("sched #if HAS_KERNEL_FEATURE(CFS_UTIL_EST) static void sched_util_est_cfs_probe(void *feature, struct cfs_rq *cfs_rq) { - _trace_cfs(cfs_rq, trace_lisa__sched_util_est_cfs); + _deprecated_trace_cfs_rq(cfs_rq, trace_lisa__sched_util_est_cfs); +} +DEFINE_TP_DEPRECATED_EVENT_FEATURE("use lisa__sched_util_est_cfs_tg instead", lisa__sched_util_est_cfs, TP_PROBES(TP_PROBE("sched_util_est_cfs_tp", sched_util_est_cfs_probe))); +#endif + +#if HAS_KERNEL_FEATURE(SE_UTIL_EST) +static void deprecated_sched_util_est_se_probe(void *feature, struct sched_entity *se) +{ + _deprecated_trace_se(se, trace_lisa__sched_util_est_se); } -DEFINE_TP_EVENT_FEATURE(lisa__sched_util_est_cfs, TP_PROBES(TP_PROBE("sched_util_est_cfs_tp", sched_util_est_cfs_probe))); +DEFINE_TP_DEPRECATED_EVENT_FEATURE("use lisa__sched_util_est_cfs_task for tasks and lisa__sched_util_est_cfs_tg for taskgroups", lisa__sched_util_est_se, TP_PROBES(TP_PROBE("sched_util_est_se_tp", deprecated_sched_util_est_se_probe))); #endif #if HAS_KERNEL_FEATURE(SE_UTIL_EST) -static void sched_util_est_se_probe(void *feature, struct sched_entity *se) +static void sched_util_est_cfs_task_probe(void *feature, struct sched_entity *se) +{ + _trace_cfs_task(se, trace_lisa__sched_util_est_cfs_task); +} +static void sched_util_est_cfs_tg_probe(void *feature, struct sched_entity *se) +{ + _trace_cfs_tg(se, trace_lisa__sched_util_est_cfs_tg); +} +static void sched_util_est_cfs_tg_rq_probe(void *feature, const struct cfs_rq *cfs_rq) { - _trace_se(se, trace_lisa__sched_util_est_se); + _trace_cfs_tg_rq(cfs_rq, trace_lisa__sched_util_est_cfs_tg); } -DEFINE_TP_EVENT_FEATURE(lisa__sched_util_est_se, TP_PROBES(TP_PROBE("sched_util_est_se_tp", sched_util_est_se_probe))); +DEFINE_TP_EVENT_FEATURE(lisa__sched_util_est_cfs_task, TP_PROBES(TP_PROBE("sched_util_est_se_tp", sched_util_est_cfs_task_probe))); +// TODO: can we attach to pelt_cfs_tp and expect the util est value to be relevant there ? +DEFINE_TP_EVENT_FEATURE(lisa__sched_util_est_cfs_tg, TP_PROBES(TP_PROBE("sched_util_est_se_tp", sched_util_est_cfs_tg_probe), TP_PROBE("pelt_cfs_tp", sched_util_est_cfs_tg_rq_probe))); #endif #if HAS_KERNEL_FEATURE(RQ_CAPACITY) diff --git a/lisa/analysis/load_tracking.py b/lisa/analysis/load_tracking.py index da30674caa745b8c8932d9935932f3f25db1f258..d38338ce3524939fc773071d19068bf131c6b6e2 100644 --- a/lisa/analysis/load_tracking.py +++ b/lisa/analysis/load_tracking.py @@ -42,7 +42,8 @@ class LoadTrackingAnalysis(TraceAnalysisBase): name = 'load_tracking' - _SCHED_PELT_SE_NAMES = [ + _SCHED_PELT_TASK_NAMES = [ + 'sched_pelt_cfs_task', 'sched_pelt_se', 'sched_load_se', 'sched_load_avg_task' @@ -52,7 +53,8 @@ class LoadTrackingAnalysis(TraceAnalysisBase): kernel versions (Android, mainline etc) """ - _SCHED_PELT_CFS_NAMES = [ + _SCHED_PELT_TG_NAMES = [ + 'sched_pelt_cfs_tg', 'sched_pelt_cfs', 'sched_load_cfs_rq', 'sched_load_avg_cpu', @@ -62,6 +64,15 @@ class LoadTrackingAnalysis(TraceAnalysisBase): kernel versions (Android, mainline etc) """ + _SCHED_UTIL_EST_TASK_NAMES = [ + 'sched_util_est_cfs_task', + 'sched_util_est_se' + ] + """ + All the names that the per-task util est event ever had in various + kernel and LISA kernel module versions. + """ + @classmethod def _columns_renaming(cls, event): """ @@ -89,7 +100,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): """ The extra columns not shared between trace event versions """ - if event in [*cls._SCHED_PELT_CFS_NAMES, 'sched_load_se', 'sched_pelt_se']: + if event in [*cls._SCHED_PELT_TG_NAMES, 'sched_load_se', 'sched_pelt_se', 'sched_pelt_cfs_task']: return ['path', 'rbl_load', 'runnable'] if event in ['sched_load_avg_task']: @@ -104,11 +115,13 @@ class LoadTrackingAnalysis(TraceAnalysisBase): # Legacy sched_load_avg_* events don't have a `path` field. if not event.startswith('sched_load_avg_'): - if event in self._SCHED_PELT_SE_NAMES: - df = df[df.path == "(null)"] + # sched_pelt_cfs_task does not contain any taskgroup record so no + # need to filter anything + if event != 'sched_pelt_cfs_task' and event in self._SCHED_PELT_TASK_NAMES: + df = df[df['path'] == "(null)"] - if event in self._SCHED_PELT_CFS_NAMES: - df = df[df.path == "/"] + if event in self._SCHED_PELT_TG_NAMES: + df = df[df['path'] == "/"] to_drop = self._columns_to_drop(event) df.drop(columns=to_drop, inplace=True, errors='ignore') @@ -129,7 +142,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): ) @will_use_events_from( - requires_one_event_of(*_SCHED_PELT_CFS_NAMES), + requires_one_event_of(*_SCHED_PELT_TG_NAMES), 'sched_util_est_cfs', 'sched_cpu_capacity', ) @@ -155,7 +168,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): """ if signal in ('util', 'load'): - df = self._df_either_event(self._SCHED_PELT_CFS_NAMES) + df = self._df_either_event(self._SCHED_PELT_TG_NAMES) elif signal == 'enqueued': df = self._df_uniformized_signal('sched_util_est_cfs') elif signal == 'capacity': @@ -179,7 +192,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): return df @deprecate(replaced_by=df_cpus_signal, deprecated_in='2.0', removed_in='4.0') - @requires_one_event_of(*_SCHED_PELT_CFS_NAMES) + @requires_one_event_of(*_SCHED_PELT_TG_NAMES) def df_cpus_signals(self): """ Get the load-tracking signals for the CPUs @@ -189,12 +202,12 @@ class LoadTrackingAnalysis(TraceAnalysisBase): * A ``util`` column (the average utilization of a CPU at time t) * A ``load`` column (the average load of a CPU at time t) """ - return self._df_either_event(self._SCHED_PELT_CFS_NAMES) + return self._df_either_event(self._SCHED_PELT_TG_NAMES) @TraceAnalysisBase.cache @will_use_events_from( - requires_one_event_of(*_SCHED_PELT_SE_NAMES), - 'sched_util_est_se' + requires_one_event_of(*_SCHED_PELT_TASK_NAMES), + requires_one_event_of(*_SCHED_UTIL_EST_TASK_NAMES), ) def df_tasks_signal(self, signal): """ @@ -215,10 +228,10 @@ class LoadTrackingAnalysis(TraceAnalysisBase): :type signal: str """ if signal in ('util', 'load'): - df = self._df_either_event(self._SCHED_PELT_SE_NAMES) + df = self._df_either_event(self._SCHED_PELT_TASK_NAMES) elif signal in ('enqueued', 'ewma'): - df = self._df_uniformized_signal('sched_util_est_se') + df = self._df_either_event(self._SCHED_UTIL_EST_TASK_NAMES) elif signal == 'required_capacity': # Add a column which represents the max capacity of the smallest @@ -231,7 +244,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): return capacity return capacities[-1] - df = self._df_either_event(self._SCHED_PELT_SE_NAMES) + df = self._df_either_event(self._SCHED_PELT_TASK_NAMES) df['required_capacity'] = df['util'].map(fits_capacity) else: @@ -258,7 +271,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): return df_filter_task_ids(df, [task_id]) @deprecate(replaced_by=df_tasks_signal, deprecated_in='2.0', removed_in='4.0') - @requires_one_event_of(*_SCHED_PELT_SE_NAMES) + @requires_one_event_of(*_SCHED_PELT_TASK_NAMES) def df_tasks_signals(self): """ Get the load-tracking signals for the tasks @@ -273,7 +286,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): * A ``required_capacity`` column (the minimum available CPU capacity required to run this task without being CPU-bound) """ - df = self._df_either_event(self._SCHED_PELT_SE_NAMES) + df = self._df_either_event(self._SCHED_PELT_TASK_NAMES) if "orig" in self.trace.plat_info['cpu-capacities']: df['required_capacity'] = self.df_tasks_signal('required_capacity')['required_capacity'] @@ -301,7 +314,7 @@ class LoadTrackingAnalysis(TraceAnalysisBase): df = self.df_tasks_signal('util') # Compute number of samples above threshold - samples = df[df.util > util_threshold].groupby('pid', observed=True, sort=False, group_keys=False).count()["util"] + samples = df[df['util'] > util_threshold].groupby('pid', observed=True, sort=False, group_keys=False).count()["util"] samples = samples[samples > min_samples] samples = samples.sort_values(ascending=False) diff --git a/lisa/datautils.py b/lisa/datautils.py index 029196c9b2a3c959f65a382e7576286de3dfa16f..8c09a90712b5793d7b895ead74559343186f301a 100644 --- a/lisa/datautils.py +++ b/lisa/datautils.py @@ -2080,6 +2080,11 @@ _SIGNALS = [ SignalDesc('sched_load_se', ['comm', 'pid']), SignalDesc('sched_util_est_se', ['comm', 'pid']), + SignalDesc('sched_pelt_cfs_task', ['comm', 'pid']), + SignalDesc('sched_pelt_cfs_tg', ['path', 'cpu']), + SignalDesc('sched_util_est_cfs_task', ['comm', 'pid']), + SignalDesc('sched_util_est_cfs_tg', ['path']), + SignalDesc('sched_util_est_cfs', ['cpu']), SignalDesc('sched_pelt_cfs', ['path', 'cpu']), SignalDesc('sched_load_cfs_rq', ['path', 'cpu']), @@ -2090,6 +2095,9 @@ _SIGNALS = [ SignalDesc('uclamp_util_se', ['pid', 'comm']), SignalDesc('uclamp_util_cfs', ['cpu']), + SignalDesc('uclamp_rq', ['cpu']), + SignalDesc('uclamp_cfs_task', ['pid', 'comm']), + SignalDesc('sched_overutilized', []), SignalDesc('sched_process_wait', ['comm', 'pid']), diff --git a/lisa/trace.py b/lisa/trace.py index 967fa5fc0ac984dbffad2b77e95d33663394313f..947e9055406b5feaa3bc04d32cc9482c1d5d1cf7 100644 --- a/lisa/trace.py +++ b/lisa/trace.py @@ -1482,6 +1482,27 @@ class TxtTraceParser(TxtTraceParserBase): 'update_time': _KERNEL_DTYPE['timestamp'], }, ), + 'lisa__sched_pelt_cfs_task': dict( + fields={ + 'comm': _KERNEL_DTYPE['comm'], + 'cpu': _KERNEL_DTYPE['cpu'], + 'load': _KERNEL_DTYPE['util'], + 'pid': _KERNEL_DTYPE['signed_pid'], + 'rbl_load': _KERNEL_DTYPE['util'], + 'util': _KERNEL_DTYPE['util'], + 'update_time': _KERNEL_DTYPE['timestamp'], + }, + ), + 'lisa__sched_pelt_cfs_tg': dict( + fields={ + 'cpu': _KERNEL_DTYPE['cpu'], + 'load': _KERNEL_DTYPE['util'], + 'path': _KERNEL_DTYPE['cgroup_path'], + 'rbl_load': _KERNEL_DTYPE['util'], + 'util': _KERNEL_DTYPE['util'], + 'update_time': _KERNEL_DTYPE['timestamp'], + }, + ), 'sched_migrate_task': dict( fields={ 'comm': _KERNEL_DTYPE['comm'], @@ -1577,15 +1598,31 @@ class TxtTraceParser(TxtTraceParserBase): ), 'lisa__uclamp_util_se': dict( fields={ + 'pid': _KERNEL_DTYPE['pid'], 'comm': _KERNEL_DTYPE['comm'], 'cpu': _KERNEL_DTYPE['cpu'], - 'pid': _KERNEL_DTYPE['pid'], 'uclamp_avg': _KERNEL_DTYPE['util'], 'uclamp_max': _KERNEL_DTYPE['util'], 'uclamp_min': _KERNEL_DTYPE['util'], 'util_avg': _KERNEL_DTYPE['util'], }, ), + 'lisa__uclamp_cfs_task': dict( + fields={ + 'pid': _KERNEL_DTYPE['pid'], + 'comm': _KERNEL_DTYPE['comm'], + 'cpu': _KERNEL_DTYPE['cpu'], + 'uclamp_avg': _KERNEL_DTYPE['util'], + 'util_avg': _KERNEL_DTYPE['util'], + }, + ), + 'lisa__uclamp_rq': dict( + fields={ + 'cpu': _KERNEL_DTYPE['cpu'], + 'uclamp_max': _KERNEL_DTYPE['util'], + 'uclamp_min': _KERNEL_DTYPE['util'], + }, + ), } @PartialInit.factory