diff mbox series

[v2] rtc: Add tracepoints for RTC system

Message ID 64a38f4a15a2f8dae4a9fd0a3fc2a312e86c733d.1510749847.git.baolin.wang@linaro.org
State Superseded
Headers show
Series [v2] rtc: Add tracepoints for RTC system | expand

Commit Message

(Exiting) Baolin Wang Nov. 16, 2017, 5:59 a.m. UTC
It will be more helpful to add some tracepoints to track RTC actions when
debugging RTC driver. Below sample is that we set/read the RTC time, then
set 2 alarms, so we can see the trace logs:

set/read RTC time:
kworker/0:1-67 [000] 21.814245: rtc_set_time: 2017-11-10 08:13:00 UTC (1510301580) (0)
kworker/0:1-67 [000] 21.814312: rtc_read_time: 2017-11-10 08:13:00 UTC (1510301580) (0)

set the first alarm timer:
kworker/0:1-67 [000] 21.829238: rtc_timer_enqueue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0
kworker/0:1-67 [000] 22.018279: rtc_set_alarm: 2017-11-10 08:15:00 UTC (1510301700) (0)

set the second alarm timer:
kworker/0:1-67 [000] 22.230284: rtc_timer_enqueue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0

the first alarm timer was expired:
kworker/0:1-67 [000] 145.155584: rtc_timer_dequeue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0
kworker/0:1-67 [000] 145.155593: rtc_timer_fired: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0
kworker/0:1-67 [000] 145.172504: rtc_set_alarm: 2017-11-10 08:17:00 UTC (1510301820) (0)

the second alarm timer was expired:
kworker/0:1-67 [000] 269.102353: rtc_timer_dequeue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0
kworker/0:1-67 [000] 269.102360: rtc_timer_fired: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0

disable alarm irq:
kworker/0:1-67 [000] 269.102469: rtc_alarm_irq_enable: disable RTC alarm IRQ (0)

Signed-off-by: Baolin Wang <baolin.wang@linaro.org>

---
Changes since v1:
 - Use unconditional tracepoints with tracing the failures.
 - Simplify the rtc_timer_class.
---
 drivers/rtc/interface.c    |   30 ++++++
 include/trace/events/rtc.h |  220 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 250 insertions(+)
 create mode 100644 include/trace/events/rtc.h

-- 
1.7.9.5

Comments

(Exiting) Baolin Wang Nov. 30, 2017, 2:56 a.m. UTC | #1
Hi Alexandre and Ingo,

On 16 November 2017 at 13:59, Baolin Wang <baolin.wang@linaro.org> wrote:
> It will be more helpful to add some tracepoints to track RTC actions when

> debugging RTC driver. Below sample is that we set/read the RTC time, then

> set 2 alarms, so we can see the trace logs:

>

> set/read RTC time:

> kworker/0:1-67 [000] 21.814245: rtc_set_time: 2017-11-10 08:13:00 UTC (1510301580) (0)

> kworker/0:1-67 [000] 21.814312: rtc_read_time: 2017-11-10 08:13:00 UTC (1510301580) (0)

>

> set the first alarm timer:

> kworker/0:1-67 [000] 21.829238: rtc_timer_enqueue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0

> kworker/0:1-67 [000] 22.018279: rtc_set_alarm: 2017-11-10 08:15:00 UTC (1510301700) (0)

>

> set the second alarm timer:

> kworker/0:1-67 [000] 22.230284: rtc_timer_enqueue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0

>

> the first alarm timer was expired:

> kworker/0:1-67 [000] 145.155584: rtc_timer_dequeue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0

> kworker/0:1-67 [000] 145.155593: rtc_timer_fired: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0

> kworker/0:1-67 [000] 145.172504: rtc_set_alarm: 2017-11-10 08:17:00 UTC (1510301820) (0)

>

> the second alarm timer was expired:

> kworker/0:1-67 [000] 269.102353: rtc_timer_dequeue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0

> kworker/0:1-67 [000] 269.102360: rtc_timer_fired: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0

>

> disable alarm irq:

> kworker/0:1-67 [000] 269.102469: rtc_alarm_irq_enable: disable RTC alarm IRQ (0)

>

> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>

> ---

> Changes since v1:

>  - Use unconditional tracepoints with tracing the failures.

>  - Simplify the rtc_timer_class.

> ---


Do you have any comments for this patch? Thanks.

-- 
Baolin Wang
Best Regards
Alexandre Belloni Dec. 12, 2017, 10:16 p.m. UTC | #2
Hi Baolin,

On 16/11/2017 at 13:59:28 +0800, Baolin Wang wrote:
> @@ -779,6 +797,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)

>  	}

>  

>  	timerqueue_add(&rtc->timerqueue, &timer->node);

> +	trace_rtc_timer_enqueue(timer);


This doesn't apply because of 74717b28cb32e1ad3c1042cafd76b264c8c0f68d.
Can you rebase?

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

> new file mode 100644

> index 0000000..b5a4add

> --- /dev/null

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

> @@ -0,0 +1,220 @@

> +#undef TRACE_SYSTEM

> +#define TRACE_SYSTEM rtc

> +

> +#if !defined(_TRACE_RTC_H) || defined(TRACE_HEADER_MULTI_READ)

> +#define _TRACE_RTC_H

> +

> +#include <linux/rtc.h>

> +#include <linux/tracepoint.h>

> +

> +DECLARE_EVENT_CLASS(rtc_time_alarm_class,

> +

> +	TP_PROTO(struct rtc_time *tm, int err),

> +

> +	TP_ARGS(tm, err),

> +

> +	TP_STRUCT__entry(

> +		__field(int, sec)

> +		__field(int, min)

> +		__field(int, hour)

> +		__field(int, mday)

> +		__field(int, mon)

> +		__field(int, year)

> +		__field(time64_t, secs)

> +		__field(int, err)

> +	),

> +

> +	TP_fast_assign(

> +		__entry->sec = tm->tm_sec;

> +		__entry->min = tm->tm_min;

> +		__entry->hour = tm->tm_hour;

> +		__entry->mday = tm->tm_mday;

> +		__entry->mon = tm->tm_mon;

> +		__entry->year = tm->tm_year;

> +		__entry->secs = rtc_tm_to_time64(tm);

> +		__entry->err = err;

> +	),

> +

> +	TP_printk("%d-%02d-%02d %02d:%02d:%02d UTC (%lld) (%d)",

> +		  __entry->year + 1900, __entry->mon + 1, __entry->mday,

> +		  __entry->hour, __entry->min, __entry->sec, __entry->secs,

> +		  __entry->err

> +	)

> +);

> +


Also, I'm a bit concerned about having a struct rtc_time here. I think
its goal is mainly to have a nice representation on the time but maybe
the best would be to make printk able to pretty print the time (some
patches were proposed).

How bad would that be to change it later? I didn't follow the whole
tracepoint ABI issue closely.


-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
(Exiting) Baolin Wang Dec. 13, 2017, 5:47 a.m. UTC | #3
Hi Alexandre,

On 13 December 2017 at 06:16, Alexandre Belloni
<alexandre.belloni@free-electrons.com> wrote:
> Hi Baolin,

>

> On 16/11/2017 at 13:59:28 +0800, Baolin Wang wrote:

>> @@ -779,6 +797,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)

>>       }

>>

>>       timerqueue_add(&rtc->timerqueue, &timer->node);

>> +     trace_rtc_timer_enqueue(timer);

>

> This doesn't apply because of 74717b28cb32e1ad3c1042cafd76b264c8c0f68d.

> Can you rebase?


Sure.

>

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

>> new file mode 100644

>> index 0000000..b5a4add

>> --- /dev/null

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

>> @@ -0,0 +1,220 @@

>> +#undef TRACE_SYSTEM

>> +#define TRACE_SYSTEM rtc

>> +

>> +#if !defined(_TRACE_RTC_H) || defined(TRACE_HEADER_MULTI_READ)

>> +#define _TRACE_RTC_H

>> +

>> +#include <linux/rtc.h>

>> +#include <linux/tracepoint.h>

>> +

>> +DECLARE_EVENT_CLASS(rtc_time_alarm_class,

>> +

>> +     TP_PROTO(struct rtc_time *tm, int err),

>> +

>> +     TP_ARGS(tm, err),

>> +

>> +     TP_STRUCT__entry(

>> +             __field(int, sec)

>> +             __field(int, min)

>> +             __field(int, hour)

>> +             __field(int, mday)

>> +             __field(int, mon)

>> +             __field(int, year)

>> +             __field(time64_t, secs)

>> +             __field(int, err)

>> +     ),

>> +

>> +     TP_fast_assign(

>> +             __entry->sec = tm->tm_sec;

>> +             __entry->min = tm->tm_min;

>> +             __entry->hour = tm->tm_hour;

>> +             __entry->mday = tm->tm_mday;

>> +             __entry->mon = tm->tm_mon;

>> +             __entry->year = tm->tm_year;

>> +             __entry->secs = rtc_tm_to_time64(tm);

>> +             __entry->err = err;

>> +     ),

>> +

>> +     TP_printk("%d-%02d-%02d %02d:%02d:%02d UTC (%lld) (%d)",

>> +               __entry->year + 1900, __entry->mon + 1, __entry->mday,

>> +               __entry->hour, __entry->min, __entry->sec, __entry->secs,

>> +               __entry->err

>> +     )

>> +);

>> +

>

> Also, I'm a bit concerned about having a struct rtc_time here. I think

> its goal is mainly to have a nice representation on the time but maybe


Yes.

> the best would be to make printk able to pretty print the time (some

> patches were proposed).


If I understood your point correctly, you did not like the format of
TP_printk() here, right? So how about if I remove the 'struct
rtc_time' and just pass one 'ktime_t' parameter? But it will be not
readable for user to trace the RTC time/alarm.

>

> How bad would that be to change it later? I didn't follow the whole

> tracepoint ABI issue closely.



-- 
Baolin.wang
Best Regards
Arnd Bergmann Dec. 13, 2017, 8:33 a.m. UTC | #4
On Wed, Dec 13, 2017 at 6:47 AM, Baolin Wang <baolin.wang@linaro.org> wrote:
>

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

>>> new file mode 100644

>>> index 0000000..b5a4add

>>> --- /dev/null

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

>>> +

>>

>> Also, I'm a bit concerned about having a struct rtc_time here. I think

>> its goal is mainly to have a nice representation on the time but maybe

>

> Yes.

>

>> the best would be to make printk able to pretty print the time (some

>> patches were proposed).

>

> If I understood your point correctly, you did not like the format of

> TP_printk() here, right? So how about if I remove the 'struct

> rtc_time' and just pass one 'ktime_t' parameter? But it will be not

> readable for user to trace the RTC time/alarm.

>

>>

>> How bad would that be to change it later? I didn't follow the whole

>> tracepoint ABI issue closely.


There is no general rule here other than "if it breaks for existing
users, we have to fix it". Anyone who uses the tracepoints correctly
would end up showing zero-date if we change all the fields, but
it should not crash here.

Printing a time64_t instead of rtc_time may be better here, as it's
cheaper to convert rtc_time to time64_t that vice versa. User space
looking at the trace data can then do the conversion back to struct tm
for printing in a C program or using /bin/date from a shell
script, but I agree it's an extra step.

It's also possible that we don't care about the overhead of doing
a time64_to_tm() or rtc_time64_to_tm() in the trace function, as long
as that only needs to be done if the tracepoint is active. I find trace
points a bit confusing, so I don't know if that is the case or not when
the tracepoint is compiled into the kernel but disabled at run time.

        Arnd
Alexandre Belloni Dec. 13, 2017, 11:04 a.m. UTC | #5
On 13/12/2017 at 09:33:23 +0100, Arnd Bergmann wrote:
> On Wed, Dec 13, 2017 at 6:47 AM, Baolin Wang <baolin.wang@linaro.org> wrote:

> >

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

> >>> new file mode 100644

> >>> index 0000000..b5a4add

> >>> --- /dev/null

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

> >>> +

> >>

> >> Also, I'm a bit concerned about having a struct rtc_time here. I think

> >> its goal is mainly to have a nice representation on the time but maybe

> >

> > Yes.

> >

> >> the best would be to make printk able to pretty print the time (some

> >> patches were proposed).

> >

> > If I understood your point correctly, you did not like the format of

> > TP_printk() here, right? So how about if I remove the 'struct

> > rtc_time' and just pass one 'ktime_t' parameter? But it will be not

> > readable for user to trace the RTC time/alarm.

> >


> >>

> >> How bad would that be to change it later? I didn't follow the whole

> >> tracepoint ABI issue closely.

> 

> There is no general rule here other than "if it breaks for existing

> users, we have to fix it". Anyone who uses the tracepoints correctly

> would end up showing zero-date if we change all the fields, but

> it should not crash here.

> 

> Printing a time64_t instead of rtc_time may be better here, as it's

> cheaper to convert rtc_time to time64_t that vice versa. User space

> looking at the trace data can then do the conversion back to struct tm

> for printing in a C program or using /bin/date from a shell

> script, but I agree it's an extra step.

> 

> It's also possible that we don't care about the overhead of doing

> a time64_to_tm() or rtc_time64_to_tm() in the trace function, as long

> as that only needs to be done if the tracepoint is active. I find trace

> points a bit confusing, so I don't know if that is the case or not when

> the tracepoint is compiled into the kernel but disabled at run time.

> 


Sorry, I was not clear and I never actually used tracepoints.

My point was that the printk format is nice and can probably be kept as
is. But I would like tracepoint to take a time64_t instead of an rtc_time even
if that means having a conversion before calling the tracepoint and
converting back to display the date/time.

Also, I think we could try having only the time64_t in the ring buffer.
Maybe I'm wrong but I think tools reading that buffer can do the
conversion themselves. Maybe I don't understand correctly how
tracepoints work and this doesn't make sense, tell me.

The printk patches I was referring to are:
https://marc.info/?l=linux-rtc&m=149693060517054&w=2
But they don't provide a way to pretty print a time64_t yet (it was just
suggested by Arnd).

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
Mark Brown Dec. 13, 2017, 12:16 p.m. UTC | #6
On Wed, Dec 13, 2017 at 12:04:26PM +0100, Alexandre Belloni wrote:

> Also, I think we could try having only the time64_t in the ring buffer.

> Maybe I'm wrong but I think tools reading that buffer can do the

> conversion themselves. Maybe I don't understand correctly how

> tracepoints work and this doesn't make sense, tell me.


Tools reading the buffer can do the conversion themselves but it's also
useful for users to just view the log directly via tracing/trace
sometimes.  OTOH the whole point is to be low overhead so...
Alexandre Belloni Dec. 13, 2017, 12:23 p.m. UTC | #7
On 13/12/2017 at 12:16:03 +0000, Mark Brown wrote:
> On Wed, Dec 13, 2017 at 12:04:26PM +0100, Alexandre Belloni wrote:

> 

> > Also, I think we could try having only the time64_t in the ring buffer.

> > Maybe I'm wrong but I think tools reading that buffer can do the

> > conversion themselves. Maybe I don't understand correctly how

> > tracepoints work and this doesn't make sense, tell me.

> 

> Tools reading the buffer can do the conversion themselves but it's also

> useful for users to just view the log directly via tracing/trace

> sometimes.  OTOH the whole point is to be low overhead so...


Yes, that is why I suggest keeping both representation in the printk
but only time64_t in the buffer. And this would be more convenient if we
add a way to pretty print a time64_t in vsprintf.

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
Steven Rostedt Dec. 13, 2017, 4:45 p.m. UTC | #8
On Wed, 13 Dec 2017 09:33:23 +0100
Arnd Bergmann <arnd@arndb.de> wrote:


> >> How bad would that be to change it later? I didn't follow the whole

> >> tracepoint ABI issue closely.  

> 

> There is no general rule here other than "if it breaks for existing

> users, we have to fix it". Anyone who uses the tracepoints correctly

> would end up showing zero-date if we change all the fields, but

> it should not crash here.


But if a tool depends on that value correct, that still is a user space
breakage, even though it was using tracepoints correctly.

> 

> Printing a time64_t instead of rtc_time may be better here, as it's

> cheaper to convert rtc_time to time64_t that vice versa. User space

> looking at the trace data can then do the conversion back to struct tm

> for printing in a C program or using /bin/date from a shell

> script, but I agree it's an extra step.

> 

> It's also possible that we don't care about the overhead of doing

> a time64_to_tm() or rtc_time64_to_tm() in the trace function, as long

> as that only needs to be done if the tracepoint is active. I find trace

> points a bit confusing, so I don't know if that is the case or not when

> the tracepoint is compiled into the kernel but disabled at run time.


Everything that is done in TP_fast_assign() is only performed when the
tracepoint is active. It adds no more overhead when tracing is
disabled, as tracepoints incorporate jump labels, and the call itself
is a nop. As long as the parameters to the trace_*() functions don't do
logic, all should be fine: like if you had:

  trace_rtc_set_alarm(call_some_function_to_return_tm(), err);

gcc could make that call_some_function_to_return_tm() happen even when
the tracepoint is not enabled. But that's not the case with this patch.

-- Steve
Steven Rostedt Dec. 13, 2017, 4:46 p.m. UTC | #9
On Wed, 13 Dec 2017 12:16:03 +0000
Mark Brown <broonie@kernel.org> wrote:

> On Wed, Dec 13, 2017 at 12:04:26PM +0100, Alexandre Belloni wrote:

> 

> > Also, I think we could try having only the time64_t in the ring buffer.

> > Maybe I'm wrong but I think tools reading that buffer can do the

> > conversion themselves. Maybe I don't understand correctly how

> > tracepoints work and this doesn't make sense, tell me.  

> 

> Tools reading the buffer can do the conversion themselves but it's also

> useful for users to just view the log directly via tracing/trace

> sometimes.  OTOH the whole point is to be low overhead so...


Ideally, we want TP_fast_assign() do as little as possible, and put all
the work in TP_printk() as the printk happens on read of the trace
buffer and TP_fast_assign() happens at the tracepoint invocation.

-- Steve
(Exiting) Baolin Wang Dec. 14, 2017, 3:07 a.m. UTC | #10
On 13 December 2017 at 20:23, Alexandre Belloni
<alexandre.belloni@free-electrons.com> wrote:
> On 13/12/2017 at 12:16:03 +0000, Mark Brown wrote:

>> On Wed, Dec 13, 2017 at 12:04:26PM +0100, Alexandre Belloni wrote:

>>

>> > Also, I think we could try having only the time64_t in the ring buffer.

>> > Maybe I'm wrong but I think tools reading that buffer can do the

>> > conversion themselves. Maybe I don't understand correctly how

>> > tracepoints work and this doesn't make sense, tell me.

>>

>> Tools reading the buffer can do the conversion themselves but it's also

>> useful for users to just view the log directly via tracing/trace

>> sometimes.  OTOH the whole point is to be low overhead so...

>

> Yes, that is why I suggest keeping both representation in the printk

> but only time64_t in the buffer. And this would be more convenient if we

> add a way to pretty print a time64_t in vsprintf.


OK. I will only keep time64_t in the buffer. Thanks for all your comments.

-- 
Baolin.wang
Best Regards
diff mbox series

Patch

diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 8cec9a0..879e40d 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -17,6 +17,9 @@ 
 #include <linux/log2.h>
 #include <linux/workqueue.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/rtc.h>
+
 static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer);
 static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer);
 
@@ -53,6 +56,8 @@  int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
 
 	err = __rtc_read_time(rtc, tm);
 	mutex_unlock(&rtc->ops_lock);
+
+	trace_rtc_read_time(tm, err);
 	return err;
 }
 EXPORT_SYMBOL_GPL(rtc_read_time);
@@ -87,6 +92,8 @@  int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
 	mutex_unlock(&rtc->ops_lock);
 	/* A timer might have just expired */
 	schedule_work(&rtc->irqwork);
+
+	trace_rtc_set_time(tm, err);
 	return err;
 }
 EXPORT_SYMBOL_GPL(rtc_set_time);
@@ -119,6 +126,8 @@  static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *al
 	}
 
 	mutex_unlock(&rtc->ops_lock);
+
+	trace_rtc_read_alarm(&alarm->time, err);
 	return err;
 }
 
@@ -316,6 +325,7 @@  int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
 	}
 	mutex_unlock(&rtc->ops_lock);
 
+	trace_rtc_read_alarm(&alarm->time, err);
 	return err;
 }
 EXPORT_SYMBOL_GPL(rtc_read_alarm);
@@ -352,6 +362,7 @@  static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
 	else
 		err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
 
+	trace_rtc_set_alarm(&alarm->time, err);
 	return err;
 }
 
@@ -406,6 +417,7 @@  int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
 
 		rtc->aie_timer.enabled = 1;
 		timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
+		trace_rtc_timer_enqueue(&rtc->aie_timer);
 	}
 	mutex_unlock(&rtc->ops_lock);
 	return err;
@@ -435,6 +447,8 @@  int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
 		err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled);
 
 	mutex_unlock(&rtc->ops_lock);
+
+	trace_rtc_alarm_irq_enable(enabled, err);
 	return err;
 }
 EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable);
@@ -709,6 +723,8 @@  int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled
 		rtc->pie_enabled = enabled;
 	}
 	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+
+	trace_rtc_irq_set_state(enabled, err);
 	return err;
 }
 EXPORT_SYMBOL_GPL(rtc_irq_set_state);
@@ -745,6 +761,8 @@  int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
 		}
 	}
 	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+
+	trace_rtc_irq_set_freq(freq, err);
 	return err;
 }
 EXPORT_SYMBOL_GPL(rtc_irq_set_freq);
@@ -779,6 +797,7 @@  static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
 	}
 
 	timerqueue_add(&rtc->timerqueue, &timer->node);
+	trace_rtc_timer_enqueue(timer);
 	if (!next) {
 		struct rtc_wkalrm alarm;
 		int err;
@@ -790,6 +809,7 @@  static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
 			schedule_work(&rtc->irqwork);
 		} else if (err) {
 			timerqueue_del(&rtc->timerqueue, &timer->node);
+			trace_rtc_timer_dequeue(timer);
 			timer->enabled = 0;
 			return err;
 		}
@@ -803,6 +823,7 @@  static void rtc_alarm_disable(struct rtc_device *rtc)
 		return;
 
 	rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
+	trace_rtc_alarm_irq_enable(0, 0);
 }
 
 /**
@@ -821,6 +842,7 @@  static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer)
 {
 	struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
 	timerqueue_del(&rtc->timerqueue, &timer->node);
+	trace_rtc_timer_dequeue(timer);
 	timer->enabled = 0;
 	if (next == &timer->node) {
 		struct rtc_wkalrm alarm;
@@ -871,16 +893,19 @@  void rtc_timer_do_work(struct work_struct *work)
 		/* expire timer */
 		timer = container_of(next, struct rtc_timer, node);
 		timerqueue_del(&rtc->timerqueue, &timer->node);
+		trace_rtc_timer_dequeue(timer);
 		timer->enabled = 0;
 		if (timer->task.func)
 			timer->task.func(timer->task.private_data);
 
+		trace_rtc_timer_fired(timer);
 		/* Re-add/fwd periodic timers */
 		if (ktime_to_ns(timer->period)) {
 			timer->node.expires = ktime_add(timer->node.expires,
 							timer->period);
 			timer->enabled = 1;
 			timerqueue_add(&rtc->timerqueue, &timer->node);
+			trace_rtc_timer_enqueue(timer);
 		}
 	}
 
@@ -902,6 +927,7 @@  void rtc_timer_do_work(struct work_struct *work)
 
 			timer = container_of(next, struct rtc_timer, node);
 			timerqueue_del(&rtc->timerqueue, &timer->node);
+			trace_rtc_timer_dequeue(timer);
 			timer->enabled = 0;
 			dev_err(&rtc->dev, "__rtc_set_alarm: err=%d\n", err);
 			goto again;
@@ -992,6 +1018,8 @@  int rtc_read_offset(struct rtc_device *rtc, long *offset)
 	mutex_lock(&rtc->ops_lock);
 	ret = rtc->ops->read_offset(rtc->dev.parent, offset);
 	mutex_unlock(&rtc->ops_lock);
+
+	trace_rtc_read_offset(*offset, ret);
 	return ret;
 }
 
@@ -1021,5 +1049,7 @@  int rtc_set_offset(struct rtc_device *rtc, long offset)
 	mutex_lock(&rtc->ops_lock);
 	ret = rtc->ops->set_offset(rtc->dev.parent, offset);
 	mutex_unlock(&rtc->ops_lock);
+
+	trace_rtc_set_offset(offset, ret);
 	return ret;
 }
diff --git a/include/trace/events/rtc.h b/include/trace/events/rtc.h
new file mode 100644
index 0000000..b5a4add
--- /dev/null
+++ b/include/trace/events/rtc.h
@@ -0,0 +1,220 @@ 
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rtc
+
+#if !defined(_TRACE_RTC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RTC_H
+
+#include <linux/rtc.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(rtc_time_alarm_class,
+
+	TP_PROTO(struct rtc_time *tm, int err),
+
+	TP_ARGS(tm, err),
+
+	TP_STRUCT__entry(
+		__field(int, sec)
+		__field(int, min)
+		__field(int, hour)
+		__field(int, mday)
+		__field(int, mon)
+		__field(int, year)
+		__field(time64_t, secs)
+		__field(int, err)
+	),
+
+	TP_fast_assign(
+		__entry->sec = tm->tm_sec;
+		__entry->min = tm->tm_min;
+		__entry->hour = tm->tm_hour;
+		__entry->mday = tm->tm_mday;
+		__entry->mon = tm->tm_mon;
+		__entry->year = tm->tm_year;
+		__entry->secs = rtc_tm_to_time64(tm);
+		__entry->err = err;
+	),
+
+	TP_printk("%d-%02d-%02d %02d:%02d:%02d UTC (%lld) (%d)",
+		  __entry->year + 1900, __entry->mon + 1, __entry->mday,
+		  __entry->hour, __entry->min, __entry->sec, __entry->secs,
+		  __entry->err
+	)
+);
+
+DEFINE_EVENT(rtc_time_alarm_class, rtc_set_time,
+
+	TP_PROTO(struct rtc_time *tm, int err),
+
+	TP_ARGS(tm, err)
+);
+
+DEFINE_EVENT(rtc_time_alarm_class, rtc_read_time,
+
+	TP_PROTO(struct rtc_time *tm, int err),
+
+	TP_ARGS(tm, err)
+);
+
+DEFINE_EVENT(rtc_time_alarm_class, rtc_set_alarm,
+
+	TP_PROTO(struct rtc_time *tm, int err),
+
+	TP_ARGS(tm, err)
+);
+
+DEFINE_EVENT(rtc_time_alarm_class, rtc_read_alarm,
+
+	TP_PROTO(struct rtc_time *tm, int err),
+
+	TP_ARGS(tm, err)
+);
+
+TRACE_EVENT(rtc_irq_set_freq,
+
+	TP_PROTO(int freq, int err),
+
+	TP_ARGS(freq, err),
+
+	TP_STRUCT__entry(
+		__field(int, freq)
+		__field(int, err)
+	),
+
+	TP_fast_assign(
+		__entry->freq = freq;
+		__entry->err = err;
+	),
+
+	TP_printk("set RTC periodic IRQ frequency:%u (%d)",
+		  __entry->freq, __entry->err
+	)
+);
+
+TRACE_EVENT(rtc_irq_set_state,
+
+	TP_PROTO(int enabled, int err),
+
+	TP_ARGS(enabled, err),
+
+	TP_STRUCT__entry(
+		__field(int, enabled)
+		__field(int, err)
+	),
+
+	TP_fast_assign(
+		__entry->enabled = enabled;
+		__entry->err = err;
+	),
+
+	TP_printk("%s RTC 2^N Hz periodic IRQs (%d)",
+		  __entry->enabled ? "enable" : "disable",
+		  __entry->err
+	)
+);
+
+TRACE_EVENT(rtc_alarm_irq_enable,
+
+	TP_PROTO(unsigned int enabled, int err),
+
+	TP_ARGS(enabled, err),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, enabled)
+		__field(int, err)
+	),
+
+	TP_fast_assign(
+		__entry->enabled = enabled;
+		__entry->err = err;
+	),
+
+	TP_printk("%s RTC alarm IRQ (%d)",
+		  __entry->enabled ? "enable" : "disable",
+		  __entry->err
+	)
+);
+
+DECLARE_EVENT_CLASS(rtc_offset_class,
+
+	TP_PROTO(long offset, int err),
+
+	TP_ARGS(offset, err),
+
+	TP_STRUCT__entry(
+		__field(long, offset)
+		__field(int, err)
+	),
+
+	TP_fast_assign(
+		__entry->offset = offset;
+		__entry->err = err;
+	),
+
+	TP_printk("RTC offset: %ld (%d)",
+		  __entry->offset, __entry->err
+	)
+);
+
+DEFINE_EVENT(rtc_offset_class, rtc_set_offset,
+
+	TP_PROTO(long offset, int err),
+
+	TP_ARGS(offset, err)
+);
+
+DEFINE_EVENT(rtc_offset_class, rtc_read_offset,
+
+	TP_PROTO(long offset, int err),
+
+	TP_ARGS(offset, err)
+);
+
+DECLARE_EVENT_CLASS(rtc_timer_class,
+
+	TP_PROTO(struct rtc_timer *timer),
+
+	TP_ARGS(timer),
+
+	TP_STRUCT__entry(
+		__field(struct rtc_timer *, timer)
+		__field(ktime_t, expires)
+		__field(ktime_t, period)
+	),
+
+	TP_fast_assign(
+		__entry->timer = timer;
+		__entry->expires = timer->node.expires;
+		__entry->period = timer->period;
+	),
+
+	TP_printk("RTC timer:(%p) expires:%lld period:%lld",
+		  __entry->timer, __entry->expires, __entry->period
+	)
+);
+
+DEFINE_EVENT(rtc_timer_class, rtc_timer_enqueue,
+
+	TP_PROTO(struct rtc_timer *timer),
+
+	TP_ARGS(timer)
+);
+
+DEFINE_EVENT(rtc_timer_class, rtc_timer_dequeue,
+
+	TP_PROTO(struct rtc_timer *timer),
+
+	TP_ARGS(timer)
+);
+
+DEFINE_EVENT(rtc_timer_class, rtc_timer_fired,
+
+	TP_PROTO(struct rtc_timer *timer),
+
+	TP_ARGS(timer)
+);
+
+#endif /* _TRACE_RTC_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>