]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branches 'tracing/kmemtrace2' and 'tracing/ftrace' into tracing/urgent
authorIngo Molnar <mingo@elte.hu>
Tue, 6 Jan 2009 09:18:43 +0000 (10:18 +0100)
committerIngo Molnar <mingo@elte.hu>
Tue, 6 Jan 2009 09:18:43 +0000 (10:18 +0100)
13 files changed:
kernel/trace/Makefile
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_boot.c
kernel/trace/trace_branch.c
kernel/trace/trace_functions_graph.c
kernel/trace/trace_hw_branches.c
kernel/trace/trace_mmiotrace.c
kernel/trace/trace_output.c [new file with mode: 0644]
kernel/trace/trace_output.h [new file with mode: 0644]
kernel/trace/trace_power.c
kernel/trace/trace_selftest.c
kernel/trace/trace_stat.c [new file with mode: 0644]

index 513dc86b5dfabb3fc4969257804dde99e60edc7f..05c9182061dece9b818a8574016d58429fb65162 100644 (file)
@@ -19,6 +19,8 @@ obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o
 obj-$(CONFIG_RING_BUFFER) += ring_buffer.o
 
 obj-$(CONFIG_TRACING) += trace.o
+obj-$(CONFIG_TRACING) += trace_output.o
+obj-$(CONFIG_TRACING) += trace_stat.o
 obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
 obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o
 obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o
index c580233add95a0915f4797b84ae05a728aa986c6..0418fc338b5c2d2d36cdc2987e7e7d4666126929 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/irqflags.h>
 
 #include "trace.h"
+#include "trace_output.h"
 
 #define TRACE_BUFFER_FLAGS     (RB_FL_OVERWRITE)
 
@@ -329,132 +330,6 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
        tracing_record_cmdline(current);
 }
 
-/**
- * trace_seq_printf - sequence printing of trace information
- * @s: trace sequence descriptor
- * @fmt: printf format string
- *
- * The tracer may use either sequence operations or its own
- * copy to user routines. To simplify formating of a trace
- * trace_seq_printf is used to store strings into a special
- * buffer (@s). Then the output may be either used by
- * the sequencer or pulled into another buffer.
- */
-int
-trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
-{
-       int len = (PAGE_SIZE - 1) - s->len;
-       va_list ap;
-       int ret;
-
-       if (!len)
-               return 0;
-
-       va_start(ap, fmt);
-       ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
-       va_end(ap);
-
-       /* If we can't write it all, don't bother writing anything */
-       if (ret >= len)
-               return 0;
-
-       s->len += ret;
-
-       return len;
-}
-
-/**
- * trace_seq_puts - trace sequence printing of simple string
- * @s: trace sequence descriptor
- * @str: simple string to record
- *
- * The tracer may use either the sequence operations or its own
- * copy to user routines. This function records a simple string
- * into a special buffer (@s) for later retrieval by a sequencer
- * or other mechanism.
- */
-static int
-trace_seq_puts(struct trace_seq *s, const char *str)
-{
-       int len = strlen(str);
-
-       if (len > ((PAGE_SIZE - 1) - s->len))
-               return 0;
-
-       memcpy(s->buffer + s->len, str, len);
-       s->len += len;
-
-       return len;
-}
-
-static int
-trace_seq_putc(struct trace_seq *s, unsigned char c)
-{
-       if (s->len >= (PAGE_SIZE - 1))
-               return 0;
-
-       s->buffer[s->len++] = c;
-
-       return 1;
-}
-
-static int
-trace_seq_putmem(struct trace_seq *s, void *mem, size_t len)
-{
-       if (len > ((PAGE_SIZE - 1) - s->len))
-               return 0;
-
-       memcpy(s->buffer + s->len, mem, len);
-       s->len += len;
-
-       return len;
-}
-
-#define MAX_MEMHEX_BYTES       8
-#define HEX_CHARS              (MAX_MEMHEX_BYTES*2 + 1)
-
-static int
-trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len)
-{
-       unsigned char hex[HEX_CHARS];
-       unsigned char *data = mem;
-       int i, j;
-
-#ifdef __BIG_ENDIAN
-       for (i = 0, j = 0; i < len; i++) {
-#else
-       for (i = len-1, j = 0; i >= 0; i--) {
-#endif
-               hex[j++] = hex_asc_hi(data[i]);
-               hex[j++] = hex_asc_lo(data[i]);
-       }
-       hex[j++] = ' ';
-
-       return trace_seq_putmem(s, hex, j);
-}
-
-static int
-trace_seq_path(struct trace_seq *s, struct path *path)
-{
-       unsigned char *p;
-
-       if (s->len >= (PAGE_SIZE - 1))
-               return 0;
-       p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
-       if (!IS_ERR(p)) {
-               p = mangle_path(s->buffer + s->len, p, "\n");
-               if (p) {
-                       s->len = p - s->buffer;
-                       return 1;
-               }
-       } else {
-               s->buffer[s->len++] = '?';
-               return 1;
-       }
-
-       return 0;
-}
-
 static void
 trace_seq_reset(struct trace_seq *s)
 {
@@ -1472,154 +1347,6 @@ static void s_stop(struct seq_file *m, void *p)
        mutex_unlock(&trace_types_lock);
 }
 
-#ifdef CONFIG_KRETPROBES
-static inline const char *kretprobed(const char *name)
-{
-       static const char tramp_name[] = "kretprobe_trampoline";
-       int size = sizeof(tramp_name);
-
-       if (strncmp(tramp_name, name, size) == 0)
-               return "[unknown/kretprobe'd]";
-       return name;
-}
-#else
-static inline const char *kretprobed(const char *name)
-{
-       return name;
-}
-#endif /* CONFIG_KRETPROBES */
-
-static int
-seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
-{
-#ifdef CONFIG_KALLSYMS
-       char str[KSYM_SYMBOL_LEN];
-       const char *name;
-
-       kallsyms_lookup(address, NULL, NULL, NULL, str);
-
-       name = kretprobed(str);
-
-       return trace_seq_printf(s, fmt, name);
-#endif
-       return 1;
-}
-
-static int
-seq_print_sym_offset(struct trace_seq *s, const char *fmt,
-                    unsigned long address)
-{
-#ifdef CONFIG_KALLSYMS
-       char str[KSYM_SYMBOL_LEN];
-       const char *name;
-
-       sprint_symbol(str, address);
-       name = kretprobed(str);
-
-       return trace_seq_printf(s, fmt, name);
-#endif
-       return 1;
-}
-
-#ifndef CONFIG_64BIT
-# define IP_FMT "%08lx"
-#else
-# define IP_FMT "%016lx"
-#endif
-
-int
-seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
-{
-       int ret;
-
-       if (!ip)
-               return trace_seq_printf(s, "0");
-
-       if (sym_flags & TRACE_ITER_SYM_OFFSET)
-               ret = seq_print_sym_offset(s, "%s", ip);
-       else
-               ret = seq_print_sym_short(s, "%s", ip);
-
-       if (!ret)
-               return 0;
-
-       if (sym_flags & TRACE_ITER_SYM_ADDR)
-               ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
-       return ret;
-}
-
-static inline int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
-                                   unsigned long ip, unsigned long sym_flags)
-{
-       struct file *file = NULL;
-       unsigned long vmstart = 0;
-       int ret = 1;
-
-       if (mm) {
-               const struct vm_area_struct *vma;
-
-               down_read(&mm->mmap_sem);
-               vma = find_vma(mm, ip);
-               if (vma) {
-                       file = vma->vm_file;
-                       vmstart = vma->vm_start;
-               }
-               if (file) {
-                       ret = trace_seq_path(s, &file->f_path);
-                       if (ret)
-                               ret = trace_seq_printf(s, "[+0x%lx]", ip - vmstart);
-               }
-               up_read(&mm->mmap_sem);
-       }
-       if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file))
-               ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
-       return ret;
-}
-
-static int
-seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s,
-                     unsigned long sym_flags)
-{
-       struct mm_struct *mm = NULL;
-       int ret = 1;
-       unsigned int i;
-
-       if (trace_flags & TRACE_ITER_SYM_USEROBJ) {
-               struct task_struct *task;
-               /*
-                * we do the lookup on the thread group leader,
-                * since individual threads might have already quit!
-                */
-               rcu_read_lock();
-               task = find_task_by_vpid(entry->ent.tgid);
-               if (task)
-                       mm = get_task_mm(task);
-               rcu_read_unlock();
-       }
-
-       for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
-               unsigned long ip = entry->caller[i];
-
-               if (ip == ULONG_MAX || !ret)
-                       break;
-               if (i && ret)
-                       ret = trace_seq_puts(s, " <- ");
-               if (!ip) {
-                       if (ret)
-                               ret = trace_seq_puts(s, "??");
-                       continue;
-               }
-               if (!ret)
-                       break;
-               if (ret)
-                       ret = seq_print_user_ip(s, mm, ip, sym_flags);
-       }
-
-       if (mm)
-               mmput(mm);
-       return ret;
-}
-
 static void print_lat_help_header(struct seq_file *m)
 {
        seq_puts(m, "#                  _------=> CPU#            \n");
@@ -1755,52 +1482,6 @@ lat_print_timestamp(struct trace_seq *s, u64 abs_usecs,
                trace_seq_puts(s, " : ");
 }
 
-static const char state_to_char[] = TASK_STATE_TO_CHAR_STR;
-
-static int task_state_char(unsigned long state)
-{
-       int bit = state ? __ffs(state) + 1 : 0;
-
-       return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?';
-}
-
-/*
- * The message is supposed to contain an ending newline.
- * If the printing stops prematurely, try to add a newline of our own.
- */
-void trace_seq_print_cont(struct trace_seq *s, struct trace_iterator *iter)
-{
-       struct trace_entry *ent;
-       struct trace_field_cont *cont;
-       bool ok = true;
-
-       ent = peek_next_entry(iter, iter->cpu, NULL);
-       if (!ent || ent->type != TRACE_CONT) {
-               trace_seq_putc(s, '\n');
-               return;
-       }
-
-       do {
-               cont = (struct trace_field_cont *)ent;
-               if (ok)
-                       ok = (trace_seq_printf(s, "%s", cont->buf) > 0);
-
-               ftrace_disable_cpu();
-
-               if (iter->buffer_iter[iter->cpu])
-                       ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
-               else
-                       ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL);
-
-               ftrace_enable_cpu();
-
-               ent = peek_next_entry(iter, iter->cpu, NULL);
-       } while (ent && ent->type == TRACE_CONT);
-
-       if (!ok)
-               trace_seq_putc(s, '\n');
-}
-
 static void test_cpu_buff_start(struct trace_iterator *iter)
 {
        struct trace_seq *s = &iter->seq;
@@ -1824,17 +1505,14 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
        struct trace_seq *s = &iter->seq;
        unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK);
        struct trace_entry *next_entry;
+       struct trace_event *event;
        unsigned long verbose = (trace_flags & TRACE_ITER_VERBOSE);
        struct trace_entry *entry = iter->ent;
        unsigned long abs_usecs;
        unsigned long rel_usecs;
        u64 next_ts;
        char *comm;
-       int S, T;
-       int i;
-
-       if (entry->type == TRACE_CONT)
-               return TRACE_TYPE_HANDLED;
+       int ret;
 
        test_cpu_buff_start(iter);
 
@@ -1859,96 +1537,16 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
                lat_print_generic(s, entry, cpu);
                lat_print_timestamp(s, abs_usecs, rel_usecs);
        }
-       switch (entry->type) {
-       case TRACE_FN: {
-               struct ftrace_entry *field;
-
-               trace_assign_type(field, entry);
-
-               seq_print_ip_sym(s, field->ip, sym_flags);
-               trace_seq_puts(s, " (");
-               seq_print_ip_sym(s, field->parent_ip, sym_flags);
-               trace_seq_puts(s, ")\n");
-               break;
-       }
-       case TRACE_CTX:
-       case TRACE_WAKE: {
-               struct ctx_switch_entry *field;
-
-               trace_assign_type(field, entry);
-
-               T = task_state_char(field->next_state);
-               S = task_state_char(field->prev_state);
-               comm = trace_find_cmdline(field->next_pid);
-               trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c %s\n",
-                                field->prev_pid,
-                                field->prev_prio,
-                                S, entry->type == TRACE_CTX ? "==>" : "  +",
-                                field->next_cpu,
-                                field->next_pid,
-                                field->next_prio,
-                                T, comm);
-               break;
-       }
-       case TRACE_SPECIAL: {
-               struct special_entry *field;
-
-               trace_assign_type(field, entry);
-
-               trace_seq_printf(s, "# %ld %ld %ld\n",
-                                field->arg1,
-                                field->arg2,
-                                field->arg3);
-               break;
-       }
-       case TRACE_STACK: {
-               struct stack_entry *field;
-
-               trace_assign_type(field, entry);
-
-               for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
-                       if (i)
-                               trace_seq_puts(s, " <= ");
-                       seq_print_ip_sym(s, field->caller[i], sym_flags);
-               }
-               trace_seq_puts(s, "\n");
-               break;
-       }
-       case TRACE_PRINT: {
-               struct print_entry *field;
-
-               trace_assign_type(field, entry);
 
-               seq_print_ip_sym(s, field->ip, sym_flags);
-               trace_seq_printf(s, ": %s", field->buf);
-               if (entry->flags & TRACE_FLAG_CONT)
-                       trace_seq_print_cont(s, iter);
-               break;
-       }
-       case TRACE_BRANCH: {
-               struct trace_branch *field;
-
-               trace_assign_type(field, entry);
-
-               trace_seq_printf(s, "[%s] %s:%s:%d\n",
-                                field->correct ? "  ok  " : " MISS ",
-                                field->func,
-                                field->file,
-                                field->line);
-               break;
+       event = ftrace_find_event(entry->type);
+       if (event && event->latency_trace) {
+               ret = event->latency_trace(s, entry, sym_flags);
+               if (ret)
+                       return ret;
+               return TRACE_TYPE_HANDLED;
        }
-       case TRACE_USER_STACK: {
-               struct userstack_entry *field;
-
-               trace_assign_type(field, entry);
 
-               seq_print_userip_objs(field, s, sym_flags);
-               trace_seq_putc(s, '\n');
-               break;
-       }
-       default:
-               trace_seq_printf(s, "Unknown type %d\n", entry->type);
-       }
+       trace_seq_printf(s, "Unknown type %d\n", entry->type);
        return TRACE_TYPE_HANDLED;
 }
 
@@ -1957,19 +1555,15 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
        struct trace_seq *s = &iter->seq;
        unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK);
        struct trace_entry *entry;
+       struct trace_event *event;
        unsigned long usec_rem;
        unsigned long long t;
        unsigned long secs;
        char *comm;
        int ret;
-       int S, T;
-       int i;
 
        entry = iter->ent;
 
-       if (entry->type == TRACE_CONT)
-               return TRACE_TYPE_HANDLED;
-
        test_cpu_buff_start(iter);
 
        comm = trace_find_cmdline(iter->ent->pid);
@@ -1988,129 +1582,17 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
-       switch (entry->type) {
-       case TRACE_FN: {
-               struct ftrace_entry *field;
-
-               trace_assign_type(field, entry);
-
-               ret = seq_print_ip_sym(s, field->ip, sym_flags);
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               if ((sym_flags & TRACE_ITER_PRINT_PARENT) &&
-                                               field->parent_ip) {
-                       ret = trace_seq_printf(s, " <-");
-                       if (!ret)
-                               return TRACE_TYPE_PARTIAL_LINE;
-                       ret = seq_print_ip_sym(s,
-                                              field->parent_ip,
-                                              sym_flags);
-                       if (!ret)
-                               return TRACE_TYPE_PARTIAL_LINE;
-               }
-               ret = trace_seq_printf(s, "\n");
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
-       }
-       case TRACE_CTX:
-       case TRACE_WAKE: {
-               struct ctx_switch_entry *field;
-
-               trace_assign_type(field, entry);
-
-               T = task_state_char(field->next_state);
-               S = task_state_char(field->prev_state);
-               ret = trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c\n",
-                                      field->prev_pid,
-                                      field->prev_prio,
-                                      S,
-                                      entry->type == TRACE_CTX ? "==>" : "  +",
-                                      field->next_cpu,
-                                      field->next_pid,
-                                      field->next_prio,
-                                      T);
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
-       }
-       case TRACE_SPECIAL: {
-               struct special_entry *field;
-
-               trace_assign_type(field, entry);
-
-               ret = trace_seq_printf(s, "# %ld %ld %ld\n",
-                                field->arg1,
-                                field->arg2,
-                                field->arg3);
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
-       }
-       case TRACE_STACK: {
-               struct stack_entry *field;
-
-               trace_assign_type(field, entry);
-
-               for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
-                       if (i) {
-                               ret = trace_seq_puts(s, " <= ");
-                               if (!ret)
-                                       return TRACE_TYPE_PARTIAL_LINE;
-                       }
-                       ret = seq_print_ip_sym(s, field->caller[i],
-                                              sym_flags);
-                       if (!ret)
-                               return TRACE_TYPE_PARTIAL_LINE;
-               }
-               ret = trace_seq_puts(s, "\n");
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
-       }
-       case TRACE_PRINT: {
-               struct print_entry *field;
-
-               trace_assign_type(field, entry);
-
-               seq_print_ip_sym(s, field->ip, sym_flags);
-               trace_seq_printf(s, ": %s", field->buf);
-               if (entry->flags & TRACE_FLAG_CONT)
-                       trace_seq_print_cont(s, iter);
-               break;
-       }
-       case TRACE_GRAPH_RET: {
-               return print_graph_function(iter);
-       }
-       case TRACE_GRAPH_ENT: {
-               return print_graph_function(iter);
-       }
-       case TRACE_BRANCH: {
-               struct trace_branch *field;
-
-               trace_assign_type(field, entry);
-
-               trace_seq_printf(s, "[%s] %s:%s:%d\n",
-                                field->correct ? "  ok  " : " MISS ",
-                                field->func,
-                                field->file,
-                                field->line);
-               break;
+       event = ftrace_find_event(entry->type);
+       if (event && event->trace) {
+               ret = event->trace(s, entry, sym_flags);
+               if (ret)
+                       return ret;
+               return TRACE_TYPE_HANDLED;
        }
-       case TRACE_USER_STACK: {
-               struct userstack_entry *field;
-
-               trace_assign_type(field, entry);
+       ret = trace_seq_printf(s, "Unknown type %d\n", entry->type);
+       if (!ret)
+               return TRACE_TYPE_PARTIAL_LINE;
 
-               ret = seq_print_userip_objs(field, s, sym_flags);
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               ret = trace_seq_putc(s, '\n');
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
-       }
-       }
        return TRACE_TYPE_HANDLED;
 }
 
@@ -2118,152 +1600,47 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter)
 {
        struct trace_seq *s = &iter->seq;
        struct trace_entry *entry;
+       struct trace_event *event;
        int ret;
-       int S, T;
 
        entry = iter->ent;
 
-       if (entry->type == TRACE_CONT)
-               return TRACE_TYPE_HANDLED;
-
        ret = trace_seq_printf(s, "%d %d %llu ",
                entry->pid, iter->cpu, iter->ts);
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
-       switch (entry->type) {
-       case TRACE_FN: {
-               struct ftrace_entry *field;
-
-               trace_assign_type(field, entry);
-
-               ret = trace_seq_printf(s, "%x %x\n",
-                                       field->ip,
-                                       field->parent_ip);
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
-       }
-       case TRACE_CTX:
-       case TRACE_WAKE: {
-               struct ctx_switch_entry *field;
-
-               trace_assign_type(field, entry);
-
-               T = task_state_char(field->next_state);
-               S = entry->type == TRACE_WAKE ? '+' :
-                       task_state_char(field->prev_state);
-               ret = trace_seq_printf(s, "%d %d %c %d %d %d %c\n",
-                                      field->prev_pid,
-                                      field->prev_prio,
-                                      S,
-                                      field->next_cpu,
-                                      field->next_pid,
-                                      field->next_prio,
-                                      T);
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
-       }
-       case TRACE_SPECIAL:
-       case TRACE_USER_STACK:
-       case TRACE_STACK: {
-               struct special_entry *field;
-
-               trace_assign_type(field, entry);
-
-               ret = trace_seq_printf(s, "# %ld %ld %ld\n",
-                                field->arg1,
-                                field->arg2,
-                                field->arg3);
-               if (!ret)
-                       return TRACE_TYPE_PARTIAL_LINE;
-               break;
+       event = ftrace_find_event(entry->type);
+       if (event && event->raw) {
+               ret = event->raw(s, entry, 0);
+               if (ret)
+                       return ret;
+               return TRACE_TYPE_HANDLED;
        }
-       case TRACE_PRINT: {
-               struct print_entry *field;
-
-               trace_assign_type(field, entry);
+       ret = trace_seq_printf(s, "%d ?\n", entry->type);
+       if (!ret)
+               return TRACE_TYPE_PARTIAL_LINE;
 
-               trace_seq_printf(s, "# %lx %s", field->ip, field->buf);
-               if (entry->flags & TRACE_FLAG_CONT)
-                       trace_seq_print_cont(s, iter);
-               break;
-       }
-       }
        return TRACE_TYPE_HANDLED;
 }
 
-#define SEQ_PUT_FIELD_RET(s, x)                                \
-do {                                                   \
-       if (!trace_seq_putmem(s, &(x), sizeof(x)))      \
-               return 0;                               \
-} while (0)
-
-#define SEQ_PUT_HEX_FIELD_RET(s, x)                    \
-do {                                                   \
-       BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES);     \
-       if (!trace_seq_putmem_hex(s, &(x), sizeof(x)))  \
-               return 0;                               \
-} while (0)
-
 static enum print_line_t print_hex_fmt(struct trace_iterator *iter)
 {
        struct trace_seq *s = &iter->seq;
        unsigned char newline = '\n';
        struct trace_entry *entry;
-       int S, T;
+       struct trace_event *event;
 
        entry = iter->ent;
 
-       if (entry->type == TRACE_CONT)
-               return TRACE_TYPE_HANDLED;
-
        SEQ_PUT_HEX_FIELD_RET(s, entry->pid);
        SEQ_PUT_HEX_FIELD_RET(s, iter->cpu);
        SEQ_PUT_HEX_FIELD_RET(s, iter->ts);
 
-       switch (entry->type) {
-       case TRACE_FN: {
-               struct ftrace_entry *field;
+       event = ftrace_find_event(entry->type);
+       if (event && event->hex)
+               event->hex(s, entry, 0);
 
-               trace_assign_type(field, entry);
-
-               SEQ_PUT_HEX_FIELD_RET(s, field->ip);
-               SEQ_PUT_HEX_FIELD_RET(s, field->parent_ip);
-               break;
-       }
-       case TRACE_CTX:
-       case TRACE_WAKE: {
-               struct ctx_switch_entry *field;
-
-               trace_assign_type(field, entry);
-
-               T = task_state_char(field->next_state);
-               S = entry->type == TRACE_WAKE ? '+' :
-                       task_state_char(field->prev_state);
-               SEQ_PUT_HEX_FIELD_RET(s, field->prev_pid);
-               SEQ_PUT_HEX_FIELD_RET(s, field->prev_prio);
-               SEQ_PUT_HEX_FIELD_RET(s, S);
-               SEQ_PUT_HEX_FIELD_RET(s, field->next_cpu);
-               SEQ_PUT_HEX_FIELD_RET(s, field->next_pid);
-               SEQ_PUT_HEX_FIELD_RET(s, field->next_prio);
-               SEQ_PUT_HEX_FIELD_RET(s, T);
-               break;
-       }
-       case TRACE_SPECIAL:
-       case TRACE_USER_STACK:
-       case TRACE_STACK: {
-               struct special_entry *field;
-
-               trace_assign_type(field, entry);
-
-               SEQ_PUT_HEX_FIELD_RET(s, field->arg1);
-               SEQ_PUT_HEX_FIELD_RET(s, field->arg2);
-               SEQ_PUT_HEX_FIELD_RET(s, field->arg3);
-               break;
-       }
-       }
        SEQ_PUT_FIELD_RET(s, newline);
 
        return TRACE_TYPE_HANDLED;
@@ -2282,9 +1659,6 @@ static enum print_line_t print_printk_msg_only(struct trace_iterator *iter)
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
-       if (entry->flags & TRACE_FLAG_CONT)
-               trace_seq_print_cont(s, iter);
-
        return TRACE_TYPE_HANDLED;
 }
 
@@ -2292,53 +1666,19 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
 {
        struct trace_seq *s = &iter->seq;
        struct trace_entry *entry;
+       struct trace_event *event;
 
        entry = iter->ent;
 
-       if (entry->type == TRACE_CONT)
-               return TRACE_TYPE_HANDLED;
-
        SEQ_PUT_FIELD_RET(s, entry->pid);
        SEQ_PUT_FIELD_RET(s, entry->cpu);
        SEQ_PUT_FIELD_RET(s, iter->ts);
 
-       switch (entry->type) {
-       case TRACE_FN: {
-               struct ftrace_entry *field;
-
-               trace_assign_type(field, entry);
-
-               SEQ_PUT_FIELD_RET(s, field->ip);
-               SEQ_PUT_FIELD_RET(s, field->parent_ip);
-               break;
-       }
-       case TRACE_CTX: {
-               struct ctx_switch_entry *field;
-
-               trace_assign_type(field, entry);
-
-               SEQ_PUT_FIELD_RET(s, field->prev_pid);
-               SEQ_PUT_FIELD_RET(s, field->prev_prio);
-               SEQ_PUT_FIELD_RET(s, field->prev_state);
-               SEQ_PUT_FIELD_RET(s, field->next_pid);
-               SEQ_PUT_FIELD_RET(s, field->next_prio);
-               SEQ_PUT_FIELD_RET(s, field->next_state);
-               break;
-       }
-       case TRACE_SPECIAL:
-       case TRACE_USER_STACK:
-       case TRACE_STACK: {
-               struct special_entry *field;
-
-               trace_assign_type(field, entry);
+       event = ftrace_find_event(entry->type);
+       if (event && event->binary)
+               event->binary(s, entry, 0);
 
-               SEQ_PUT_FIELD_RET(s, field->arg1);
-               SEQ_PUT_FIELD_RET(s, field->arg2);
-               SEQ_PUT_FIELD_RET(s, field->arg3);
-               break;
-       }
-       }
-       return 1;
+       return TRACE_TYPE_HANDLED;
 }
 
 static int trace_empty(struct trace_iterator *iter)
@@ -3013,6 +2353,7 @@ static int tracing_set_tracer(char *buf)
                if (ret)
                        goto out;
        }
+       init_tracer_stat(t);
 
        trace_branch_enable(tr);
  out:
@@ -3877,7 +3218,7 @@ __init static int tracer_alloc_buffers(void)
 #else
        current_trace = &nop_trace;
 #endif
-
+       init_tracer_stat(current_trace);
        /* All seems OK, enable tracing */
        tracing_disabled = 0;
 
index 742fe134927692649b81eaa8c087e8f14a2dd575..94ed45e93a80774c0f07f1d46c2df90ce9ef7671 100644 (file)
@@ -17,7 +17,6 @@ enum trace_type {
        TRACE_FN,
        TRACE_CTX,
        TRACE_WAKE,
-       TRACE_CONT,
        TRACE_STACK,
        TRACE_PRINT,
        TRACE_SPECIAL,
@@ -34,7 +33,7 @@ enum trace_type {
        TRACE_KMEM_FREE,
        TRACE_POWER,
 
-       __TRACE_LAST_TYPE
+       __TRACE_LAST_TYPE,
 };
 
 /*
@@ -199,7 +198,6 @@ struct kmemtrace_free_entry {
  *  NEED_RESCED                - reschedule is requested
  *  HARDIRQ            - inside an interrupt handler
  *  SOFTIRQ            - inside a softirq handler
- *  CONT               - multiple entries hold the trace item
  */
 enum trace_flag_type {
        TRACE_FLAG_IRQS_OFF             = 0x01,
@@ -207,7 +205,6 @@ enum trace_flag_type {
        TRACE_FLAG_NEED_RESCHED         = 0x04,
        TRACE_FLAG_HARDIRQ              = 0x08,
        TRACE_FLAG_SOFTIRQ              = 0x10,
-       TRACE_FLAG_CONT                 = 0x20,
 };
 
 #define TRACE_BUF_SIZE         1024
@@ -283,7 +280,6 @@ extern void __ftrace_bad_type(void);
        do {                                                            \
                IF_ASSIGN(var, ent, struct ftrace_entry, TRACE_FN);     \
                IF_ASSIGN(var, ent, struct ctx_switch_entry, 0);        \
-               IF_ASSIGN(var, ent, struct trace_field_cont, TRACE_CONT); \
                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);   \
@@ -365,6 +361,21 @@ struct tracer {
        struct tracer           *next;
        int                     print_max;
        struct tracer_flags     *flags;
+
+       /*
+        * If you change one of the following on tracing runtime, recall
+        * init_tracer_stat()
+        */
+
+       /* Iteration over statistic entries */
+       void                    *(*stat_start)(void);
+       void                    *(*stat_next)(void *prev, int idx);
+       /* Compare two entries for sorting (optional) for stats */
+       int                     (*stat_cmp)(void *p1, void *p2);
+       /* Print a stat entry */
+       int                     (*stat_show)(struct seq_file *s, void *p);
+       /* Print the headers of your stat entries */
+       int                     (*stat_headers)(struct seq_file *s);
 };
 
 struct trace_seq {
@@ -450,6 +461,8 @@ void tracing_start_sched_switch_record(void);
 int register_tracer(struct tracer *type);
 void unregister_tracer(struct tracer *type);
 
+void init_tracer_stat(struct tracer *trace);
+
 extern unsigned long nsecs_to_usecs(unsigned long nsecs);
 
 extern unsigned long tracing_max_latency;
@@ -481,10 +494,10 @@ struct tracer_switch_ops {
        void                            *private;
        struct tracer_switch_ops        *next;
 };
-
-char *trace_find_cmdline(int pid);
 #endif /* CONFIG_CONTEXT_SWITCH_TRACER */
 
+extern char *trace_find_cmdline(int pid);
+
 #ifdef CONFIG_DYNAMIC_FTRACE
 extern unsigned long ftrace_update_tot_cnt;
 #define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func
@@ -513,15 +526,6 @@ extern int trace_selftest_startup_branch(struct tracer *trace,
 #endif /* CONFIG_FTRACE_STARTUP_TEST */
 
 extern void *head_page(struct trace_array_cpu *data);
-extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...);
-extern void trace_seq_print_cont(struct trace_seq *s,
-                                struct trace_iterator *iter);
-
-extern int
-seq_print_ip_sym(struct trace_seq *s, unsigned long ip,
-               unsigned long sym_flags);
-extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf,
-                                size_t cnt);
 extern long ns2usecs(cycle_t nsec);
 extern int
 trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args);
index 366c8c333e136e77d79a4579b4e70f24242b1404..0e94b3d091f70229d4ff521ea18aa3823c6b5ed7 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kallsyms.h>
 
 #include "trace.h"
+#include "trace_output.h"
 
 static struct trace_array *boot_trace;
 static bool pre_initcalls_finished;
index 6c00feb3bac7553c07cb6033da3fc11218ad8815..4785a3b9bc4a54cbaf7ee3310fe60ba26cc57044 100644 (file)
 #include <linux/hash.h>
 #include <linux/fs.h>
 #include <asm/local.h>
+
 #include "trace.h"
+#include "trace_output.h"
+
+static struct tracer branch_trace;
 
 #ifdef CONFIG_BRANCH_TRACER
 
 static int branch_tracing_enabled __read_mostly;
 static DEFINE_MUTEX(branch_tracing_mutex);
+
 static struct trace_array *branch_tracer;
 
 static void
@@ -142,22 +147,50 @@ static void branch_trace_reset(struct trace_array *tr)
        stop_branch_trace(tr);
 }
 
-struct tracer branch_trace __read_mostly =
+static int
+trace_print_print(struct trace_seq *s, struct trace_entry *entry, int flags)
 {
-       .name           = "branch",
-       .init           = branch_trace_init,
-       .reset          = branch_trace_reset,
-#ifdef CONFIG_FTRACE_SELFTEST
-       .selftest       = trace_selftest_startup_branch,
-#endif
-};
+       struct print_entry *field;
 
-__init static int init_branch_trace(void)
+       trace_assign_type(field, entry);
+
+       if (seq_print_ip_sym(s, field->ip, flags))
+               goto partial;
+
+       if (trace_seq_printf(s, ": %s", field->buf))
+               goto partial;
+
+ partial:
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static int
+trace_branch_print(struct trace_seq *s, struct trace_entry *entry, int flags)
 {
-       return register_tracer(&branch_trace);
+       struct trace_branch *field;
+
+       trace_assign_type(field, entry);
+
+       if (trace_seq_printf(s, "[%s] %s:%s:%d\n",
+                            field->correct ? "  ok  " : " MISS ",
+                            field->func,
+                            field->file,
+                            field->line))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return 0;
 }
 
-device_initcall(init_branch_trace);
+
+static struct trace_event trace_branch_event = {
+       .type           = TRACE_BRANCH,
+       .trace          = trace_branch_print,
+       .latency_trace  = trace_branch_print,
+       .raw            = trace_nop_print,
+       .hex            = trace_nop_print,
+       .binary         = trace_nop_print,
+};
+
 #else
 static inline
 void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
@@ -183,66 +216,39 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
 }
 EXPORT_SYMBOL(ftrace_likely_update);
 
-struct ftrace_pointer {
-       void            *start;
-       void            *stop;
-       int             hit;
-};
+extern unsigned long __start_annotated_branch_profile[];
+extern unsigned long __stop_annotated_branch_profile[];
 
-static void *
-t_next(struct seq_file *m, void *v, loff_t *pos)
+static int annotated_branch_stat_headers(struct seq_file *m)
 {
-       const struct ftrace_pointer *f = m->private;
-       struct ftrace_branch_data *p = v;
-
-       (*pos)++;
-
-       if (v == (void *)1)
-               return f->start;
-
-       ++p;
-
-       if ((void *)p >= (void *)f->stop)
-               return NULL;
-
-       return p;
+       seq_printf(m, " correct incorrect  %% ");
+       seq_printf(m, "       Function                "
+                             "  File              Line\n"
+                             " ------- ---------  - "
+                             "       --------                "
+                             "  ----              ----\n");
+       return 0;
 }
 
-static void *t_start(struct seq_file *m, loff_t *pos)
+static inline long get_incorrect_percent(struct ftrace_branch_data *p)
 {
-       void *t = (void *)1;
-       loff_t l = 0;
-
-       for (; t && l < *pos; t = t_next(m, t, &l))
-               ;
+       long percent;
 
-       return t;
-}
+       if (p->correct) {
+               percent = p->incorrect * 100;
+               percent /= p->correct + p->incorrect;
+       } else
+               percent = p->incorrect ? 100 : -1;
 
-static void t_stop(struct seq_file *m, void *p)
-{
+       return percent;
 }
 
-static int t_show(struct seq_file *m, void *v)
+static int branch_stat_show(struct seq_file *m, void *v)
 {
-       const struct ftrace_pointer *fp = m->private;
        struct ftrace_branch_data *p = v;
        const char *f;
        long percent;
 
-       if (v == (void *)1) {
-               if (fp->hit)
-                       seq_printf(m, "   miss      hit    %% ");
-               else
-                       seq_printf(m, " correct incorrect  %% ");
-               seq_printf(m, "       Function                "
-                             "  File              Line\n"
-                             " ------- ---------  - "
-                             "       --------                "
-                             "  ----              ----\n");
-               return 0;
-       }
-
        /* Only print the file, not the path */
        f = p->file + strlen(p->file);
        while (f >= p->file && *f != '/')
@@ -252,11 +258,7 @@ static int t_show(struct seq_file *m, void *v)
        /*
         * The miss is overlayed on correct, and hit on incorrect.
         */
-       if (p->correct) {
-               percent = p->incorrect * 100;
-               percent /= p->correct + p->incorrect;
-       } else
-               percent = p->incorrect ? 100 : -1;
+       percent = get_incorrect_percent(p);
 
        seq_printf(m, "%8lu %8lu ",  p->correct, p->incorrect);
        if (percent < 0)
@@ -267,76 +269,143 @@ static int t_show(struct seq_file *m, void *v)
        return 0;
 }
 
-static struct seq_operations tracing_likely_seq_ops = {
-       .start          = t_start,
-       .next           = t_next,
-       .stop           = t_stop,
-       .show           = t_show,
-};
+static void *annotated_branch_stat_start(void)
+{
+       return __start_annotated_branch_profile;
+}
 
-static int tracing_branch_open(struct inode *inode, struct file *file)
+static void *
+annotated_branch_stat_next(void *v, int idx)
 {
-       int ret;
+       struct ftrace_branch_data *p = v;
 
-       ret = seq_open(file, &tracing_likely_seq_ops);
-       if (!ret) {
-               struct seq_file *m = file->private_data;
-               m->private = (void *)inode->i_private;
-       }
+       ++p;
 
-       return ret;
+       if ((void *)p >= (void *)__stop_annotated_branch_profile)
+               return NULL;
+
+       return p;
 }
 
-static const struct file_operations tracing_branch_fops = {
-       .open           = tracing_branch_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-};
+static int annotated_branch_stat_cmp(void *p1, void *p2)
+{
+       struct ftrace_branch_data *a = p1;
+       struct ftrace_branch_data *b = p2;
+
+       long percent_a, percent_b;
+
+       percent_a = get_incorrect_percent(a);
+       percent_b = get_incorrect_percent(b);
+
+       if (percent_a < percent_b)
+               return -1;
+       if (percent_a > percent_b)
+               return 1;
+       else
+               return 0;
+}
 
 #ifdef CONFIG_PROFILE_ALL_BRANCHES
-extern unsigned long __start_branch_profile[];
-extern unsigned long __stop_branch_profile[];
+enum {
+       TRACE_BRANCH_OPT_ALL = 0x1
+};
 
-static const struct ftrace_pointer ftrace_branch_pos = {
-       .start                  = __start_branch_profile,
-       .stop                   = __stop_branch_profile,
-       .hit                    = 1,
+static struct tracer_opt branch_opts[] = {
+       { TRACER_OPT(stat_all_branch, TRACE_BRANCH_OPT_ALL) },
+       { }
 };
 
-#endif /* CONFIG_PROFILE_ALL_BRANCHES */
+static struct tracer_flags branch_flags = {
+       .val = 0,
+       .opts = branch_opts
+};
 
-extern unsigned long __start_annotated_branch_profile[];
-extern unsigned long __stop_annotated_branch_profile[];
+extern unsigned long __start_branch_profile[];
+extern unsigned long __stop_branch_profile[];
 
-static const struct ftrace_pointer ftrace_annotated_branch_pos = {
-       .start                  = __start_annotated_branch_profile,
-       .stop                   = __stop_annotated_branch_profile,
-};
+static int all_branch_stat_headers(struct seq_file *m)
+{
+       seq_printf(m, "   miss      hit    %% ");
+       seq_printf(m, "       Function                "
+                             "  File              Line\n"
+                             " ------- ---------  - "
+                             "       --------                "
+                             "  ----              ----\n");
+       return 0;
+}
+
+static void *all_branch_stat_start(void)
+{
+       return __start_branch_profile;
+}
 
-static __init int ftrace_branch_init(void)
+static void *
+all_branch_stat_next(void *v, int idx)
 {
-       struct dentry *d_tracer;
-       struct dentry *entry;
+       struct ftrace_branch_data *p = v;
 
-       d_tracer = tracing_init_dentry();
+       ++p;
 
-       entry = debugfs_create_file("profile_annotated_branch", 0444, d_tracer,
-                                   (void *)&ftrace_annotated_branch_pos,
-                                   &tracing_branch_fops);
-       if (!entry)
-               pr_warning("Could not create debugfs "
-                          "'profile_annotatet_branch' entry\n");
+       if ((void *)p >= (void *)__stop_branch_profile)
+               return NULL;
 
-#ifdef CONFIG_PROFILE_ALL_BRANCHES
-       entry = debugfs_create_file("profile_branch", 0444, d_tracer,
-                                   (void *)&ftrace_branch_pos,
-                                   &tracing_branch_fops);
-       if (!entry)
-               pr_warning("Could not create debugfs"
-                          " 'profile_branch' entry\n");
-#endif
+       return p;
+}
 
+static int branch_set_flag(u32 old_flags, u32 bit, int set)
+{
+       if (bit == TRACE_BRANCH_OPT_ALL) {
+               if (set) {
+                       branch_trace.stat_headers = all_branch_stat_headers;
+                       branch_trace.stat_start = all_branch_stat_start;
+                       branch_trace.stat_next = all_branch_stat_next;
+                       branch_trace.stat_cmp = NULL;
+               } else {
+                       branch_trace.stat_headers =
+                               annotated_branch_stat_headers;
+                       branch_trace.stat_start = annotated_branch_stat_start;
+                       branch_trace.stat_next = annotated_branch_stat_next;
+                       branch_trace.stat_cmp = annotated_branch_stat_cmp;
+               }
+               init_tracer_stat(&branch_trace);
+       }
        return 0;
 }
 
-device_initcall(ftrace_branch_init);
+#endif /* CONFIG_PROFILE_ALL_BRANCHES */
+
+static struct tracer branch_trace __read_mostly =
+{
+       .name           = "branch",
+#ifdef CONFIG_BRANCH_TRACER
+       .init           = branch_trace_init,
+       .reset          = branch_trace_reset,
+#ifdef CONFIG_FTRACE_SELFTEST
+       .selftest       = trace_selftest_startup_branch,
+#endif /* CONFIG_FTRACE_SELFTEST */
+#endif /* CONFIG_BRANCH_TRACER */
+       .stat_start     =       annotated_branch_stat_start,
+       .stat_next      = annotated_branch_stat_next,
+       .stat_show      = branch_stat_show,
+       .stat_headers   = annotated_branch_stat_headers,
+       .stat_cmp       = annotated_branch_stat_cmp,
+#ifdef CONFIG_PROFILE_ALL_BRANCHES
+       .flags  = &branch_flags,
+       .set_flag       = branch_set_flag,
+#endif
+};
+
+__init static int init_branch_trace(void)
+{
+#ifdef CONFIG_BRANCH_TRACER
+       int ret;
+       ret = register_ftrace_event(&trace_branch_event);
+       if (!ret) {
+               printk(KERN_WARNING "Warning: could not register branch events\n");
+               return 1;
+       }
+#endif
+
+       return register_tracer(&branch_trace);
+}
+device_initcall(init_branch_trace);
index 8516e4f09e1b4559c8b450183f70c0cb39614950..3c545984816f35654b5fb8a72735bec75e9950f8 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/fs.h>
 
 #include "trace.h"
+#include "trace_output.h"
 
 #define TRACE_GRAPH_INDENT     2
 
@@ -589,9 +590,6 @@ print_graph_comment(struct print_entry *trace, struct trace_seq *s,
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
-       if (ent->flags & TRACE_FLAG_CONT)
-               trace_seq_print_cont(s, iter);
-
        /* Strip ending newline */
        if (s->buffer[s->len - 1] == '\n') {
                s->buffer[s->len - 1] = '\0';
index 649df22d435fc3507d05aecbb244ccf5cbcfffa1..df21c1e72b95f865ab5ea2a21f3787463117dcf3 100644 (file)
@@ -14,6 +14,7 @@
 #include <asm/ds.h>
 
 #include "trace.h"
+#include "trace_output.h"
 
 
 #define SIZEOF_BTS (1 << 13)
index fffcb069f1dcb48213ec0c51a4c8d1774e8235a4..fcec59ff2355bff472c4e66e923298ed1561e2a3 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/pci.h>
 
 #include "trace.h"
+#include "trace_output.h"
 
 struct header_iter {
        struct pci_dev *dev;
@@ -262,9 +263,6 @@ static enum print_line_t mmio_print_mark(struct trace_iterator *iter)
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
-       if (entry->flags & TRACE_FLAG_CONT)
-               trace_seq_print_cont(s, iter);
-
        return TRACE_TYPE_HANDLED;
 }
 
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
new file mode 100644 (file)
index 0000000..df0c25c
--- /dev/null
@@ -0,0 +1,832 @@
+/*
+ * trace_output.c
+ *
+ * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/ftrace.h>
+
+#include "trace_output.h"
+
+/* must be a power of 2 */
+#define EVENT_HASHSIZE 128
+
+static DEFINE_MUTEX(trace_event_mutex);
+static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly;
+
+static int next_event_type = __TRACE_LAST_TYPE + 1;
+
+/**
+ * trace_seq_printf - sequence printing of trace information
+ * @s: trace sequence descriptor
+ * @fmt: printf format string
+ *
+ * The tracer may use either sequence operations or its own
+ * copy to user routines. To simplify formating of a trace
+ * trace_seq_printf is used to store strings into a special
+ * buffer (@s). Then the output may be either used by
+ * the sequencer or pulled into another buffer.
+ */
+int
+trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
+{
+       int len = (PAGE_SIZE - 1) - s->len;
+       va_list ap;
+       int ret;
+
+       if (!len)
+               return 0;
+
+       va_start(ap, fmt);
+       ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
+       va_end(ap);
+
+       /* If we can't write it all, don't bother writing anything */
+       if (ret >= len)
+               return 0;
+
+       s->len += ret;
+
+       return len;
+}
+
+/**
+ * trace_seq_puts - trace sequence printing of simple string
+ * @s: trace sequence descriptor
+ * @str: simple string to record
+ *
+ * The tracer may use either the sequence operations or its own
+ * copy to user routines. This function records a simple string
+ * into a special buffer (@s) for later retrieval by a sequencer
+ * or other mechanism.
+ */
+int trace_seq_puts(struct trace_seq *s, const char *str)
+{
+       int len = strlen(str);
+
+       if (len > ((PAGE_SIZE - 1) - s->len))
+               return 0;
+
+       memcpy(s->buffer + s->len, str, len);
+       s->len += len;
+
+       return len;
+}
+
+int trace_seq_putc(struct trace_seq *s, unsigned char c)
+{
+       if (s->len >= (PAGE_SIZE - 1))
+               return 0;
+
+       s->buffer[s->len++] = c;
+
+       return 1;
+}
+
+int trace_seq_putmem(struct trace_seq *s, void *mem, size_t len)
+{
+       if (len > ((PAGE_SIZE - 1) - s->len))
+               return 0;
+
+       memcpy(s->buffer + s->len, mem, len);
+       s->len += len;
+
+       return len;
+}
+
+int trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len)
+{
+       unsigned char hex[HEX_CHARS];
+       unsigned char *data = mem;
+       int i, j;
+
+#ifdef __BIG_ENDIAN
+       for (i = 0, j = 0; i < len; i++) {
+#else
+       for (i = len-1, j = 0; i >= 0; i--) {
+#endif
+               hex[j++] = hex_asc_hi(data[i]);
+               hex[j++] = hex_asc_lo(data[i]);
+       }
+       hex[j++] = ' ';
+
+       return trace_seq_putmem(s, hex, j);
+}
+
+int trace_seq_path(struct trace_seq *s, struct path *path)
+{
+       unsigned char *p;
+
+       if (s->len >= (PAGE_SIZE - 1))
+               return 0;
+       p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
+       if (!IS_ERR(p)) {
+               p = mangle_path(s->buffer + s->len, p, "\n");
+               if (p) {
+                       s->len = p - s->buffer;
+                       return 1;
+               }
+       } else {
+               s->buffer[s->len++] = '?';
+               return 1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_KRETPROBES
+static inline const char *kretprobed(const char *name)
+{
+       static const char tramp_name[] = "kretprobe_trampoline";
+       int size = sizeof(tramp_name);
+
+       if (strncmp(tramp_name, name, size) == 0)
+               return "[unknown/kretprobe'd]";
+       return name;
+}
+#else
+static inline const char *kretprobed(const char *name)
+{
+       return name;
+}
+#endif /* CONFIG_KRETPROBES */
+
+static int
+seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
+{
+#ifdef CONFIG_KALLSYMS
+       char str[KSYM_SYMBOL_LEN];
+       const char *name;
+
+       kallsyms_lookup(address, NULL, NULL, NULL, str);
+
+       name = kretprobed(str);
+
+       return trace_seq_printf(s, fmt, name);
+#endif
+       return 1;
+}
+
+static int
+seq_print_sym_offset(struct trace_seq *s, const char *fmt,
+                    unsigned long address)
+{
+#ifdef CONFIG_KALLSYMS
+       char str[KSYM_SYMBOL_LEN];
+       const char *name;
+
+       sprint_symbol(str, address);
+       name = kretprobed(str);
+
+       return trace_seq_printf(s, fmt, name);
+#endif
+       return 1;
+}
+
+#ifndef CONFIG_64BIT
+# define IP_FMT "%08lx"
+#else
+# define IP_FMT "%016lx"
+#endif
+
+int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
+                     unsigned long ip, unsigned long sym_flags)
+{
+       struct file *file = NULL;
+       unsigned long vmstart = 0;
+       int ret = 1;
+
+       if (mm) {
+               const struct vm_area_struct *vma;
+
+               down_read(&mm->mmap_sem);
+               vma = find_vma(mm, ip);
+               if (vma) {
+                       file = vma->vm_file;
+                       vmstart = vma->vm_start;
+               }
+               if (file) {
+                       ret = trace_seq_path(s, &file->f_path);
+                       if (ret)
+                               ret = trace_seq_printf(s, "[+0x%lx]",
+                                                      ip - vmstart);
+               }
+               up_read(&mm->mmap_sem);
+       }
+       if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file))
+               ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
+       return ret;
+}
+
+int
+seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s,
+                     unsigned long sym_flags)
+{
+       struct mm_struct *mm = NULL;
+       int ret = 1;
+       unsigned int i;
+
+       if (trace_flags & TRACE_ITER_SYM_USEROBJ) {
+               struct task_struct *task;
+               /*
+                * we do the lookup on the thread group leader,
+                * since individual threads might have already quit!
+                */
+               rcu_read_lock();
+               task = find_task_by_vpid(entry->ent.tgid);
+               if (task)
+                       mm = get_task_mm(task);
+               rcu_read_unlock();
+       }
+
+       for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
+               unsigned long ip = entry->caller[i];
+
+               if (ip == ULONG_MAX || !ret)
+                       break;
+               if (i && ret)
+                       ret = trace_seq_puts(s, " <- ");
+               if (!ip) {
+                       if (ret)
+                               ret = trace_seq_puts(s, "??");
+                       continue;
+               }
+               if (!ret)
+                       break;
+               if (ret)
+                       ret = seq_print_user_ip(s, mm, ip, sym_flags);
+       }
+
+       if (mm)
+               mmput(mm);
+       return ret;
+}
+
+int
+seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
+{
+       int ret;
+
+       if (!ip)
+               return trace_seq_printf(s, "0");
+
+       if (sym_flags & TRACE_ITER_SYM_OFFSET)
+               ret = seq_print_sym_offset(s, "%s", ip);
+       else
+               ret = seq_print_sym_short(s, "%s", ip);
+
+       if (!ret)
+               return 0;
+
+       if (sym_flags & TRACE_ITER_SYM_ADDR)
+               ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
+       return ret;
+}
+
+static const char state_to_char[] = TASK_STATE_TO_CHAR_STR;
+
+static int task_state_char(unsigned long state)
+{
+       int bit = state ? __ffs(state) + 1 : 0;
+
+       return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?';
+}
+
+/**
+ * ftrace_find_event - find a registered event
+ * @type: the type of event to look for
+ *
+ * Returns an event of type @type otherwise NULL
+ */
+struct trace_event *ftrace_find_event(int type)
+{
+       struct trace_event *event;
+       struct hlist_node *n;
+       unsigned key;
+
+       key = type & (EVENT_HASHSIZE - 1);
+
+       hlist_for_each_entry_rcu(event, n, &event_hash[key], node) {
+               if (event->type == type)
+                       return event;
+       }
+
+       return NULL;
+}
+
+/**
+ * register_ftrace_event - register output for an event type
+ * @event: the event type to register
+ *
+ * Event types are stored in a hash and this hash is used to
+ * find a way to print an event. If the @event->type is set
+ * then it will use that type, otherwise it will assign a
+ * type to use.
+ *
+ * If you assign your own type, please make sure it is added
+ * to the trace_type enum in trace.h, to avoid collisions
+ * with the dynamic types.
+ *
+ * Returns the event type number or zero on error.
+ */
+int register_ftrace_event(struct trace_event *event)
+{
+       unsigned key;
+       int ret = 0;
+
+       mutex_lock(&trace_event_mutex);
+
+       if (!event->type)
+               event->type = next_event_type++;
+       else if (event->type > __TRACE_LAST_TYPE) {
+               printk(KERN_WARNING "Need to add type to trace.h\n");
+               WARN_ON(1);
+       }
+
+       if (ftrace_find_event(event->type))
+               goto out;
+
+       key = event->type & (EVENT_HASHSIZE - 1);
+
+       hlist_add_head_rcu(&event->node, &event_hash[key]);
+
+       ret = event->type;
+ out:
+       mutex_unlock(&trace_event_mutex);
+
+       return ret;
+}
+
+/**
+ * unregister_ftrace_event - remove a no longer used event
+ * @event: the event to remove
+ */
+int unregister_ftrace_event(struct trace_event *event)
+{
+       mutex_lock(&trace_event_mutex);
+       hlist_del(&event->node);
+       mutex_unlock(&trace_event_mutex);
+
+       return 0;
+}
+
+/*
+ * Standard events
+ */
+
+int
+trace_nop_print(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       return 0;
+}
+
+/* TRACE_FN */
+static int
+trace_fn_latency(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct ftrace_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (!seq_print_ip_sym(s, field->ip, flags))
+               goto partial;
+       if (!trace_seq_puts(s, " ("))
+               goto partial;
+       if (!seq_print_ip_sym(s, field->parent_ip, flags))
+               goto partial;
+       if (!trace_seq_puts(s, ")\n"))
+               goto partial;
+
+       return 0;
+
+ partial:
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static int
+trace_fn_trace(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct ftrace_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (!seq_print_ip_sym(s, field->ip, flags))
+               goto partial;
+
+       if ((flags & TRACE_ITER_PRINT_PARENT) && field->parent_ip) {
+               if (!trace_seq_printf(s, " <-"))
+                       goto partial;
+               if (!seq_print_ip_sym(s,
+                                     field->parent_ip,
+                                     flags))
+                       goto partial;
+       }
+       if (!trace_seq_printf(s, "\n"))
+               goto partial;
+
+       return 0;
+
+ partial:
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static int
+trace_fn_raw(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct ftrace_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (trace_seq_printf(s, "%x %x\n",
+                            field->ip,
+                            field->parent_ip))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return 0;
+}
+
+static int
+trace_fn_hex(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct ftrace_entry *field;
+
+       trace_assign_type(field, entry);
+
+       SEQ_PUT_HEX_FIELD_RET(s, field->ip);
+       SEQ_PUT_HEX_FIELD_RET(s, field->parent_ip);
+
+       return 0;
+}
+
+static int
+trace_fn_bin(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct ftrace_entry *field;
+
+       trace_assign_type(field, entry);
+
+       SEQ_PUT_FIELD_RET(s, field->ip);
+       SEQ_PUT_FIELD_RET(s, field->parent_ip);
+
+       return 0;
+}
+
+static struct trace_event trace_fn_event = {
+       .type           = TRACE_FN,
+       .trace          = trace_fn_trace,
+       .latency_trace  = trace_fn_latency,
+       .raw            = trace_fn_raw,
+       .hex            = trace_fn_hex,
+       .binary         = trace_fn_bin,
+};
+
+/* TRACE_CTX an TRACE_WAKE */
+static int
+trace_ctxwake_print(struct trace_seq *s, struct trace_entry *entry, int flags,
+                   char *delim)
+{
+       struct ctx_switch_entry *field;
+       char *comm;
+       int S, T;
+
+       trace_assign_type(field, entry);
+
+       T = task_state_char(field->next_state);
+       S = task_state_char(field->prev_state);
+       comm = trace_find_cmdline(field->next_pid);
+       if (trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c %s\n",
+                            field->prev_pid,
+                            field->prev_prio,
+                            S, delim,
+                            field->next_cpu,
+                            field->next_pid,
+                            field->next_prio,
+                            T, comm))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return 0;
+}
+
+static int
+trace_ctx_print(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       return trace_ctxwake_print(s, entry, flags, "==>");
+}
+
+static int
+trace_wake_print(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       return trace_ctxwake_print(s, entry, flags, "  +");
+}
+
+static int
+trace_ctxwake_raw(struct trace_seq *s, struct trace_entry *entry, int flags,
+                 char S)
+{
+       struct ctx_switch_entry *field;
+       int T;
+
+       trace_assign_type(field, entry);
+
+       if (!S)
+               task_state_char(field->prev_state);
+       T = task_state_char(field->next_state);
+       if (trace_seq_printf(s, "%d %d %c %d %d %d %c\n",
+                            field->prev_pid,
+                            field->prev_prio,
+                            S,
+                            field->next_cpu,
+                            field->next_pid,
+                            field->next_prio,
+                            T))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return 0;
+}
+
+static int
+trace_ctx_raw(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       return trace_ctxwake_raw(s, entry, flags, 0);
+}
+
+static int
+trace_wake_raw(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       return trace_ctxwake_raw(s, entry, flags, '+');
+}
+
+
+static int
+trace_ctxwake_hex(struct trace_seq *s, struct trace_entry *entry, int flags,
+                 char S)
+{
+       struct ctx_switch_entry *field;
+       int T;
+
+       trace_assign_type(field, entry);
+
+       if (!S)
+               task_state_char(field->prev_state);
+       T = task_state_char(field->next_state);
+
+       SEQ_PUT_HEX_FIELD_RET(s, field->prev_pid);
+       SEQ_PUT_HEX_FIELD_RET(s, field->prev_prio);
+       SEQ_PUT_HEX_FIELD_RET(s, S);
+       SEQ_PUT_HEX_FIELD_RET(s, field->next_cpu);
+       SEQ_PUT_HEX_FIELD_RET(s, field->next_pid);
+       SEQ_PUT_HEX_FIELD_RET(s, field->next_prio);
+       SEQ_PUT_HEX_FIELD_RET(s, T);
+
+       return 0;
+}
+
+static int
+trace_ctx_hex(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       return trace_ctxwake_hex(s, entry, flags, 0);
+}
+
+static int
+trace_wake_hex(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       return trace_ctxwake_hex(s, entry, flags, '+');
+}
+
+static int
+trace_ctxwake_bin(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct ctx_switch_entry *field;
+
+       trace_assign_type(field, entry);
+
+       SEQ_PUT_FIELD_RET(s, field->prev_pid);
+       SEQ_PUT_FIELD_RET(s, field->prev_prio);
+       SEQ_PUT_FIELD_RET(s, field->prev_state);
+       SEQ_PUT_FIELD_RET(s, field->next_pid);
+       SEQ_PUT_FIELD_RET(s, field->next_prio);
+       SEQ_PUT_FIELD_RET(s, field->next_state);
+
+       return 0;
+}
+
+static struct trace_event trace_ctx_event = {
+       .type           = TRACE_CTX,
+       .trace          = trace_ctx_print,
+       .latency_trace  = trace_ctx_print,
+       .raw            = trace_ctx_raw,
+       .hex            = trace_ctx_hex,
+       .binary         = trace_ctxwake_bin,
+};
+
+static struct trace_event trace_wake_event = {
+       .type           = TRACE_WAKE,
+       .trace          = trace_wake_print,
+       .latency_trace  = trace_wake_print,
+       .raw            = trace_wake_raw,
+       .hex            = trace_wake_hex,
+       .binary         = trace_ctxwake_bin,
+};
+
+/* TRACE_SPECIAL */
+static int
+trace_special_print(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct special_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (trace_seq_printf(s, "# %ld %ld %ld\n",
+                            field->arg1,
+                            field->arg2,
+                            field->arg3))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return 0;
+}
+
+static int
+trace_special_hex(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct special_entry *field;
+
+       trace_assign_type(field, entry);
+
+       SEQ_PUT_HEX_FIELD_RET(s, field->arg1);
+       SEQ_PUT_HEX_FIELD_RET(s, field->arg2);
+       SEQ_PUT_HEX_FIELD_RET(s, field->arg3);
+
+       return 0;
+}
+
+static int
+trace_special_bin(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct special_entry *field;
+
+       trace_assign_type(field, entry);
+
+       SEQ_PUT_FIELD_RET(s, field->arg1);
+       SEQ_PUT_FIELD_RET(s, field->arg2);
+       SEQ_PUT_FIELD_RET(s, field->arg3);
+
+       return 0;
+}
+
+static struct trace_event trace_special_event = {
+       .type           = TRACE_SPECIAL,
+       .trace          = trace_special_print,
+       .latency_trace  = trace_special_print,
+       .raw            = trace_special_print,
+       .hex            = trace_special_hex,
+       .binary         = trace_special_bin,
+};
+
+/* TRACE_STACK */
+
+static int
+trace_stack_print(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct stack_entry *field;
+       int i;
+
+       trace_assign_type(field, entry);
+
+       for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
+               if (i) {
+                       if (trace_seq_puts(s, " <= "))
+                               goto partial;
+
+                       if (seq_print_ip_sym(s, field->caller[i], flags))
+                               goto partial;
+               }
+               if (trace_seq_puts(s, "\n"))
+                       goto partial;
+       }
+
+       return 0;
+
+ partial:
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static struct trace_event trace_stack_event = {
+       .type           = TRACE_STACK,
+       .trace          = trace_stack_print,
+       .latency_trace  = trace_stack_print,
+       .raw            = trace_special_print,
+       .hex            = trace_special_hex,
+       .binary         = trace_special_bin,
+};
+
+/* TRACE_USER_STACK */
+static int
+trace_user_stack_print(struct trace_seq *s, struct trace_entry *entry,
+                      int flags)
+{
+       struct userstack_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (seq_print_userip_objs(field, s, flags))
+               goto partial;
+
+       if (trace_seq_putc(s, '\n'))
+               goto partial;
+
+       return 0;
+
+ partial:
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static struct trace_event trace_user_stack_event = {
+       .type           = TRACE_USER_STACK,
+       .trace          = trace_user_stack_print,
+       .latency_trace  = trace_user_stack_print,
+       .raw            = trace_special_print,
+       .hex            = trace_special_hex,
+       .binary         = trace_special_bin,
+};
+
+/* TRACE_PRINT */
+static int
+trace_print_print(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct print_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (seq_print_ip_sym(s, field->ip, flags))
+               goto partial;
+
+       if (trace_seq_printf(s, ": %s", field->buf))
+               goto partial;
+
+       return 0;
+
+ partial:
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static int
+trace_print_raw(struct trace_seq *s, struct trace_entry *entry, int flags)
+{
+       struct print_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (seq_print_ip_sym(s, field->ip, flags))
+               goto partial;
+
+       if (trace_seq_printf(s, "# %lx %s", field->ip, field->buf))
+               goto partial;
+
+       return 0;
+
+ partial:
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static struct trace_event trace_print_event = {
+       .type           = TRACE_PRINT,
+       .trace          = trace_print_print,
+       .latency_trace  = trace_print_print,
+       .raw            = trace_print_raw,
+       .hex            = trace_nop_print,
+       .binary         = trace_nop_print,
+};
+
+static struct trace_event *events[] __initdata = {
+       &trace_fn_event,
+       &trace_ctx_event,
+       &trace_wake_event,
+       &trace_special_event,
+       &trace_stack_event,
+       &trace_user_stack_event,
+       &trace_print_event,
+       NULL
+};
+
+__init static int init_events(void)
+{
+       struct trace_event *event;
+       int i, ret;
+
+       for (i = 0; events[i]; i++) {
+               event = events[i];
+
+               ret = register_ftrace_event(event);
+               if (!ret) {
+                       printk(KERN_WARNING "event %d failed to register\n",
+                              event->type);
+                       WARN_ON_ONCE(1);
+               }
+       }
+
+       return 0;
+}
+device_initcall(init_events);
diff --git a/kernel/trace/trace_output.h b/kernel/trace/trace_output.h
new file mode 100644 (file)
index 0000000..ecab4ea
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef __TRACE_EVENTS_H
+#define __TRACE_EVENTS_H
+
+#include "trace.h"
+
+typedef int (*trace_print_func)(struct trace_seq *s, struct trace_entry *entry,
+                               int flags);
+
+struct trace_event {
+       struct hlist_node       node;
+       int                     type;
+       trace_print_func        trace;
+       trace_print_func        latency_trace;
+       trace_print_func        raw;
+       trace_print_func        hex;
+       trace_print_func        binary;
+};
+
+extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...);
+extern int
+seq_print_ip_sym(struct trace_seq *s, unsigned long ip,
+               unsigned long sym_flags);
+extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf,
+                                size_t cnt);
+int trace_seq_puts(struct trace_seq *s, const char *str);
+int trace_seq_putc(struct trace_seq *s, unsigned char c);
+int trace_seq_putmem(struct trace_seq *s, void *mem, size_t len);
+int trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len);
+int trace_seq_path(struct trace_seq *s, struct path *path);
+int seq_print_userip_objs(const struct userstack_entry *entry,
+                         struct trace_seq *s, unsigned long sym_flags);
+int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
+                     unsigned long ip, unsigned long sym_flags);
+
+struct trace_event *ftrace_find_event(int type);
+int register_ftrace_event(struct trace_event *event);
+int unregister_ftrace_event(struct trace_event *event);
+
+int
+trace_nop_print(struct trace_seq *s, struct trace_entry *entry, int flags);
+
+#define MAX_MEMHEX_BYTES       8
+#define HEX_CHARS              (MAX_MEMHEX_BYTES*2 + 1)
+
+#define SEQ_PUT_FIELD_RET(s, x)                                \
+do {                                                   \
+       if (!trace_seq_putmem(s, &(x), sizeof(x)))      \
+               return 0;                               \
+} while (0)
+
+#define SEQ_PUT_HEX_FIELD_RET(s, x)                    \
+do {                                                   \
+       BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES);     \
+       if (!trace_seq_putmem_hex(s, &(x), sizeof(x)))  \
+               return 0;                               \
+} while (0)
+
+#endif
+
index 7bda248daf5557a04ff1f178764b80777a33dff5..faa6ab7a1f5c589b92a9b4ea7e23f20168ea6263 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 
 #include "trace.h"
+#include "trace_output.h"
 
 static struct trace_array *power_trace;
 static int __read_mostly trace_power_enabled;
index 88c8eb70f54aeb3508dda9c668f082b9a43b0c78..5013812578b1dcec27b1394e1131cad491546176 100644 (file)
@@ -9,7 +9,6 @@ static inline int trace_valid_entry(struct trace_entry *entry)
        case TRACE_FN:
        case TRACE_CTX:
        case TRACE_WAKE:
-       case TRACE_CONT:
        case TRACE_STACK:
        case TRACE_PRINT:
        case TRACE_SPECIAL:
diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c
new file mode 100644 (file)
index 0000000..6f194a3
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Infrastructure for statistic tracing (histogram output).
+ *
+ * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
+ *
+ * Based on the code from trace_branch.c which is
+ * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+
+
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include "trace.h"
+
+
+/* List of stat entries from a tracer */
+struct trace_stat_list {
+       struct list_head list;
+       void *stat;
+};
+
+static struct trace_stat_list stat_list;
+
+/*
+ * This is a copy of the current tracer to avoid racy
+ * and dangerous output while the current tracer is
+ * switched.
+ */
+static struct tracer current_tracer;
+
+/*
+ * Protect both the current tracer and the global
+ * stat list.
+ */
+static DEFINE_MUTEX(stat_list_mutex);
+
+
+static void reset_stat_list(void)
+{
+       struct trace_stat_list *node;
+       struct list_head *next;
+
+       if (list_empty(&stat_list.list))
+               return;
+
+       node = list_entry(stat_list.list.next, struct trace_stat_list, list);
+       next = node->list.next;
+
+       while (&node->list != next) {
+               kfree(node);
+               node = list_entry(next, struct trace_stat_list, list);
+       }
+       kfree(node);
+
+       INIT_LIST_HEAD(&stat_list.list);
+}
+
+void init_tracer_stat(struct tracer *trace)
+{
+       mutex_lock(&stat_list_mutex);
+       current_tracer = *trace;
+       mutex_unlock(&stat_list_mutex);
+}
+
+/*
+ * For tracers that don't provide a stat_cmp callback.
+ * This one will force an immediate insertion on tail of
+ * the list.
+ */
+static int dummy_cmp(void *p1, void *p2)
+{
+       return 1;
+}
+
+/*
+ * Initialize the stat list at each trace_stat file opening.
+ * All of these copies and sorting are required on all opening
+ * since the stats could have changed between two file sessions.
+ */
+static int stat_seq_init(void)
+{
+       struct trace_stat_list *iter_entry, *new_entry;
+       void *prev_stat;
+       int ret = 0;
+       int i;
+
+       mutex_lock(&stat_list_mutex);
+       reset_stat_list();
+
+       if (!current_tracer.stat_start || !current_tracer.stat_next ||
+                                       !current_tracer.stat_show)
+               goto exit;
+
+       if (!current_tracer.stat_cmp)
+               current_tracer.stat_cmp = dummy_cmp;
+
+       /*
+        * The first entry. Actually this is the second, but the first
+        * one (the stat_list head) is pointless.
+        */
+       new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL);
+       if (!new_entry) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       INIT_LIST_HEAD(&new_entry->list);
+       list_add(&new_entry->list, &stat_list.list);
+       new_entry->stat = current_tracer.stat_start();
+
+       prev_stat = new_entry->stat;
+
+       /*
+        * Iterate over the tracer stat entries and store them in a sorted
+        * list.
+        */
+       for (i = 1; ; i++) {
+               new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL);
+               if (!new_entry) {
+                       ret = -ENOMEM;
+                       goto exit_free_list;
+               }
+
+               INIT_LIST_HEAD(&new_entry->list);
+               new_entry->stat = current_tracer.stat_next(prev_stat, i);
+
+               /* End of insertion */
+               if (!new_entry->stat)
+                       break;
+
+               list_for_each_entry(iter_entry, &stat_list.list, list) {
+                       /* Insertion with a descendent sorting */
+                       if (current_tracer.stat_cmp(new_entry->stat,
+                                               iter_entry->stat) > 0) {
+
+                               list_add_tail(&new_entry->list,
+                                               &iter_entry->list);
+                               break;
+
+                       /* The current smaller value */
+                       } else if (list_is_last(&iter_entry->list,
+                                               &stat_list.list)) {
+                               list_add(&new_entry->list, &iter_entry->list);
+                               break;
+                       }
+               }
+
+               prev_stat = new_entry->stat;
+       }
+exit:
+       mutex_unlock(&stat_list_mutex);
+       return ret;
+
+exit_free_list:
+       reset_stat_list();
+       mutex_unlock(&stat_list_mutex);
+       return ret;
+}
+
+
+static void *stat_seq_start(struct seq_file *s, loff_t *pos)
+{
+       struct trace_stat_list *l = (struct trace_stat_list *)s->private;
+
+       /* Prevent from tracer switch or stat_list modification */
+       mutex_lock(&stat_list_mutex);
+
+       /* If we are in the beginning of the file, print the headers */
+       if (!*pos && current_tracer.stat_headers)
+               current_tracer.stat_headers(s);
+
+       return seq_list_start(&l->list, *pos);
+}
+
+static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
+{
+       struct trace_stat_list *l = (struct trace_stat_list *)s->private;
+
+       return seq_list_next(p, &l->list, pos);
+}
+
+static void stat_seq_stop(struct seq_file *m, void *p)
+{
+       mutex_unlock(&stat_list_mutex);
+}
+
+static int stat_seq_show(struct seq_file *s, void *v)
+{
+       struct trace_stat_list *l = list_entry(v, struct trace_stat_list, list);
+       return current_tracer.stat_show(s, l->stat);
+}
+
+static const struct seq_operations trace_stat_seq_ops = {
+       .start = stat_seq_start,
+       .next = stat_seq_next,
+       .stop = stat_seq_stop,
+       .show = stat_seq_show
+};
+
+static int tracing_stat_open(struct inode *inode, struct file *file)
+{
+       int ret;
+
+       ret = seq_open(file, &trace_stat_seq_ops);
+       if (!ret) {
+               struct seq_file *m = file->private_data;
+               m->private = &stat_list;
+               ret = stat_seq_init();
+       }
+
+       return ret;
+}
+
+
+/*
+ * Avoid consuming memory with our now useless list.
+ */
+static int tracing_stat_release(struct inode *i, struct file *f)
+{
+       mutex_lock(&stat_list_mutex);
+       reset_stat_list();
+       mutex_unlock(&stat_list_mutex);
+       return 0;
+}
+
+static const struct file_operations tracing_stat_fops = {
+       .open           = tracing_stat_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = tracing_stat_release
+};
+
+static int __init tracing_stat_init(void)
+{
+       struct dentry *d_tracing;
+       struct dentry *entry;
+
+       INIT_LIST_HEAD(&stat_list.list);
+       d_tracing = tracing_init_dentry();
+
+       entry = debugfs_create_file("trace_stat", 0444, d_tracing,
+                                       NULL,
+                                   &tracing_stat_fops);
+       if (!entry)
+               pr_warning("Could not create debugfs "
+                          "'trace_stat' entry\n");
+       return 0;
+}
+fs_initcall(tracing_stat_init);