diff mbox

[v4,3/3] tracing: Histogram for missed timer offsets

Message ID 1472552924-22297-4-git-send-email-binoy.jayan@linaro.org
State New
Headers show

Commit Message

Binoy Jayan Aug. 30, 2016, 10:28 a.m. UTC
Latencies of missed timer offsets. Generate a histogram of missed
timer offsets in microseconds. This will be a based along with irq
and preemption latencies to calculate the effective process wakeup
latencies.

The following filter(s) may be used

'hist:key=common_pid.execname'
'hist:key=common_pid.execname:val=toffset,hitcount'

Signed-off-by: Binoy Jayan <binoy.jayan@linaro.org>

---
 include/linux/hrtimer.h        |  3 +++
 include/trace/events/latency.h | 29 +++++++++++++++++++++++++++++
 kernel/time/hrtimer.c          | 40 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)

-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

Comments

Masami Hiramatsu Aug. 30, 2016, 10:50 a.m. UTC | #1
Hi Binoy,

2016-08-30 19:28 GMT+09:00 Binoy Jayan <binoy.jayan@linaro.org>:
> Latencies of missed timer offsets. Generate a histogram of missed

> timer offsets in microseconds. This will be a based along with irq

> and preemption latencies to calculate the effective process wakeup

> latencies.

>

> The following filter(s) may be used

>

> 'hist:key=common_pid.execname'

> 'hist:key=common_pid.execname:val=toffset,hitcount'

>

> Signed-off-by: Binoy Jayan <binoy.jayan@linaro.org>

> ---

>  include/linux/hrtimer.h        |  3 +++

>  include/trace/events/latency.h | 29 +++++++++++++++++++++++++++++

>  kernel/time/hrtimer.c          | 40 ++++++++++++++++++++++++++++++++++++++++

>  3 files changed, 72 insertions(+)

>

> diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h

> index 5e00f80..e09de14 100644

> --- a/include/linux/hrtimer.h

> +++ b/include/linux/hrtimer.h

> @@ -104,6 +104,9 @@ struct hrtimer {

>         struct hrtimer_clock_base       *base;

>         u8                              state;

>         u8                              is_rel;

> +#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)

> +       ktime_t                         praecox;

> +#endif

>  #ifdef CONFIG_TIMER_STATS

>         int                             start_pid;

>         void                            *start_site;

> diff --git a/include/trace/events/latency.h b/include/trace/events/latency.h

> index e89be12..7fca7cd 100644

> --- a/include/trace/events/latency.h

> +++ b/include/trace/events/latency.h

> @@ -29,6 +29,35 @@ DEFINE_EVENT(latency_template, latency_preempt,

>             TP_PROTO(int ltype, cycles_t latency),

>             TP_ARGS(ltype, latency));

>

> +TRACE_EVENT(latency_hrtimer_interrupt,

> +

> +       TP_PROTO(long long toffset, struct task_struct *curr,

> +               struct task_struct *task),

> +

> +       TP_ARGS(toffset, curr, task),

> +

> +       TP_STRUCT__entry(

> +               __field(long long,      toffset)

> +               __array(char,           ccomm,  TASK_COMM_LEN)

> +               __field(int,            cprio)

> +               __array(char,           tcomm,  TASK_COMM_LEN)

> +               __field(int,            tprio)

> +       ),

> +

> +       TP_fast_assign(

> +               __entry->toffset = toffset;

> +               memcpy(__entry->ccomm, curr->comm, TASK_COMM_LEN);

> +               __entry->cprio  = curr->prio;

> +               memcpy(__entry->tcomm, task != NULL ? task->comm : "<none>",

> +                       task != NULL ? TASK_COMM_LEN : 7);

> +               __entry->tprio  = task != NULL ? task->prio : -1;

> +       ),

> +

> +       TP_printk("toffset=%lld curr=%s[%d] thread=%s[%d]",

> +               __entry->toffset, __entry->ccomm, __entry->cprio,

> +               __entry->tcomm, __entry->tprio)

> +);

> +

>  #endif /* _TRACE_HIST_H */

>

>  /* This part must be outside protection */

> diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c

> index 9ba7c82..f3e1d92 100644

> --- a/kernel/time/hrtimer.c

> +++ b/kernel/time/hrtimer.c

> @@ -53,9 +53,12 @@

>  #include <asm/uaccess.h>

>

>  #include <trace/events/timer.h>

> +#include <trace/events/latency.h>

>

>  #include "tick-internal.h"

>

> +static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer);

> +

>  /*

>   * The timer bases:

>   *

> @@ -960,6 +963,39 @@ static inline ktime_t hrtimer_update_lowres(struct hrtimer *timer, ktime_t tim,

>         return tim;

>  }

>

> +static inline void trace_latency_hrtimer_mark_ts(struct hrtimer *timer,

> +                                        struct hrtimer_clock_base *new_base,

> +                                        ktime_t tim)

> +{

> +#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)

> +       if (trace_latency_hrtimer_interrupt_enabled()) {


You would better use unlikely() here.

> +               ktime_t now = new_base->get_time();

> +

> +               if (ktime_to_ns(tim) < ktime_to_ns(now))


Wouldn't we need to consider the case of wrap around?

> +                       timer->praecox = now;

> +               else

> +                       timer->praecox = ktime_set(0, 0);

> +       }

> +#endif

> +}

> +

> +static inline void trace_latency_missed_hrtimer(struct hrtimer *timer,

> +                                               ktime_t basenow)

> +{

> +#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)

> +       if (trace_latency_hrtimer_interrupt_enabled())


Here, you'd better add unlikely() too.

> +               trace_latency_hrtimer_interrupt(

> +                       ktime_to_ns(ktime_sub(ktime_to_ns(timer->praecox) ?

> +                               timer->praecox : hrtimer_get_expires(timer),

> +                               basenow)),

> +                       current,

> +                       timer->function == hrtimer_wakeup ?

> +                               container_of(timer, struct hrtimer_sleeper,

> +                                       timer)->task : NULL);

> +#endif

> +

> +}

> +

>  /**

>   * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU

>   * @timer:     the timer to be added

> @@ -992,6 +1028,8 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,

>

>         timer_stats_hrtimer_set_start_info(timer);

>

> +       trace_latency_hrtimer_mark_ts(timer, new_base, tim);


Hmm, since these calls are not actual tracepoints, it should not start
with "trace_".
I would like to ask you to use other function name, like
hrtimer_mark_start_timestamp()

> +

>         leftmost = enqueue_hrtimer(timer, new_base);

>         if (!leftmost)

>                 goto unlock;

> @@ -1284,6 +1322,8 @@ static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)

>

>                         timer = container_of(node, struct hrtimer, node);

>

> +                       trace_latency_missed_hrtimer(timer, basenow);


Here, too. hrtimer_expired_latency() etc.

BTW, I think "missed" is a bit misleadable, since the timer itself is
not missed,
it may be just not in time. :)

Thank you,

> +

>                         /*

>                          * The immediate goal for using the softexpires is

>                          * minimizing wakeups, not running timers at the

> --

> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,

> a Linux Foundation Collaborative Project

>




-- 
Masami Hiramatsu
Binoy Jayan Aug. 31, 2016, 11:34 a.m. UTC | #2
On 30 August 2016 at 19:45, Steven Rostedt <rostedt@goodmis.org> wrote:
> On Tue, 30 Aug 2016 15:58:44 +0530

> Binoy Jayan <binoy.jayan@linaro.org> wrote:

>

>> +

>> +     TP_STRUCT__entry(

>> +             __field(long long,      toffset)

>> +             __array(char,           ccomm,  TASK_COMM_LEN)

>

> Can curr be different than current? If not, lets not record it.

>

> -- Steve

>


Hi Steve,

If my understanding is right, I think both are not the same. The
predefined field relates to the current
process which was interrupted by the hrtimer. This I guess does not
have a meaning in this context.
Mostly it is the idle process which is interrupted by the hrtimer. But
the ccomm field refers to the task
woken up by the process. The latencies are measured for this task. So
I it is needed.

-Binoy
Binoy Jayan Sept. 2, 2016, 12:41 p.m. UTC | #3
On 30 August 2016 at 16:20, Masami Hiramatsu
<masami.hiramatsu@linaro.org> wrote:
> Hi Binoy,

>>

>> +static inline void trace_latency_hrtimer_mark_ts(struct hrtimer *timer,

>> +                                        struct hrtimer_clock_base *new_base,

>> +                                        ktime_t tim)

>> +{

>> +#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)

>> +       if (trace_latency_hrtimer_interrupt_enabled()) {

>

> You would better use unlikely() here.

>

>> +               ktime_t now = new_base->get_time();

>> +

>> +               if (ktime_to_ns(tim) < ktime_to_ns(now))

>

> Wouldn't we need to consider the case of wrap around?

>

>> +                       timer->praecox = now;

>> +               else

>> +                       timer->praecox = ktime_set(0, 0);

>> +       }

>> +#endif

>> +}


Hi Masami,

I always see these values to be relative and not absolute time. I
found 'praecox' to be always zero during test.
What do you think.

Binoy
Masami Hiramatsu Sept. 2, 2016, 3:54 p.m. UTC | #4
2016-09-02 21:41 GMT+09:00 Binoy Jayan <binoy.jayan@linaro.org>:
> On 30 August 2016 at 16:20, Masami Hiramatsu

> <masami.hiramatsu@linaro.org> wrote:

>> Hi Binoy,

>>>

>>> +static inline void trace_latency_hrtimer_mark_ts(struct hrtimer *timer,

>>> +                                        struct hrtimer_clock_base *new_base,

>>> +                                        ktime_t tim)

>>> +{

>>> +#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)

>>> +       if (trace_latency_hrtimer_interrupt_enabled()) {

>>

>> You would better use unlikely() here.

>>

>>> +               ktime_t now = new_base->get_time();

>>> +

>>> +               if (ktime_to_ns(tim) < ktime_to_ns(now))

>>

>> Wouldn't we need to consider the case of wrap around?

>>

>>> +                       timer->praecox = now;

>>> +               else

>>> +                       timer->praecox = ktime_set(0, 0);

>>> +       }

>>> +#endif

>>> +}

>

> Hi Masami,

>

> I always see these values to be relative and not absolute time. I

> found 'praecox' to be always zero during test.

> What do you think.


Ah, right. Since "tim" is expire time (timer target), that should
always be "now + x"
(x is enough larger than how long setting the hrtimer takes). Or,
hrtimer expires
before finished to set. :)

Thank you,
-- 
Masami Hiramatsu
diff mbox

Patch

diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 5e00f80..e09de14 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -104,6 +104,9 @@  struct hrtimer {
 	struct hrtimer_clock_base	*base;
 	u8				state;
 	u8				is_rel;
+#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)
+	ktime_t				praecox;
+#endif
 #ifdef CONFIG_TIMER_STATS
 	int				start_pid;
 	void				*start_site;
diff --git a/include/trace/events/latency.h b/include/trace/events/latency.h
index e89be12..7fca7cd 100644
--- a/include/trace/events/latency.h
+++ b/include/trace/events/latency.h
@@ -29,6 +29,35 @@  DEFINE_EVENT(latency_template, latency_preempt,
 	    TP_PROTO(int ltype, cycles_t latency),
 	    TP_ARGS(ltype, latency));
 
+TRACE_EVENT(latency_hrtimer_interrupt,
+
+	TP_PROTO(long long toffset, struct task_struct *curr,
+		struct task_struct *task),
+
+	TP_ARGS(toffset, curr, task),
+
+	TP_STRUCT__entry(
+		__field(long long,	toffset)
+		__array(char,		ccomm,	TASK_COMM_LEN)
+		__field(int,		cprio)
+		__array(char,		tcomm,	TASK_COMM_LEN)
+		__field(int,		tprio)
+	),
+
+	TP_fast_assign(
+		__entry->toffset = toffset;
+		memcpy(__entry->ccomm, curr->comm, TASK_COMM_LEN);
+		__entry->cprio  = curr->prio;
+		memcpy(__entry->tcomm, task != NULL ? task->comm : "<none>",
+			task != NULL ? TASK_COMM_LEN : 7);
+		__entry->tprio  = task != NULL ? task->prio : -1;
+	),
+
+	TP_printk("toffset=%lld curr=%s[%d] thread=%s[%d]",
+		__entry->toffset, __entry->ccomm, __entry->cprio,
+		__entry->tcomm, __entry->tprio)
+);
+
 #endif /* _TRACE_HIST_H */
 
 /* This part must be outside protection */
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 9ba7c82..f3e1d92 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -53,9 +53,12 @@ 
 #include <asm/uaccess.h>
 
 #include <trace/events/timer.h>
+#include <trace/events/latency.h>
 
 #include "tick-internal.h"
 
+static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer);
+
 /*
  * The timer bases:
  *
@@ -960,6 +963,39 @@  static inline ktime_t hrtimer_update_lowres(struct hrtimer *timer, ktime_t tim,
 	return tim;
 }
 
+static inline void trace_latency_hrtimer_mark_ts(struct hrtimer *timer,
+					 struct hrtimer_clock_base *new_base,
+					 ktime_t tim)
+{
+#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)
+	if (trace_latency_hrtimer_interrupt_enabled()) {
+		ktime_t now = new_base->get_time();
+
+		if (ktime_to_ns(tim) < ktime_to_ns(now))
+			timer->praecox = now;
+		else
+			timer->praecox = ktime_set(0, 0);
+	}
+#endif
+}
+
+static inline void trace_latency_missed_hrtimer(struct hrtimer *timer,
+						ktime_t basenow)
+{
+#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)
+	if (trace_latency_hrtimer_interrupt_enabled())
+		trace_latency_hrtimer_interrupt(
+			ktime_to_ns(ktime_sub(ktime_to_ns(timer->praecox) ?
+				timer->praecox : hrtimer_get_expires(timer),
+				basenow)),
+			current,
+			timer->function == hrtimer_wakeup ?
+				container_of(timer, struct hrtimer_sleeper,
+					timer)->task : NULL);
+#endif
+
+}
+
 /**
  * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU
  * @timer:	the timer to be added
@@ -992,6 +1028,8 @@  void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
 
 	timer_stats_hrtimer_set_start_info(timer);
 
+	trace_latency_hrtimer_mark_ts(timer, new_base, tim);
+
 	leftmost = enqueue_hrtimer(timer, new_base);
 	if (!leftmost)
 		goto unlock;
@@ -1284,6 +1322,8 @@  static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
 
 			timer = container_of(node, struct hrtimer, node);
 
+			trace_latency_missed_hrtimer(timer, basenow);
+
 			/*
 			 * The immediate goal for using the softexpires is
 			 * minimizing wakeups, not running timers at the