mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 07:01:32 +00:00
6249687f76
When debugging tight race conditions, it can be helpful to have a synchronized tracing method. Although in most cases the global clock provides this functionality, if timings is not the issue, it is more comforting to know that the order of events really happened in a precise order. Instead of using a clock, add a "counter" that is simply an incrementing atomic 64bit counter that orders the events as they are perceived to happen. The trace_clock_counter() is added from the attempt by Peter Zijlstra trying to convert the trace_clock_global() to it. I took Peter's counter code and made trace_clock_counter() instead, and added it to the choice of clocks. Just echo counter > /debug/tracing/trace_clock to activate it. Requested-by: Thomas Gleixner <tglx@linutronix.de> Requested-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Reviewed-By: Valdis Kletnieks <valdis.kletnieks@vt.edu> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
128 lines
3.0 KiB
C
128 lines
3.0 KiB
C
/*
|
|
* tracing clocks
|
|
*
|
|
* Copyright (C) 2009 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
|
|
*
|
|
* Implements 3 trace clock variants, with differing scalability/precision
|
|
* tradeoffs:
|
|
*
|
|
* - local: CPU-local trace clock
|
|
* - medium: scalable global clock with some jitter
|
|
* - global: globally monotonic, serialized clock
|
|
*
|
|
* Tracer plugins will chose a default from these clocks.
|
|
*/
|
|
#include <linux/spinlock.h>
|
|
#include <linux/irqflags.h>
|
|
#include <linux/hardirq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/ktime.h>
|
|
#include <linux/trace_clock.h>
|
|
|
|
#include "trace.h"
|
|
|
|
/*
|
|
* trace_clock_local(): the simplest and least coherent tracing clock.
|
|
*
|
|
* Useful for tracing that does not cross to other CPUs nor
|
|
* does it go through idle events.
|
|
*/
|
|
u64 notrace trace_clock_local(void)
|
|
{
|
|
u64 clock;
|
|
|
|
/*
|
|
* sched_clock() is an architecture implemented, fast, scalable,
|
|
* lockless clock. It is not guaranteed to be coherent across
|
|
* CPUs, nor across CPU idle events.
|
|
*/
|
|
preempt_disable_notrace();
|
|
clock = sched_clock();
|
|
preempt_enable_notrace();
|
|
|
|
return clock;
|
|
}
|
|
|
|
/*
|
|
* trace_clock(): 'between' trace clock. Not completely serialized,
|
|
* but not completely incorrect when crossing CPUs either.
|
|
*
|
|
* This is based on cpu_clock(), which will allow at most ~1 jiffy of
|
|
* jitter between CPUs. So it's a pretty scalable clock, but there
|
|
* can be offsets in the trace data.
|
|
*/
|
|
u64 notrace trace_clock(void)
|
|
{
|
|
return local_clock();
|
|
}
|
|
|
|
|
|
/*
|
|
* trace_clock_global(): special globally coherent trace clock
|
|
*
|
|
* It has higher overhead than the other trace clocks but is still
|
|
* an order of magnitude faster than GTOD derived hardware clocks.
|
|
*
|
|
* Used by plugins that need globally coherent timestamps.
|
|
*/
|
|
|
|
/* keep prev_time and lock in the same cacheline. */
|
|
static struct {
|
|
u64 prev_time;
|
|
arch_spinlock_t lock;
|
|
} trace_clock_struct ____cacheline_aligned_in_smp =
|
|
{
|
|
.lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED,
|
|
};
|
|
|
|
u64 notrace trace_clock_global(void)
|
|
{
|
|
unsigned long flags;
|
|
int this_cpu;
|
|
u64 now;
|
|
|
|
local_irq_save(flags);
|
|
|
|
this_cpu = raw_smp_processor_id();
|
|
now = cpu_clock(this_cpu);
|
|
/*
|
|
* If in an NMI context then dont risk lockups and return the
|
|
* cpu_clock() time:
|
|
*/
|
|
if (unlikely(in_nmi()))
|
|
goto out;
|
|
|
|
arch_spin_lock(&trace_clock_struct.lock);
|
|
|
|
/*
|
|
* TODO: if this happens often then maybe we should reset
|
|
* my_scd->clock to prev_time+1, to make sure
|
|
* we start ticking with the local clock from now on?
|
|
*/
|
|
if ((s64)(now - trace_clock_struct.prev_time) < 0)
|
|
now = trace_clock_struct.prev_time + 1;
|
|
|
|
trace_clock_struct.prev_time = now;
|
|
|
|
arch_spin_unlock(&trace_clock_struct.lock);
|
|
|
|
out:
|
|
local_irq_restore(flags);
|
|
|
|
return now;
|
|
}
|
|
|
|
static atomic64_t trace_counter;
|
|
|
|
/*
|
|
* trace_clock_counter(): simply an atomic counter.
|
|
* Use the trace_counter "counter" for cases where you do not care
|
|
* about timings, but are interested in strict ordering.
|
|
*/
|
|
u64 notrace trace_clock_counter(void)
|
|
{
|
|
return atomic64_add_return(1, &trace_counter);
|
|
}
|