ftrace: add tracepoint for timer
Subsystems (2)
PATCH v4 1/3 ftrace: add tracepoint for timer
This patch is modify from Mathieu's patch base on Ingo's suggestion, the original patch can be found here:
http://marc.info/?l=linux-kernel&m=123791201816247&w=2
Great thanks to Thomas for giving me so many valuable advices.
Those tracepoints are wanted and useful:
1: We can detect a timer's delay
2: We can monitor the lifecycle and behaviors of a timer
Thus they help in analysing and debuging.
Example ftrace output:
insmod-1712 [000] 145.901459: timer_init: timer=d0ace4b4 insmod-1712 [000] 145.901469: timer_start: timer=d0ace4b4 func=timer_fun expires=4294813629 timeout=199 <idle>-0 [000] 146.100230: timer_cancel: timer=d0ace4b4 <idle>-0 [000] 146.100233: timer_expire: timer=d0ace4b4 jiffies=4294813629 <idle>-0 [000] 146.100627: timer_callback_done: func=d0ace4b4
We expect the timer expires at 4294813629, after 199 jiffies, actually the timer expires at 4294813629, so it is delayed by 0 jiffies.
We also realize the timer's callback started at 146.100233, and it finished at 146.100627, so it's taking 146.100627-146.100233=0.4ms.
Changelog:v1->v2:
1: Add jiffies info into timer
2: Combine debugobjects and trace as Thomas's suggestion v2->v3:
1: Remove function address from timer_expire and timer_cancel
as Thomas's suggestion
2: Remove debug_and_trace_timer_expire() as Thomas's suggestion 3: Rename trace_timer_entry() and trace_timer_exit() to match this patch v3->v4:
1: Rename debug_and_trace_*() to debug_*() as Peter Zijlstra's suggestion 2: Integrate Anton Blanchard's patch, which can been found at:
http://marc.info/?l=linux-kernel&m=124331396919301&w=2
--- include/trace/events/timer.h | 134 ++++++++++++++++++++++++++++++++++++++++++ kernel/timer.c | 32 +++++++++- 2 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 include/trace/events/timer.h
include/trace/events/timer.h new file +134 -0
@@ -0,0 +1,134 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM timer + +#if !defined(_TRACE_TIMER_H) || defined(TRACE_HEADER_MULTI_READ ) +#define _TRACE_TIMER_H + +#include <linux/tracepoint.h> +#include <linux/timer.h> + +/** +* timer_init - called when the timer is initialized +* @timer: pointer to struct timer_list +**/ +TRACE_EVENT (timer_init, + + TP_PROTO(struct timer_list *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field( void *, timer ) + ), + +TP_fast_assign ( + __entry->timer = timer; + ), + +TP_printk ("timer=%p" , __entry->timer) +); + +/** +* timer_start - called when the timer is started +* @timer: pointer to struct timer_list +* @expires: the timer's expire time +**/ +TRACE_EVENT (timer_start, + + TP_PROTO(struct timer_list *timer, unsigned long expires), + +TP_ARGS (timer, expires), + +TP_STRUCT__entry ( + __field( void *, timer ) + __field( void *, function ) + __field( unsigned long, expires ) +__field ( int, timeout ) + ), + +TP_fast_assign ( + __entry->timer = timer; + __entry->function = timer->function; + __entry->expires = expires; + __entry->timeout = timer->expires - jiffies; + ), + +TP_printk ("timer=%p func=%pf expires=%lu timeout=%d" , __entry->timer, + __entry->function, __entry->expires, __entry->timeout) +); + +/** +* timer_expire - called immediately before the timer callback +* @timer: pointer to struct timer_list +* +* When used in combination with the timer_callback_done tracepoint we can +* determine the timer latency. +**/ +TRACE_EVENT (timer_expire, + + TP_PROTO(struct timer_list *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field( void *, timer ) + __field( unsigned long, jiffies ) + ), + +TP_fast_assign ( + __entry->timer = timer; + __entry->jiffies = jiffies; + ), + +TP_printk ("timer=%p jiffies=%lu" , __entry->timer, __entry->jiffies) +); + +/** +* timer_callback_done - called immediately after the timer callback returns +* @timer: pointer to struct timer_list +* +* When used in combination with the timer_expire tracepoint we can +* determine the timer latency. +**/ +TRACE_EVENT (timer_callback_done, + + TP_PROTO(struct timer_list *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field(void *, timer ) + ), + +TP_fast_assign ( + __entry->timer = timer; + ), + +TP_printk ("func=%p" , __entry->timer) +); + +/** +* timer_cancel - called when the timer is canceled +* @timer: pointer to struct timer_list +**/ +TRACE_EVENT (timer_cancel, + + TP_PROTO(struct timer_list *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field( void *, timer ) + ), + +TP_fast_assign ( + __entry->timer = timer; + ), + +TP_printk ("timer=%p" , __entry->timer) +); + +#endif /* _TRACE_TIMER_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h>
kernel/timer.c +28 -4
@@ -46,6 +46,9 @@#include <asm/timex.h> #include <asm/io.h> +#define CREATE_TRACE_POINTS +#include <trace/events/timer.h> + u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;EXPORT_SYMBOL (jiffies_64);
@@ -520,6 +523,25 @@ static inline void debug_timer_activate(struct timer_list *timer) { }static inline void debug_timer_deactivate (struct timer_list *timer) { }#endif +static inline void debug_init (struct timer_list *timer) +{ +debug_timer_init (timer); +trace_timer_init (timer); +} + +static inline void +debug_activate (struct timer_list *timer,unsigned long expires) +{ +debug_timer_activate (timer); +trace_timer_start (timer, expires); +} + +static inline void debug_deactivate (struct timer_list *timer) +{ +debug_timer_deactivate (timer); +trace_timer_cancel (timer); +} +static void __init_timer (struct timer_list *timer,const char *name,struct lock_class_key *key)
@@ -548,7 +570,7 @@ void init_timer_key(struct timer_list *timer,const char *name, struct lock_class_key *key) { -debug_timer_init (timer ); +debug_init (timer );__init_timer (timer, name, key); }EXPORT_SYMBOL (init_timer_key);
@@ -567,7 +589,7 @@ static inline void detach_timer(struct timer_list *timer, {struct list_head *entry = &timer->entry; -debug_timer_deactivate (timer); +debug_deactivate (timer);__list_del (entry->prev, entry->next);if (clear_pending)
@@ -628,7 +650,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, goto out_unlock; } -debug_timer_activate (timer); +debug_activate (timer, expires); new_base =__get_cpu_var (tvec_bases);
@@ -780,7 +802,7 @@ void add_timer_on(struct timer_list *timer, int cpu)BUG_ON (timer_pending (timer) || !timer->function);spin_lock_irqsave (&base->lock, flags);timer_set_base (timer, base); -debug_timer_activate (timer); +debug_activate (timer, timer->expires);internal_add_timer (base, timer);/* * Check whether the other CPU is idle and needs to be
@@ -984,7 +1006,9 @@ static inline void __run_timers(struct tvec_base *base)*/ lock_map_acquire (&lockdep_map); +trace_timer_expire (timer);fn (data); +trace_timer_callback_done (timer);lock_map_release (&lockdep_map);
Signature · 1 line
1.6.1.2
PATCH v4 2/3 ftrace: add tracepoint for hrtimer
Those tracepoints are wanted and useful:
1: We can detect a hrtimer's delay
2: We can monitor the lifecycle and behaviors of a hrtimer
Thus they help in analysing and debuging.
Great thanks to Thomas for giving me so many valuable advices.
Example ftrace output:
insmod-1824 [001] 606.749538: hrtimer_init: timer=d0ae04c0 clockid=CLOCK_REALTIME mode=HRTIMER_MODE_ABS insmod-1824 [001] 606.749614: hrtimer_start: timer=d0ae04c0 func=hrtimer_fun expires=1246707447252299435ns softexpires=1246707447252299435ns <idle>-0 [001] 606.754372: hrtimer_cancel: timer=d0ae04c0 <idle>-0 [001] 606.754374: hrtimer_expire: timer=d0ae04c0 now=1246707447257141331ns <idle>-0 [001] 606.754430: hrtimer_callback_done: timer=d0ae04c0
We expect the hrtimer expires at 1246707447252299435ns, actually the hrtimer expires at 1246707447257141331ns, so it is delayed by 1246707447257141331-1246707447252299435=4841896ns.
We also realize the hrtimer's callback started at 606.754374, and it finished at 606.754430, so it's taking 606.754430-606.754374=0.05ms.
Changelog:v1->v2:
1: Remove ktime_to_ns() in TP_fast_assign()
2: Combine debugobjects and trace as Thomas's suggestionv2->v3:
1: Remove function address from hrtimer_expire and hrtimer_cancel
as Thomas's suggestion
2: Remove debug_and_trace_hrtimer_expire() as Thomas's suggestion 3: Rename trace_hrtimer_entry() and trace_hrtimer_exit() to match this patch v3->v4:
1: Rename debug_and_trace_*() to debug_*() as Peter Zijlstra's suggestion 2: Move trace_hrtimer_expire() to __run_hrtimer() in order to calculate
exacts timer latency as Peter Zijlstra's suggestion
3: Integrate Anton Blanchard's patch, which can been found at:
http://marc.info/?l=linux-kernel&m=124331396919301&w=2
--- include/trace/events/timer.h | 133 ++++++++++++++++++++++++++++++++++++++++++ kernel/hrtimer.c | 34 +++++++++-- 2 files changed, 162 insertions(+), 5 deletions(-)
include/trace/events/timer.h +133 -0
@@ -128,6 +128,139 @@ TRACE_EVENT(timer_cancel,TP_printk ("timer=%p" , __entry->timer) ); +/** +* hrtimer_init - called when the hrtimer is initialized +* @timer: pointer to struct hrtimer +* @clockid: the hrtimer's clock +* @mode: the hrtimer's mode +**/ +TRACE_EVENT (hrtimer_init, + + TP_PROTO(struct hrtimer *timer, clockid_t clockid, enum hrtimer_mode mode), + +TP_ARGS (timer, clockid, mode), + +TP_STRUCT__entry ( + __field( void *, timer ) + __field( clockid_t, clockid ) + __field( enum hrtimer_mode, mode ) + ), + +TP_fast_assign ( + __entry->timer = timer; + __entry->clockid = clockid; + __entry->mode = mode; + ), + +TP_printk ("timer=%p clockid=%s mode=%s" , __entry->timer, + __entry->clockid ==CLOCK_REALTIME ? +"CLOCK_REALTIME" :"CLOCK_MONOTONIC" , + __entry->mode ==HRTIMER_MODE_ABS ? +"HRTIMER_MODE_ABS" :"HRTIMER_MODE_REL" ) +); + +/** +* hrtimer_start - called when the hrtimer is started +* @timer: pointer to struct hrtimer +**/ +TRACE_EVENT (hrtimer_start, + + TP_PROTO(struct hrtimer *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field( void *, timer ) + __field( void *, function ) + __field( s64, expires ) +__field ( s64, softexpires ) + ), + +TP_fast_assign ( + __entry->timer = timer; + __entry->function = timer->function; + __entry->expires = hrtimer_get_expires(timer).tv64; + __entry->softexpires = hrtimer_get_softexpires(timer).tv64; + ), + +TP_printk ("timer=%p func=%pf expires=%lluns softexpires=%lluns" , + __entry->timer, __entry->function, + (unsigned long long )__entry->expires, + (unsigned long long )__entry->softexpires) +); + +/** +* htimmer_expire - called immediately before the hrtimer callback +* @timer: pointer to struct hrtimer +* +* When used in combination with the hrtimer_callback_done tracepoint we can +* determine the high res timer latency. +**/ +TRACE_EVENT (hrtimer_expire, + + TP_PROTO(struct hrtimer *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field( void *, timer ) +__field ( s64, now ) + ), + +TP_fast_assign ( + __entry->timer = timer; + __entry->now = timer->base->get_time().tv64; + ), + +TP_printk ("timer=%p now=%lluns" , __entry->timer, + (unsigned long long )__entry->now) + ); + +/** +* hrtimer_callback_done - called immediately after the hrtimer callback returns +* @timer: pointer to struct hrtimer +* +* When used in combination with the hrtimer_expire tracepoint we can +* determine the high res timer latency. +**/ +TRACE_EVENT (hrtimer_callback_done, + + TP_PROTO(struct hrtimer *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field( void *, timer ) + ), + +TP_fast_assign ( + __entry->timer = timer; + ), + +TP_printk ("timer=%p" , __entry->timer) +); + +/** +* hrtimer_cancel - called when the hrtimer is canceled +* @timer: pointer to struct hrtimer +**/ +TRACE_EVENT (hrtimer_cancel, + + TP_PROTO(struct hrtimer *timer), + +TP_ARGS (timer), + +TP_STRUCT__entry ( + __field( void *, timer ) + ), + +TP_fast_assign ( + __entry->timer = timer; + ), + +TP_printk ("timer=%p" , __entry->timer) +); +#endif /* _TRACE_TIMER_H */ /* This part must be outside protection */
kernel/hrtimer.c +29 -5
@@ -48,6 +48,8 @@#include <asm/uaccess.h> +#include <trace/events/timer.h> +/* * The timer bases: *
@@ -441,6 +443,26 @@ static inline void debug_hrtimer_activate(struct hrtimer *timer) { }static inline void debug_hrtimer_deactivate (struct hrtimer *timer) { }#endif +static inline void +debug_init (struct hrtimer *timer,clockid_t clockid, +enum hrtimer_mode mode) +{ +debug_hrtimer_init (timer); +trace_hrtimer_init (timer, clockid, mode); +} + +static inline void debug_activate (struct hrtimer *timer) +{ +debug_hrtimer_activate (timer); +trace_hrtimer_start (timer); +} + +static inline void debug_deactivate (struct hrtimer *timer) +{ +debug_hrtimer_deactivate (timer); +trace_hrtimer_cancel (timer); +} +/* High resolution timer related functions */ #ifdef CONFIG_HIGH_RES_TIMERS
@@ -797,7 +819,7 @@ static int enqueue_hrtimer(struct hrtimer *timer,struct hrtimer *entry;int leftmost =1 ; -debug_hrtimer_activate (timer); +debug_activate (timer);/* * Find the right place in the rbtree:
@@ -883,7 +905,7 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base)* reprogramming happens in the interrupt handler. This is a * rare case and less expensive than a smp call. */ -debug_hrtimer_deactivate (timer); +debug_deactivate (timer);timer_stats_hrtimer_clear_start_info (timer); reprogram = base->cpu_base == &__get_cpu_var (hrtimer_bases); __remove_hrtimer(timer, base, HRTIMER_STATE_INACTIVE,
@@ -1116,7 +1138,7 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,void hrtimer_init (struct hrtimer *timer,clockid_t clock_id,enum hrtimer_mode mode) { -debug_hrtimer_init (timer); +debug_init (timer, clock_id, mode);__hrtimer_init (timer, clock_id, mode); }EXPORT_SYMBOL_GPL (hrtimer_init);
@@ -1149,7 +1171,7 @@ static void __run_hrtimer(struct hrtimer *timer)WARN_ON (!irqs_disabled ()); -debug_hrtimer_deactivate (timer); +debug_deactivate (timer);__remove_hrtimer (timer, base,HRTIMER_STATE_CALLBACK ,0 );timer_stats_account_hrtimer (timer); fn = timer->function;
@@ -1160,7 +1182,9 @@ static void __run_hrtimer(struct hrtimer *timer)* the timer base. */ spin_unlock (&cpu_base->lock); +trace_hrtimer_expire (timer); restart =fn (timer); +trace_hrtimer_callback_done (timer);spin_lock (&cpu_base->lock);/*
@@ -1569,7 +1593,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,while ((node =rb_first (&old_base->active))) { timer = rb_entry(node, struct hrtimer, node);BUG_ON (hrtimer_callback_running (timer)); -debug_hrtimer_deactivate (timer); +debug_deactivate (timer);/* * Mark it as STATE_MIGRATE not INACTIVE otherwise the
Signature · 1 line
1.6.1.2
PATCH v4 3/3 ftrace: add tracepoint for itimer
In reply to Xiao GuangrongThose tracepoints are wanted and useful:
1: We can detect a itimer's delay
2: We can monitor the lifecycle and behaviors of a itimer
Thus they help in analysing and debuging.
Great thanks to Thomas for giving me so many valuable advices.
There have two case:
case 1, if we use ITIMER_REAL itimer:
Example ftrace output:
main-1849 [001] 889.355307: hrtimer_start: timer=cf59fc44 func=it_real_fn expires=891440459185ns softexpires=891440459185ns main-1849 [001] 889.355312: itimer_state: which=0 expires=0 it_value=0.500 it_interval=0.0 main-1849 [001] 889.358327: hrtimer_cancel: timer=cf59fc44 main-1849 [001] 889.358327: hrtimer_expire: timer=cf59fc44 now=891442992881ns main-1849 [001] 889.358400: itimer_expire: pid=1849 which=0 now=0 main-1849 [001] 889.358473: hrtimer_callback_done: timer=cf59fc44 main-1849 [001] 889.358792: itimer_state: which=0 expires=0 it_value=0.0 it_interval=0.0
ITIMER_REAL itime use hrtimer to calculate time, so we can cooperate with hrtimer's tracepoint to get it's delay.
from this output information, the process set up a ITIMER_REAL iteimr(which=0), use a hrtime which address is cf59fc44 , we can get the delay is 891442992881-891440459185=2533696ns
case 2, if we use ITIMER_VIRTUAL/ITIMER_PROF itimer:
Example ftrace output:
main-5409 [001] 12763.874835: itimer_state: which=1 expires=2 it_value=0.500 it_interval=0.500 main-5409 [001] 12763.883495: itimer_expire: pid=5409 which=1 now=2 main-5409 [001] 12763.883712: itimer_state: which=1 expires=0 it_value=0.0 it_interval=0.0
from this, we can know the itimer delay is 2-2=0 jiffies.
Changelog:v1->v2:
Remove ktime_to_ns() in TP_fast_assign()v2->v3:
Below changes are all base on Thomas's suggestion:
1: Remove ITIMER_REAL's timer address and 'state' from trace_itimer_state() 2: Don't save task's name in trace_itimer_state()
3: Save task's pid instead of task's name in trace_itimer_expire()
--- include/trace/events/timer.h | 52 ++++++++++++++++++++++++++++++++++++++++++ kernel/itimer.c | 5 +++- kernel/posix-cpu-timers.c | 3 ++ 3 files changed, 59 insertions(+), 1 deletions(-)
include/trace/events/timer.h +52 -0
@@ -261,6 +261,58 @@ TRACE_EVENT(hrtimer_cancel,TP_printk ("timer=%p" , __entry->timer) ); +TRACE_EVENT (itimer_state, + + TP_PROTO(int which, struct itimerval *value, cputime_t expires), + +TP_ARGS (which, value, expires), + +TP_STRUCT__entry ( + __field( int, which ) + __field( cputime_t, expires ) + __field( long, value_sec ) + __field( long, value_usec ) + __field( long, interval_sec ) +__field ( long, interval_usec ) + ), + +TP_fast_assign ( + __entry->which = which; + __entry->expires = expires; + __entry->value_sec = value->it_value.tv_sec; + __entry->value_usec = value->it_value.tv_usec; + __entry->interval_sec = value->it_interval.tv_sec; + __entry->interval_usec = value->it_interval.tv_usec; + ), + +TP_printk ("which=%d expires=%lu it_value=%lu.%lu it_interval=%lu.%lu" , + __entry->which, __entry->expires, + __entry->value_sec, __entry->value_usec, + __entry->interval_sec, __entry->interval_usec) +); + +TRACE_EVENT (itimer_expire, + + TP_PROTO(int which, struct pid *pid, cputime_t now), + +TP_ARGS (which, pid, now), + +TP_STRUCT__entry ( + __field( int , which ) + __field( int, pid ) +__field ( cputime_t, now ) + ), + +TP_fast_assign ( + __entry->which = which; + __entry->now = now; + __entry->pid = pid_vnr(pid); + ), + +TP_printk ("pid=%d which=%d now=%lu" , __entry->pid, __entry->which, + __entry->now) +); +#endif /* _TRACE_TIMER_H */ /* This part must be outside protection */
kernel/itimer.c +4 -1
@@ -12,6 +12,7 @@#include <linux/time.h> #include <linux/posix-timers.h> #include <linux/hrtimer.h> +#include <trace/events/timer.h> #include <asm/uaccess.h>
@@ -123,6 +124,7 @@ enum hrtimer_restart it_real_fn(struct hrtimer *timer)struct signal_struct *sig = container_of(timer, struct signal_struct, real_timer); +trace_itimer_expire (ITIMER_REAL , sig->leader_pid,0 );kill_pid_info (SIGALRM ,SEND_SIG_PRIV , sig->leader_pid);return HRTIMER_NORESTART ;
@@ -139,7 +141,7 @@ int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)struct task_struct *tsk = current;struct hrtimer *timer;ktime_t expires; -cputime_t cval, cinterval, nval, ninterval; +cputime_t cval, cinterval, nval =0 , ninterval;/* * Validate the timevals in value.
@@ -220,6 +222,7 @@ again:default :return -EINVAL ; } +trace_itimer_state (which, value, nval);return 0 ; }
kernel/posix-cpu-timers.c +3 -0
@@ -8,6 +8,7 @@#include <linux/math64.h> #include <asm/uaccess.h> #include <linux/kernel_stat.h> +#include <trace/events/timer.h> /* * Called after updating RLIMIT_CPU to set timer expiration if necessary.
@@ -1160,6 +1161,7 @@ static void check_process_timers(struct task_struct *tsk, sig->it_prof_expires =cputime_add ( sig->it_prof_expires, ptime); } +trace_itimer_expire (ITIMER_PROF , sig->leader_pid, ptime);__group_send_sig_info (SIGPROF ,SEND_SIG_PRIV , tsk); } if (!cputime_eq(sig->it_prof_expires, cputime_zero) &&
@@ -1176,6 +1178,7 @@ static void check_process_timers(struct task_struct *tsk, sig->it_virt_expires =cputime_add ( sig->it_virt_expires, utime); } +trace_itimer_expire (ITIMER_VIRTUAL , sig->leader_pid, utime);__group_send_sig_info (SIGVTALRM ,SEND_SIG_PRIV , tsk); } if (!cputime_eq(sig->it_virt_expires, cputime_zero) &&
Signature · 1 line
1.6.1.2