forked from Minki/linux
Merge branch 'tip/tracing/ftrace' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into tracing/ftrace
This commit is contained in:
commit
2084e0225e
@ -258,6 +258,11 @@ enum
|
||||
NR_SOFTIRQS
|
||||
};
|
||||
|
||||
/* map softirq index to softirq name. update 'softirq_to_name' in
|
||||
* kernel/softirq.c when adding a new softirq.
|
||||
*/
|
||||
extern char *softirq_to_name[NR_SOFTIRQS];
|
||||
|
||||
/* softirq mask and active fields moved to irq_cpustat_t in
|
||||
* asm/hardirq.h to get better cache usage. KAO
|
||||
*/
|
||||
|
@ -452,31 +452,45 @@ do { \
|
||||
|
||||
#define trace_printk(fmt, args...) \
|
||||
do { \
|
||||
static const char *trace_printk_fmt \
|
||||
__attribute__((section("__trace_printk_fmt"))); \
|
||||
\
|
||||
if (!trace_printk_fmt) \
|
||||
trace_printk_fmt = fmt; \
|
||||
\
|
||||
__trace_printk_check_format(fmt, ##args); \
|
||||
__trace_printk(_THIS_IP_, trace_printk_fmt, ##args); \
|
||||
if (__builtin_constant_p(fmt)) { \
|
||||
static const char *trace_printk_fmt \
|
||||
__attribute__((section("__trace_printk_fmt"))) = \
|
||||
__builtin_constant_p(fmt) ? fmt : NULL; \
|
||||
\
|
||||
__trace_bprintk(_THIS_IP_, trace_printk_fmt, ##args); \
|
||||
} else \
|
||||
__trace_printk(_THIS_IP_, fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
extern int
|
||||
__trace_bprintk(unsigned long ip, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
extern int
|
||||
__trace_printk(unsigned long ip, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
/*
|
||||
* The double __builtin_constant_p is because gcc will give us an error
|
||||
* if we try to allocate the static variable to fmt if it is not a
|
||||
* constant. Even with the outer if statement.
|
||||
*/
|
||||
#define ftrace_vprintk(fmt, vargs) \
|
||||
do { \
|
||||
static const char *trace_printk_fmt \
|
||||
__attribute__((section("__trace_printk_fmt"))); \
|
||||
if (__builtin_constant_p(fmt)) { \
|
||||
static const char *trace_printk_fmt \
|
||||
__attribute__((section("__trace_printk_fmt"))) = \
|
||||
__builtin_constant_p(fmt) ? fmt : NULL; \
|
||||
\
|
||||
if (!trace_printk_fmt) \
|
||||
trace_printk_fmt = fmt; \
|
||||
\
|
||||
__ftrace_vprintk(_THIS_IP_, trace_printk_fmt, vargs); \
|
||||
__ftrace_vbprintk(_THIS_IP_, trace_printk_fmt, vargs); \
|
||||
} else \
|
||||
__ftrace_vprintk(_THIS_IP_, fmt, vargs); \
|
||||
} while (0)
|
||||
|
||||
extern int
|
||||
__ftrace_vbprintk(unsigned long ip, const char *fmt, va_list ap);
|
||||
|
||||
extern int
|
||||
__ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap);
|
||||
|
||||
|
@ -40,4 +40,16 @@ TRACE_EVENT(irq_handler_exit,
|
||||
__entry->irq, __entry->ret ? "handled" : "unhandled")
|
||||
);
|
||||
|
||||
TRACE_FORMAT(softirq_entry,
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_ARGS(h, vec),
|
||||
TP_FMT("softirq=%d action=%s", (int)(h - vec), softirq_to_name[h-vec])
|
||||
);
|
||||
|
||||
TRACE_FORMAT(softirq_exit,
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_ARGS(h, vec),
|
||||
TP_FMT("softirq=%d action=%s", (int)(h - vec), softirq_to_name[h-vec])
|
||||
);
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/tick.h>
|
||||
#include <trace/irq.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
/*
|
||||
@ -53,6 +54,12 @@ static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp
|
||||
|
||||
static DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
|
||||
|
||||
char *softirq_to_name[NR_SOFTIRQS] = {
|
||||
"HI_SOFTIRQ", "TIMER_SOFTIRQ", "NET_TX_SOFTIRQ", "NET_RX_SOFTIRQ",
|
||||
"BLOCK_SOFTIRQ", "TASKLET_SOFTIRQ", "SCHED_SOFTIRQ", "HRTIMER_SOFTIRQ",
|
||||
"RCU_SOFTIRQ"
|
||||
};
|
||||
|
||||
/*
|
||||
* we cannot loop indefinitely here to avoid userspace starvation,
|
||||
* but we also don't want to introduce a worst case 1/HZ latency
|
||||
@ -180,6 +187,9 @@ EXPORT_SYMBOL(local_bh_enable_ip);
|
||||
*/
|
||||
#define MAX_SOFTIRQ_RESTART 10
|
||||
|
||||
DEFINE_TRACE(softirq_entry);
|
||||
DEFINE_TRACE(softirq_exit);
|
||||
|
||||
asmlinkage void __do_softirq(void)
|
||||
{
|
||||
struct softirq_action *h;
|
||||
@ -206,12 +216,14 @@ restart:
|
||||
if (pending & 1) {
|
||||
int prev_count = preempt_count();
|
||||
|
||||
trace_softirq_entry(h, softirq_vec);
|
||||
h->action(h);
|
||||
|
||||
trace_softirq_exit(h, softirq_vec);
|
||||
if (unlikely(prev_count != preempt_count())) {
|
||||
printk(KERN_ERR "huh, entered softirq %td %p"
|
||||
printk(KERN_ERR "huh, entered softirq %td %s %p"
|
||||
"with preempt_count %08x,"
|
||||
" exited with %08x?\n", h - softirq_vec,
|
||||
softirq_to_name[h - softirq_vec],
|
||||
h->action, prev_count, preempt_count());
|
||||
preempt_count() = prev_count;
|
||||
}
|
||||
|
@ -16,10 +16,79 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
/*
|
||||
* The ring buffer is made up of a list of pages. A separate list of pages is
|
||||
* allocated for each CPU. A writer may only write to a buffer that is
|
||||
* associated with the CPU it is currently executing on. A reader may read
|
||||
* from any per cpu buffer.
|
||||
*
|
||||
* The reader is special. For each per cpu buffer, the reader has its own
|
||||
* reader page. When a reader has read the entire reader page, this reader
|
||||
* page is swapped with another page in the ring buffer.
|
||||
*
|
||||
* Now, as long as the writer is off the reader page, the reader can do what
|
||||
* ever it wants with that page. The writer will never write to that page
|
||||
* again (as long as it is out of the ring buffer).
|
||||
*
|
||||
* Here's some silly ASCII art.
|
||||
*
|
||||
* +------+
|
||||
* |reader| RING BUFFER
|
||||
* |page |
|
||||
* +------+ +---+ +---+ +---+
|
||||
* | |-->| |-->| |
|
||||
* +---+ +---+ +---+
|
||||
* ^ |
|
||||
* | |
|
||||
* +---------------+
|
||||
*
|
||||
*
|
||||
* +------+
|
||||
* |reader| RING BUFFER
|
||||
* |page |------------------v
|
||||
* +------+ +---+ +---+ +---+
|
||||
* | |-->| |-->| |
|
||||
* +---+ +---+ +---+
|
||||
* ^ |
|
||||
* | |
|
||||
* +---------------+
|
||||
*
|
||||
*
|
||||
* +------+
|
||||
* |reader| RING BUFFER
|
||||
* |page |------------------v
|
||||
* +------+ +---+ +---+ +---+
|
||||
* ^ | |-->| |-->| |
|
||||
* | +---+ +---+ +---+
|
||||
* | |
|
||||
* | |
|
||||
* +------------------------------+
|
||||
*
|
||||
*
|
||||
* +------+
|
||||
* |buffer| RING BUFFER
|
||||
* |page |------------------v
|
||||
* +------+ +---+ +---+ +---+
|
||||
* ^ | | | |-->| |
|
||||
* | New +---+ +---+ +---+
|
||||
* | Reader------^ |
|
||||
* | page |
|
||||
* +------------------------------+
|
||||
*
|
||||
*
|
||||
* After we make this swap, the reader can hand this page off to the splice
|
||||
* code and be done with it. It can even allocate a new page if it needs to
|
||||
* and swap that into the ring buffer.
|
||||
*
|
||||
* We will be using cmpxchg soon to make all this lockless.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* A fast way to enable or disable all ring buffers is to
|
||||
* call tracing_on or tracing_off. Turning off the ring buffers
|
||||
@ -301,6 +370,10 @@ struct ring_buffer {
|
||||
struct mutex mutex;
|
||||
|
||||
struct ring_buffer_per_cpu **buffers;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
struct notifier_block cpu_notify;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ring_buffer_iter {
|
||||
@ -459,6 +532,11 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
*/
|
||||
extern int ring_buffer_page_too_big(void);
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
static int __cpuinit rb_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ring_buffer_alloc - allocate a new ring_buffer
|
||||
* @size: the size in bytes per cpu that is needed.
|
||||
@ -496,7 +574,8 @@ struct ring_buffer *ring_buffer_alloc(unsigned long size, unsigned flags)
|
||||
if (buffer->pages == 1)
|
||||
buffer->pages++;
|
||||
|
||||
cpumask_copy(buffer->cpumask, cpu_possible_mask);
|
||||
get_online_cpus();
|
||||
cpumask_copy(buffer->cpumask, cpu_online_mask);
|
||||
buffer->cpus = nr_cpu_ids;
|
||||
|
||||
bsize = sizeof(void *) * nr_cpu_ids;
|
||||
@ -512,6 +591,13 @@ struct ring_buffer *ring_buffer_alloc(unsigned long size, unsigned flags)
|
||||
goto fail_free_buffers;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
buffer->cpu_notify.notifier_call = rb_cpu_notify;
|
||||
buffer->cpu_notify.priority = 0;
|
||||
register_cpu_notifier(&buffer->cpu_notify);
|
||||
#endif
|
||||
|
||||
put_online_cpus();
|
||||
mutex_init(&buffer->mutex);
|
||||
|
||||
return buffer;
|
||||
@ -525,6 +611,7 @@ struct ring_buffer *ring_buffer_alloc(unsigned long size, unsigned flags)
|
||||
|
||||
fail_free_cpumask:
|
||||
free_cpumask_var(buffer->cpumask);
|
||||
put_online_cpus();
|
||||
|
||||
fail_free_buffer:
|
||||
kfree(buffer);
|
||||
@ -541,9 +628,17 @@ ring_buffer_free(struct ring_buffer *buffer)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
unregister_cpu_notifier(&buffer->cpu_notify);
|
||||
#endif
|
||||
|
||||
for_each_buffer_cpu(buffer, cpu)
|
||||
rb_free_cpu_buffer(buffer->buffers[cpu]);
|
||||
|
||||
put_online_cpus();
|
||||
|
||||
free_cpumask_var(buffer->cpumask);
|
||||
|
||||
kfree(buffer);
|
||||
@ -649,16 +744,15 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size)
|
||||
return size;
|
||||
|
||||
mutex_lock(&buffer->mutex);
|
||||
get_online_cpus();
|
||||
|
||||
nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
|
||||
|
||||
if (size < buffer_size) {
|
||||
|
||||
/* easy case, just free pages */
|
||||
if (RB_WARN_ON(buffer, nr_pages >= buffer->pages)) {
|
||||
mutex_unlock(&buffer->mutex);
|
||||
return -1;
|
||||
}
|
||||
if (RB_WARN_ON(buffer, nr_pages >= buffer->pages))
|
||||
goto out_fail;
|
||||
|
||||
rm_pages = buffer->pages - nr_pages;
|
||||
|
||||
@ -677,10 +771,8 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size)
|
||||
* add these pages to the cpu_buffers. Otherwise we just free
|
||||
* them all and return -ENOMEM;
|
||||
*/
|
||||
if (RB_WARN_ON(buffer, nr_pages <= buffer->pages)) {
|
||||
mutex_unlock(&buffer->mutex);
|
||||
return -1;
|
||||
}
|
||||
if (RB_WARN_ON(buffer, nr_pages <= buffer->pages))
|
||||
goto out_fail;
|
||||
|
||||
new_pages = nr_pages - buffer->pages;
|
||||
|
||||
@ -705,13 +797,12 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size)
|
||||
rb_insert_pages(cpu_buffer, &pages, new_pages);
|
||||
}
|
||||
|
||||
if (RB_WARN_ON(buffer, !list_empty(&pages))) {
|
||||
mutex_unlock(&buffer->mutex);
|
||||
return -1;
|
||||
}
|
||||
if (RB_WARN_ON(buffer, !list_empty(&pages)))
|
||||
goto out_fail;
|
||||
|
||||
out:
|
||||
buffer->pages = nr_pages;
|
||||
put_online_cpus();
|
||||
mutex_unlock(&buffer->mutex);
|
||||
|
||||
return size;
|
||||
@ -721,8 +812,18 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size)
|
||||
list_del_init(&bpage->list);
|
||||
free_buffer_page(bpage);
|
||||
}
|
||||
put_online_cpus();
|
||||
mutex_unlock(&buffer->mutex);
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Something went totally wrong, and we are too paranoid
|
||||
* to even clean up the mess.
|
||||
*/
|
||||
out_fail:
|
||||
put_online_cpus();
|
||||
mutex_unlock(&buffer->mutex);
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_resize);
|
||||
|
||||
@ -1564,12 +1665,15 @@ EXPORT_SYMBOL_GPL(ring_buffer_record_enable_cpu);
|
||||
unsigned long ring_buffer_entries_cpu(struct ring_buffer *buffer, int cpu)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
unsigned long ret;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return 0;
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
return cpu_buffer->entries;
|
||||
ret = cpu_buffer->entries;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_entries_cpu);
|
||||
|
||||
@ -1581,12 +1685,15 @@ EXPORT_SYMBOL_GPL(ring_buffer_entries_cpu);
|
||||
unsigned long ring_buffer_overrun_cpu(struct ring_buffer *buffer, int cpu)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
unsigned long ret;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return 0;
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
return cpu_buffer->overrun;
|
||||
ret = cpu_buffer->overrun;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_overrun_cpu);
|
||||
|
||||
@ -1663,9 +1770,14 @@ static void rb_iter_reset(struct ring_buffer_iter *iter)
|
||||
*/
|
||||
void ring_buffer_iter_reset(struct ring_buffer_iter *iter)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer = iter->cpu_buffer;
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
unsigned long flags;
|
||||
|
||||
if (!iter)
|
||||
return;
|
||||
|
||||
cpu_buffer = iter->cpu_buffer;
|
||||
|
||||
spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
|
||||
rb_iter_reset(iter);
|
||||
spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
@ -1900,9 +2012,6 @@ rb_buffer_peek(struct ring_buffer *buffer, int cpu, u64 *ts)
|
||||
struct buffer_page *reader;
|
||||
int nr_loops = 0;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return NULL;
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
|
||||
again:
|
||||
@ -2031,6 +2140,9 @@ ring_buffer_peek(struct ring_buffer *buffer, int cpu, u64 *ts)
|
||||
struct ring_buffer_event *event;
|
||||
unsigned long flags;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
|
||||
event = rb_buffer_peek(buffer, cpu, ts);
|
||||
spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
@ -2071,24 +2183,31 @@ ring_buffer_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
|
||||
struct ring_buffer_event *
|
||||
ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu];
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
struct ring_buffer_event *event = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return NULL;
|
||||
/* might be called in atomic */
|
||||
preempt_disable();
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
goto out;
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
event = rb_buffer_peek(buffer, cpu, ts);
|
||||
if (!event)
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
|
||||
rb_advance_reader(cpu_buffer);
|
||||
|
||||
out:
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
out:
|
||||
preempt_enable();
|
||||
|
||||
return event;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_consume);
|
||||
@ -2268,6 +2387,7 @@ int ring_buffer_empty(struct ring_buffer *buffer)
|
||||
if (!rb_per_cpu_empty(cpu_buffer))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_empty);
|
||||
@ -2280,12 +2400,16 @@ EXPORT_SYMBOL_GPL(ring_buffer_empty);
|
||||
int ring_buffer_empty_cpu(struct ring_buffer *buffer, int cpu)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
int ret;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return 1;
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
return rb_per_cpu_empty(cpu_buffer);
|
||||
ret = rb_per_cpu_empty(cpu_buffer);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_empty_cpu);
|
||||
|
||||
@ -2304,32 +2428,35 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a,
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer_a;
|
||||
struct ring_buffer_per_cpu *cpu_buffer_b;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer_a->cpumask) ||
|
||||
!cpumask_test_cpu(cpu, buffer_b->cpumask))
|
||||
return -EINVAL;
|
||||
goto out;
|
||||
|
||||
/* At least make sure the two buffers are somewhat the same */
|
||||
if (buffer_a->pages != buffer_b->pages)
|
||||
return -EINVAL;
|
||||
goto out;
|
||||
|
||||
ret = -EAGAIN;
|
||||
|
||||
if (ring_buffer_flags != RB_BUFFERS_ON)
|
||||
return -EAGAIN;
|
||||
goto out;
|
||||
|
||||
if (atomic_read(&buffer_a->record_disabled))
|
||||
return -EAGAIN;
|
||||
goto out;
|
||||
|
||||
if (atomic_read(&buffer_b->record_disabled))
|
||||
return -EAGAIN;
|
||||
goto out;
|
||||
|
||||
cpu_buffer_a = buffer_a->buffers[cpu];
|
||||
cpu_buffer_b = buffer_b->buffers[cpu];
|
||||
|
||||
if (atomic_read(&cpu_buffer_a->record_disabled))
|
||||
return -EAGAIN;
|
||||
goto out;
|
||||
|
||||
if (atomic_read(&cpu_buffer_b->record_disabled))
|
||||
return -EAGAIN;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We can't do a synchronize_sched here because this
|
||||
@ -2349,7 +2476,9 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a,
|
||||
atomic_dec(&cpu_buffer_a->record_disabled);
|
||||
atomic_dec(&cpu_buffer_b->record_disabled);
|
||||
|
||||
return 0;
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_swap_cpu);
|
||||
|
||||
@ -2464,27 +2593,30 @@ int ring_buffer_read_page(struct ring_buffer *buffer,
|
||||
u64 save_timestamp;
|
||||
int ret = -1;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If len is not big enough to hold the page header, then
|
||||
* we can not copy anything.
|
||||
*/
|
||||
if (len <= BUF_PAGE_HDR_SIZE)
|
||||
return -1;
|
||||
goto out;
|
||||
|
||||
len -= BUF_PAGE_HDR_SIZE;
|
||||
|
||||
if (!data_page)
|
||||
return -1;
|
||||
goto out;
|
||||
|
||||
bpage = *data_page;
|
||||
if (!bpage)
|
||||
return -1;
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
reader = rb_get_reader_page(cpu_buffer);
|
||||
if (!reader)
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
|
||||
event = rb_reader_event(cpu_buffer);
|
||||
|
||||
@ -2506,7 +2638,7 @@ int ring_buffer_read_page(struct ring_buffer *buffer,
|
||||
unsigned int size;
|
||||
|
||||
if (full)
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
|
||||
if (len > (commit - read))
|
||||
len = (commit - read);
|
||||
@ -2514,7 +2646,7 @@ int ring_buffer_read_page(struct ring_buffer *buffer,
|
||||
size = rb_event_length(event);
|
||||
|
||||
if (len < size)
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
|
||||
/* save the current timestamp, since the user will need it */
|
||||
save_timestamp = cpu_buffer->read_stamp;
|
||||
@ -2553,9 +2685,10 @@ int ring_buffer_read_page(struct ring_buffer *buffer,
|
||||
}
|
||||
ret = read;
|
||||
|
||||
out:
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2629,3 +2762,42 @@ static __init int rb_init_debugfs(void)
|
||||
}
|
||||
|
||||
fs_initcall(rb_init_debugfs);
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
static int __cpuinit rb_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
struct ring_buffer *buffer =
|
||||
container_of(self, struct ring_buffer, cpu_notify);
|
||||
long cpu = (long)hcpu;
|
||||
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
if (cpu_isset(cpu, *buffer->cpumask))
|
||||
return NOTIFY_OK;
|
||||
|
||||
buffer->buffers[cpu] =
|
||||
rb_allocate_cpu_buffer(buffer, cpu);
|
||||
if (!buffer->buffers[cpu]) {
|
||||
WARN(1, "failed to allocate ring buffer on CPU %ld\n",
|
||||
cpu);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
smp_wmb();
|
||||
cpu_set(cpu, *buffer->cpumask);
|
||||
break;
|
||||
case CPU_DOWN_PREPARE:
|
||||
case CPU_DOWN_PREPARE_FROZEN:
|
||||
/*
|
||||
* Do nothing.
|
||||
* If we were to free the buffer, then the user would
|
||||
* lose any trace that was in the buffer.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
#endif
|
||||
|
@ -44,6 +44,12 @@
|
||||
unsigned long __read_mostly tracing_max_latency;
|
||||
unsigned long __read_mostly tracing_thresh;
|
||||
|
||||
/*
|
||||
* On boot up, the ring buffer is set to the minimum size, so that
|
||||
* we do not waste memory on systems that are not using tracing.
|
||||
*/
|
||||
static int ring_buffer_expanded;
|
||||
|
||||
/*
|
||||
* We need to change this state when a selftest is running.
|
||||
* A selftest will lurk into the ring-buffer to count the
|
||||
@ -128,6 +134,8 @@ static int __init set_ftrace(char *str)
|
||||
{
|
||||
strncpy(bootup_tracer_buf, str, BOOTUP_TRACER_SIZE);
|
||||
default_bootup_tracer = bootup_tracer_buf;
|
||||
/* We are using ftrace early, expand it */
|
||||
ring_buffer_expanded = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ftrace=", set_ftrace);
|
||||
@ -1171,10 +1179,10 @@ void trace_graph_return(struct ftrace_graph_ret *trace)
|
||||
|
||||
|
||||
/**
|
||||
* trace_vprintk - write binary msg to tracing buffer
|
||||
* trace_vbprintk - write binary msg to tracing buffer
|
||||
*
|
||||
*/
|
||||
int trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args)
|
||||
int trace_vbprintk(unsigned long ip, int depth, const char *fmt, va_list args)
|
||||
{
|
||||
static raw_spinlock_t trace_buf_lock =
|
||||
(raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
|
||||
@ -1183,7 +1191,7 @@ int trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args)
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_array *tr = &global_trace;
|
||||
struct trace_array_cpu *data;
|
||||
struct print_entry *entry;
|
||||
struct bprint_entry *entry;
|
||||
unsigned long flags;
|
||||
int resched;
|
||||
int cpu, len = 0, size, pc;
|
||||
@ -1211,7 +1219,7 @@ int trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args)
|
||||
goto out_unlock;
|
||||
|
||||
size = sizeof(*entry) + sizeof(u32) * len;
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_PRINT, size, flags, pc);
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_BPRINT, size, flags, pc);
|
||||
if (!event)
|
||||
goto out_unlock;
|
||||
entry = ring_buffer_event_data(event);
|
||||
@ -1232,6 +1240,60 @@ out:
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_vbprintk);
|
||||
|
||||
int trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args)
|
||||
{
|
||||
static raw_spinlock_t trace_buf_lock = __RAW_SPIN_LOCK_UNLOCKED;
|
||||
static char trace_buf[TRACE_BUF_SIZE];
|
||||
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_array *tr = &global_trace;
|
||||
struct trace_array_cpu *data;
|
||||
int cpu, len = 0, size, pc;
|
||||
struct print_entry *entry;
|
||||
unsigned long irq_flags;
|
||||
|
||||
if (tracing_disabled || tracing_selftest_running)
|
||||
return 0;
|
||||
|
||||
pc = preempt_count();
|
||||
preempt_disable_notrace();
|
||||
cpu = raw_smp_processor_id();
|
||||
data = tr->data[cpu];
|
||||
|
||||
if (unlikely(atomic_read(&data->disabled)))
|
||||
goto out;
|
||||
|
||||
pause_graph_tracing();
|
||||
raw_local_irq_save(irq_flags);
|
||||
__raw_spin_lock(&trace_buf_lock);
|
||||
len = vsnprintf(trace_buf, TRACE_BUF_SIZE, fmt, args);
|
||||
|
||||
len = min(len, TRACE_BUF_SIZE-1);
|
||||
trace_buf[len] = 0;
|
||||
|
||||
size = sizeof(*entry) + len + 1;
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_PRINT, size, irq_flags, pc);
|
||||
if (!event)
|
||||
goto out_unlock;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ip = ip;
|
||||
entry->depth = depth;
|
||||
|
||||
memcpy(&entry->buf, trace_buf, len);
|
||||
entry->buf[len] = 0;
|
||||
ring_buffer_unlock_commit(tr->buffer, event);
|
||||
|
||||
out_unlock:
|
||||
__raw_spin_unlock(&trace_buf_lock);
|
||||
raw_local_irq_restore(irq_flags);
|
||||
unpause_graph_tracing();
|
||||
out:
|
||||
preempt_enable_notrace();
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_vprintk);
|
||||
|
||||
enum trace_file_type {
|
||||
@ -1620,6 +1682,22 @@ static enum print_line_t print_hex_fmt(struct trace_iterator *iter)
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t print_bprintk_msg_only(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct bprint_entry *field;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
|
||||
ret = trace_seq_bprintf(s, field->fmt, field->buf);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t print_printk_msg_only(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
@ -1629,7 +1707,7 @@ static enum print_line_t print_printk_msg_only(struct trace_iterator *iter)
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
|
||||
ret = trace_seq_bprintf(s, field->fmt, field->buf);
|
||||
ret = trace_seq_printf(s, "%s", field->buf);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
@ -1658,6 +1736,19 @@ static int trace_empty(struct trace_iterator *iter)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
/* If we are looking at one CPU buffer, only check that one */
|
||||
if (iter->cpu_file != TRACE_PIPE_ALL_CPU) {
|
||||
cpu = iter->cpu_file;
|
||||
if (iter->buffer_iter[cpu]) {
|
||||
if (!ring_buffer_iter_empty(iter->buffer_iter[cpu]))
|
||||
return 0;
|
||||
} else {
|
||||
if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
for_each_tracing_cpu(cpu) {
|
||||
if (iter->buffer_iter[cpu]) {
|
||||
if (!ring_buffer_iter_empty(iter->buffer_iter[cpu]))
|
||||
@ -1681,6 +1772,11 @@ static enum print_line_t print_trace_line(struct trace_iterator *iter)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (iter->ent->type == TRACE_BPRINT &&
|
||||
trace_flags & TRACE_ITER_PRINTK &&
|
||||
trace_flags & TRACE_ITER_PRINTK_MSGONLY)
|
||||
return print_bprintk_msg_only(iter);
|
||||
|
||||
if (iter->ent->type == TRACE_PRINT &&
|
||||
trace_flags & TRACE_ITER_PRINTK &&
|
||||
trace_flags & TRACE_ITER_PRINTK_MSGONLY)
|
||||
@ -1784,17 +1880,11 @@ __tracing_open(struct inode *inode, struct file *file)
|
||||
|
||||
iter->buffer_iter[cpu] =
|
||||
ring_buffer_read_start(iter->tr->buffer, cpu);
|
||||
|
||||
if (!iter->buffer_iter[cpu])
|
||||
goto fail_buffer;
|
||||
}
|
||||
} else {
|
||||
cpu = iter->cpu_file;
|
||||
iter->buffer_iter[cpu] =
|
||||
ring_buffer_read_start(iter->tr->buffer, cpu);
|
||||
|
||||
if (!iter->buffer_iter[cpu])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* TODO stop tracer */
|
||||
@ -2315,6 +2405,75 @@ int tracer_init(struct tracer *t, struct trace_array *tr)
|
||||
return t->init(tr);
|
||||
}
|
||||
|
||||
static int tracing_resize_ring_buffer(unsigned long size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If kernel or user changes the size of the ring buffer
|
||||
* we use the size that was given, and we can forget about
|
||||
* expanding it later.
|
||||
*/
|
||||
ring_buffer_expanded = 1;
|
||||
|
||||
ret = ring_buffer_resize(global_trace.buffer, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ring_buffer_resize(max_tr.buffer, size);
|
||||
if (ret < 0) {
|
||||
int r;
|
||||
|
||||
r = ring_buffer_resize(global_trace.buffer,
|
||||
global_trace.entries);
|
||||
if (r < 0) {
|
||||
/*
|
||||
* AARGH! We are left with different
|
||||
* size max buffer!!!!
|
||||
* The max buffer is our "snapshot" buffer.
|
||||
* When a tracer needs a snapshot (one of the
|
||||
* latency tracers), it swaps the max buffer
|
||||
* with the saved snap shot. We succeeded to
|
||||
* update the size of the main buffer, but failed to
|
||||
* update the size of the max buffer. But when we tried
|
||||
* to reset the main buffer to the original size, we
|
||||
* failed there too. This is very unlikely to
|
||||
* happen, but if it does, warn and kill all
|
||||
* tracing.
|
||||
*/
|
||||
WARN_ON(1);
|
||||
tracing_disabled = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
global_trace.entries = size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* tracing_update_buffers - used by tracing facility to expand ring buffers
|
||||
*
|
||||
* To save on memory when the tracing is never used on a system with it
|
||||
* configured in. The ring buffers are set to a minimum size. But once
|
||||
* a user starts to use the tracing facility, then they need to grow
|
||||
* to their default size.
|
||||
*
|
||||
* This function is to be called when a tracer is about to be used.
|
||||
*/
|
||||
int tracing_update_buffers(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
if (!ring_buffer_expanded)
|
||||
ret = tracing_resize_ring_buffer(trace_buf_size);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct trace_option_dentry;
|
||||
|
||||
static struct trace_option_dentry *
|
||||
@ -2331,6 +2490,14 @@ static int tracing_set_tracer(const char *buf)
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
|
||||
if (!ring_buffer_expanded) {
|
||||
ret = tracing_resize_ring_buffer(trace_buf_size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
for (t = trace_types; t; t = t->next) {
|
||||
if (strcmp(t->name, buf) == 0)
|
||||
break;
|
||||
@ -2856,10 +3023,18 @@ tracing_entries_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct trace_array *tr = filp->private_data;
|
||||
char buf[64];
|
||||
char buf[96];
|
||||
int r;
|
||||
|
||||
r = sprintf(buf, "%lu\n", tr->entries >> 10);
|
||||
mutex_lock(&trace_types_lock);
|
||||
if (!ring_buffer_expanded)
|
||||
r = sprintf(buf, "%lu (expanded: %lu)\n",
|
||||
tr->entries >> 10,
|
||||
trace_buf_size >> 10);
|
||||
else
|
||||
r = sprintf(buf, "%lu\n", tr->entries >> 10);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||
}
|
||||
|
||||
@ -2903,28 +3078,11 @@ tracing_entries_write(struct file *filp, const char __user *ubuf,
|
||||
val <<= 10;
|
||||
|
||||
if (val != global_trace.entries) {
|
||||
ret = ring_buffer_resize(global_trace.buffer, val);
|
||||
ret = tracing_resize_ring_buffer(val);
|
||||
if (ret < 0) {
|
||||
cnt = ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ring_buffer_resize(max_tr.buffer, val);
|
||||
if (ret < 0) {
|
||||
int r;
|
||||
cnt = ret;
|
||||
r = ring_buffer_resize(global_trace.buffer,
|
||||
global_trace.entries);
|
||||
if (r < 0) {
|
||||
/* AARGH! We are left with different
|
||||
* size max buffer!!!! */
|
||||
WARN_ON(1);
|
||||
tracing_disabled = 1;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
global_trace.entries = val;
|
||||
}
|
||||
|
||||
filp->f_pos += cnt;
|
||||
@ -3916,6 +4074,7 @@ void ftrace_dump(void)
|
||||
__init static int tracer_alloc_buffers(void)
|
||||
{
|
||||
struct trace_array_cpu *data;
|
||||
int ring_buf_size;
|
||||
int i;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
@ -3928,12 +4087,18 @@ __init static int tracer_alloc_buffers(void)
|
||||
if (!alloc_cpumask_var(&tracing_reader_cpumask, GFP_KERNEL))
|
||||
goto out_free_tracing_cpumask;
|
||||
|
||||
/* To save memory, keep the ring buffer size to its minimum */
|
||||
if (ring_buffer_expanded)
|
||||
ring_buf_size = trace_buf_size;
|
||||
else
|
||||
ring_buf_size = 1;
|
||||
|
||||
cpumask_copy(tracing_buffer_mask, cpu_possible_mask);
|
||||
cpumask_copy(tracing_cpumask, cpu_all_mask);
|
||||
cpumask_clear(tracing_reader_cpumask);
|
||||
|
||||
/* TODO: make the number of buffers hot pluggable with CPUS */
|
||||
global_trace.buffer = ring_buffer_alloc(trace_buf_size,
|
||||
global_trace.buffer = ring_buffer_alloc(ring_buf_size,
|
||||
TRACE_BUFFER_FLAGS);
|
||||
if (!global_trace.buffer) {
|
||||
printk(KERN_ERR "tracer: failed to allocate ring buffer!\n");
|
||||
@ -3944,7 +4109,7 @@ __init static int tracer_alloc_buffers(void)
|
||||
|
||||
|
||||
#ifdef CONFIG_TRACER_MAX_TRACE
|
||||
max_tr.buffer = ring_buffer_alloc(trace_buf_size,
|
||||
max_tr.buffer = ring_buffer_alloc(ring_buf_size,
|
||||
TRACE_BUFFER_FLAGS);
|
||||
if (!max_tr.buffer) {
|
||||
printk(KERN_ERR "tracer: failed to allocate max ring buffer!\n");
|
||||
|
@ -20,6 +20,7 @@ enum trace_type {
|
||||
TRACE_WAKE,
|
||||
TRACE_STACK,
|
||||
TRACE_PRINT,
|
||||
TRACE_BPRINT,
|
||||
TRACE_SPECIAL,
|
||||
TRACE_MMIO_RW,
|
||||
TRACE_MMIO_MAP,
|
||||
@ -117,7 +118,7 @@ struct userstack_entry {
|
||||
/*
|
||||
* trace_printk entry:
|
||||
*/
|
||||
struct print_entry {
|
||||
struct bprint_entry {
|
||||
struct trace_entry ent;
|
||||
unsigned long ip;
|
||||
int depth;
|
||||
@ -125,6 +126,13 @@ struct print_entry {
|
||||
u32 buf[];
|
||||
};
|
||||
|
||||
struct print_entry {
|
||||
struct trace_entry ent;
|
||||
unsigned long ip;
|
||||
int depth;
|
||||
char buf[];
|
||||
};
|
||||
|
||||
#define TRACE_OLD_SIZE 88
|
||||
|
||||
struct trace_field_cont {
|
||||
@ -286,6 +294,7 @@ extern void __ftrace_bad_type(void);
|
||||
IF_ASSIGN(var, ent, struct stack_entry, TRACE_STACK); \
|
||||
IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\
|
||||
IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \
|
||||
IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \
|
||||
IF_ASSIGN(var, ent, struct special_entry, 0); \
|
||||
IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \
|
||||
TRACE_MMIO_RW); \
|
||||
@ -570,6 +579,8 @@ extern int trace_selftest_startup_branch(struct tracer *trace,
|
||||
extern void *head_page(struct trace_array_cpu *data);
|
||||
extern long ns2usecs(cycle_t nsec);
|
||||
extern int
|
||||
trace_vbprintk(unsigned long ip, int depth, const char *fmt, va_list args);
|
||||
extern int
|
||||
trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args);
|
||||
|
||||
extern unsigned long trace_flags;
|
||||
@ -737,6 +748,9 @@ static inline void trace_branch_disable(void)
|
||||
}
|
||||
#endif /* CONFIG_BRANCH_TRACER */
|
||||
|
||||
/* set ring buffers to default size if not already done so */
|
||||
int tracing_update_buffers(void);
|
||||
|
||||
/* trace event type bit fields, not numeric */
|
||||
enum {
|
||||
TRACE_EVENT_TYPE_PRINTF = 1,
|
||||
@ -759,4 +773,21 @@ void event_trace_printk(unsigned long ip, const char *fmt, ...);
|
||||
extern struct ftrace_event_call __start_ftrace_events[];
|
||||
extern struct ftrace_event_call __stop_ftrace_events[];
|
||||
|
||||
extern const char *__start___trace_bprintk_fmt[];
|
||||
extern const char *__stop___trace_bprintk_fmt[];
|
||||
|
||||
#define event_trace_printk(ip, fmt, args...) \
|
||||
do { \
|
||||
__trace_printk_check_format(fmt, ##args); \
|
||||
tracing_record_cmdline(current); \
|
||||
if (__builtin_constant_p(fmt)) { \
|
||||
static const char *trace_printk_fmt \
|
||||
__attribute__((section("__trace_printk_fmt"))) = \
|
||||
__builtin_constant_p(fmt) ? fmt : NULL; \
|
||||
\
|
||||
__trace_bprintk(ip, trace_printk_fmt, ##args); \
|
||||
} else \
|
||||
__trace_printk(ip, fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _LINUX_KERNEL_TRACE_H */
|
||||
|
@ -102,7 +102,7 @@ TRACE_EVENT_FORMAT(user_stack, TRACE_USER_STACK, userstack_entry, ignore,
|
||||
"\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n")
|
||||
);
|
||||
|
||||
TRACE_EVENT_FORMAT(print, TRACE_PRINT, print_entry, ignore,
|
||||
TRACE_EVENT_FORMAT(bprint, TRACE_BPRINT, bprint_entry, ignore,
|
||||
TRACE_STRUCT(
|
||||
TRACE_FIELD(unsigned long, ip, ip)
|
||||
TRACE_FIELD(unsigned int, depth, depth)
|
||||
@ -112,6 +112,15 @@ TRACE_EVENT_FORMAT(print, TRACE_PRINT, print_entry, ignore,
|
||||
TP_RAW_FMT("%08lx (%d) fmt:%p %s")
|
||||
);
|
||||
|
||||
TRACE_EVENT_FORMAT(print, TRACE_PRINT, print_entry, ignore,
|
||||
TRACE_STRUCT(
|
||||
TRACE_FIELD(unsigned long, ip, ip)
|
||||
TRACE_FIELD(unsigned int, depth, depth)
|
||||
TRACE_FIELD_ZERO_CHAR(buf)
|
||||
),
|
||||
TP_RAW_FMT("%08lx (%d) fmt:%p %s")
|
||||
);
|
||||
|
||||
TRACE_EVENT_FORMAT(branch, TRACE_BRANCH, trace_branch, ignore,
|
||||
TRACE_STRUCT(
|
||||
TRACE_FIELD(unsigned int, line, line)
|
||||
|
@ -24,16 +24,6 @@ static DEFINE_MUTEX(event_mutex);
|
||||
(unsigned long)event < (unsigned long)__stop_ftrace_events; \
|
||||
event++)
|
||||
|
||||
void event_trace_printk(unsigned long ip, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
tracing_record_cmdline(current);
|
||||
trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void ftrace_clear_events(void)
|
||||
{
|
||||
struct ftrace_event_call *call = (void *)__start_ftrace_events;
|
||||
@ -141,6 +131,10 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
|
||||
if (!cnt || cnt < 0)
|
||||
return 0;
|
||||
|
||||
ret = tracing_update_buffers();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = get_user(ch, ubuf++);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -331,6 +325,10 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tracing_update_buffers();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
case 1:
|
||||
|
@ -57,7 +57,7 @@ ftrace_raw_output_##call(struct trace_iterator *iter, int flags) \
|
||||
\
|
||||
field = (typeof(field))entry; \
|
||||
\
|
||||
ret = trace_seq_printf(s, print); \
|
||||
ret = trace_seq_printf(s, #call ": " print); \
|
||||
if (!ret) \
|
||||
return TRACE_TYPE_PARTIAL_LINE; \
|
||||
\
|
||||
|
@ -684,7 +684,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_comment(struct print_entry *trace, struct trace_seq *s,
|
||||
print_graph_comment(struct bprint_entry *trace, struct trace_seq *s,
|
||||
struct trace_entry *ent, struct trace_iterator *iter)
|
||||
{
|
||||
int i;
|
||||
@ -781,8 +781,8 @@ print_graph_function(struct trace_iterator *iter)
|
||||
trace_assign_type(field, entry);
|
||||
return print_graph_return(&field->ret, s, entry, iter);
|
||||
}
|
||||
case TRACE_PRINT: {
|
||||
struct print_entry *field;
|
||||
case TRACE_BPRINT: {
|
||||
struct bprint_entry *field;
|
||||
trace_assign_type(field, entry);
|
||||
return print_graph_comment(field, s, entry, iter);
|
||||
}
|
||||
|
@ -254,6 +254,7 @@ static enum print_line_t mmio_print_mark(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct print_entry *print = (struct print_entry *)entry;
|
||||
const char *msg = print->buf;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
unsigned long long t = ns2usecs(iter->ts);
|
||||
unsigned long usec_rem = do_div(t, USEC_PER_SEC);
|
||||
@ -261,11 +262,7 @@ static enum print_line_t mmio_print_mark(struct trace_iterator *iter)
|
||||
int ret;
|
||||
|
||||
/* The trailing newline must be in the message. */
|
||||
ret = trace_seq_printf(s, "MARK %u.%06lu ", secs, usec_rem);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_bprintf(s, print->fmt, print->buf);
|
||||
ret = trace_seq_printf(s, "MARK %u.%06lu %s", secs, usec_rem, msg);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
|
@ -832,13 +832,13 @@ static struct trace_event trace_user_stack_event = {
|
||||
.binary = trace_special_bin,
|
||||
};
|
||||
|
||||
/* TRACE_PRINT */
|
||||
/* TRACE_BPRINT */
|
||||
static enum print_line_t
|
||||
trace_print_print(struct trace_iterator *iter, int flags)
|
||||
trace_bprint_print(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct print_entry *field;
|
||||
struct bprint_entry *field;
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
|
||||
@ -858,9 +858,10 @@ trace_print_print(struct trace_iterator *iter, int flags)
|
||||
}
|
||||
|
||||
|
||||
static enum print_line_t trace_print_raw(struct trace_iterator *iter, int flags)
|
||||
static enum print_line_t
|
||||
trace_bprint_raw(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct print_entry *field;
|
||||
struct bprint_entry *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
@ -878,12 +879,55 @@ static enum print_line_t trace_print_raw(struct trace_iterator *iter, int flags)
|
||||
}
|
||||
|
||||
|
||||
static struct trace_event trace_bprint_event = {
|
||||
.type = TRACE_BPRINT,
|
||||
.trace = trace_bprint_print,
|
||||
.raw = trace_bprint_raw,
|
||||
};
|
||||
|
||||
/* TRACE_PRINT */
|
||||
static enum print_line_t trace_print_print(struct trace_iterator *iter,
|
||||
int flags)
|
||||
{
|
||||
struct print_entry *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
if (!seq_print_ip_sym(s, field->ip, flags))
|
||||
goto partial;
|
||||
|
||||
if (!trace_seq_printf(s, ": %s", field->buf))
|
||||
goto partial;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
|
||||
partial:
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
static enum print_line_t trace_print_raw(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct print_entry *field;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
if (!trace_seq_printf(&iter->seq, "# %lx %s", field->ip, field->buf))
|
||||
goto partial;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
|
||||
partial:
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
static struct trace_event trace_print_event = {
|
||||
.type = TRACE_PRINT,
|
||||
.type = TRACE_PRINT,
|
||||
.trace = trace_print_print,
|
||||
.raw = trace_print_raw,
|
||||
};
|
||||
|
||||
|
||||
static struct trace_event *events[] __initdata = {
|
||||
&trace_fn_event,
|
||||
&trace_ctx_event,
|
||||
@ -891,6 +935,7 @@ static struct trace_event *events[] __initdata = {
|
||||
&trace_special_event,
|
||||
&trace_stack_event,
|
||||
&trace_user_stack_event,
|
||||
&trace_bprint_event,
|
||||
&trace_print_event,
|
||||
NULL
|
||||
};
|
||||
|
@ -4,18 +4,19 @@
|
||||
* Copyright (C) 2008 Lai Jiangshan <laijs@cn.fujitsu.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/marker.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/marker.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
@ -99,7 +100,7 @@ struct notifier_block module_trace_bprintk_format_nb = {
|
||||
.notifier_call = module_trace_bprintk_format_notify,
|
||||
};
|
||||
|
||||
int __trace_printk(unsigned long ip, const char *fmt, ...)
|
||||
int __trace_bprintk(unsigned long ip, const char *fmt, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list ap;
|
||||
@ -107,6 +108,33 @@ int __trace_printk(unsigned long ip, const char *fmt, ...)
|
||||
if (unlikely(!fmt))
|
||||
return 0;
|
||||
|
||||
if (!(trace_flags & TRACE_ITER_PRINTK))
|
||||
return 0;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = trace_vbprintk(ip, task_curr_ret_stack(current), fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__trace_bprintk);
|
||||
|
||||
int __ftrace_vbprintk(unsigned long ip, const char *fmt, va_list ap)
|
||||
{
|
||||
if (unlikely(!fmt))
|
||||
return 0;
|
||||
|
||||
if (!(trace_flags & TRACE_ITER_PRINTK))
|
||||
return 0;
|
||||
|
||||
return trace_vbprintk(ip, task_curr_ret_stack(current), fmt, ap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__ftrace_vbprintk);
|
||||
|
||||
int __trace_printk(unsigned long ip, const char *fmt, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
if (!(trace_flags & TRACE_ITER_PRINTK))
|
||||
return 0;
|
||||
|
||||
@ -118,10 +146,7 @@ int __trace_printk(unsigned long ip, const char *fmt, ...)
|
||||
EXPORT_SYMBOL_GPL(__trace_printk);
|
||||
|
||||
int __ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap)
|
||||
{
|
||||
if (unlikely(!fmt))
|
||||
return 0;
|
||||
|
||||
{
|
||||
if (!(trace_flags & TRACE_ITER_PRINTK))
|
||||
return 0;
|
||||
|
||||
@ -129,6 +154,113 @@ int __ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__ftrace_vprintk);
|
||||
|
||||
static void *
|
||||
t_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
const char **fmt = m->private;
|
||||
const char **next = fmt;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if ((unsigned long)fmt >= (unsigned long)__stop___trace_bprintk_fmt)
|
||||
return NULL;
|
||||
|
||||
next = fmt;
|
||||
m->private = ++next;
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
static void *t_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return t_next(m, NULL, pos);
|
||||
}
|
||||
|
||||
static int t_show(struct seq_file *m, void *v)
|
||||
{
|
||||
const char **fmt = v;
|
||||
const char *str = *fmt;
|
||||
int i;
|
||||
|
||||
seq_printf(m, "0x%lx : \"", (unsigned long)fmt);
|
||||
|
||||
/*
|
||||
* Tabs and new lines need to be converted.
|
||||
*/
|
||||
for (i = 0; str[i]; i++) {
|
||||
switch (str[i]) {
|
||||
case '\n':
|
||||
seq_puts(m, "\\n");
|
||||
break;
|
||||
case '\t':
|
||||
seq_puts(m, "\\t");
|
||||
break;
|
||||
case '\\':
|
||||
seq_puts(m, "\\");
|
||||
break;
|
||||
case '"':
|
||||
seq_puts(m, "\\\"");
|
||||
break;
|
||||
default:
|
||||
seq_putc(m, str[i]);
|
||||
}
|
||||
}
|
||||
seq_puts(m, "\"\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void t_stop(struct seq_file *m, void *p)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct seq_operations show_format_seq_ops = {
|
||||
.start = t_start,
|
||||
.next = t_next,
|
||||
.show = t_show,
|
||||
.stop = t_stop,
|
||||
};
|
||||
|
||||
static int
|
||||
ftrace_formats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &show_format_seq_ops);
|
||||
if (!ret) {
|
||||
struct seq_file *m = file->private_data;
|
||||
|
||||
m->private = __start___trace_bprintk_fmt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations ftrace_formats_fops = {
|
||||
.open = ftrace_formats_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static __init int init_trace_printk_function_export(void)
|
||||
{
|
||||
struct dentry *d_tracer;
|
||||
struct dentry *entry;
|
||||
|
||||
d_tracer = tracing_init_dentry();
|
||||
if (!d_tracer)
|
||||
return 0;
|
||||
|
||||
entry = debugfs_create_file("printk_formats", 0444, d_tracer,
|
||||
NULL, &ftrace_formats_fops);
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs "
|
||||
"'printk_formats' entry\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs_initcall(init_trace_printk_function_export);
|
||||
|
||||
static __init int init_trace_printk(void)
|
||||
{
|
||||
|
@ -245,16 +245,31 @@ static int trace_lookup_stack(struct seq_file *m, long i)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void print_disabled(struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, "#\n"
|
||||
"# Stack tracer disabled\n"
|
||||
"#\n"
|
||||
"# To enable the stack tracer, either add 'stacktrace' to the\n"
|
||||
"# kernel command line\n"
|
||||
"# or 'echo 1 > /proc/sys/kernel/stack_tracer_enabled'\n"
|
||||
"#\n");
|
||||
}
|
||||
|
||||
static int t_show(struct seq_file *m, void *v)
|
||||
{
|
||||
long i;
|
||||
int size;
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
seq_printf(m, " Depth Size Location"
|
||||
seq_printf(m, " Depth Size Location"
|
||||
" (%d entries)\n"
|
||||
" ----- ---- --------\n",
|
||||
" ----- ---- --------\n",
|
||||
max_stack_trace.nr_entries);
|
||||
|
||||
if (!stack_tracer_enabled && !max_stack_size)
|
||||
print_disabled(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -193,12 +193,20 @@ static int workqueue_stat_show(struct seq_file *s, void *p)
|
||||
struct cpu_workqueue_stats *cws = p;
|
||||
unsigned long flags;
|
||||
int cpu = cws->cpu;
|
||||
struct task_struct *tsk = find_task_by_vpid(cws->pid);
|
||||
struct pid *pid;
|
||||
struct task_struct *tsk;
|
||||
|
||||
seq_printf(s, "%3d %6d %6u %s\n", cws->cpu,
|
||||
atomic_read(&cws->inserted),
|
||||
cws->executed,
|
||||
tsk ? tsk->comm : "<...>");
|
||||
pid = find_get_pid(cws->pid);
|
||||
if (pid) {
|
||||
tsk = get_pid_task(pid, PIDTYPE_PID);
|
||||
if (tsk) {
|
||||
seq_printf(s, "%3d %6d %6u %s\n", cws->cpu,
|
||||
atomic_read(&cws->inserted), cws->executed,
|
||||
tsk->comm);
|
||||
put_task_struct(tsk);
|
||||
}
|
||||
put_pid(pid);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags);
|
||||
if (&cws->list == workqueue_cpu_stat(cpu)->list.next)
|
||||
|
Loading…
Reference in New Issue
Block a user