Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 May 2012 01:18:55 +0000 (18:18 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 May 2012 01:18:55 +0000 (18:18 -0700)
Pull perf changes from Ingo Molnar:
 "Lots of changes:

   - (much) improved assembly annotation support in perf report, with
     jump visualization, searching, navigation, visual output
     improvements and more.

    - kernel support for AMD IBS PMU hardware features.  Notably 'perf
      record -e cycles:p' and 'perf top -e cycles:p' should work without
      skid now, like PEBS does on the Intel side, because it takes
      advantage of IBS transparently.

    - the libtracevents library: it is the first step towards unifying
      tracing tooling and perf, and it also gives a tracing library for
      external tools like powertop to rely on.

    - infrastructure: various improvements and refactoring of the UI
      modules and related code

    - infrastructure: cleanup and simplification of the profiling
      targets code (--uid, --pid, --tid, --cpu, --all-cpus, etc.)

    - tons of robustness fixes all around

    - various ftrace updates: speedups, cleanups, robustness
      improvements.

    - typing 'make' in tools/ will now give you a menu of projects to
      build and a short help text to explain what each does.

    - ... and lots of other changes I forgot to list.

  The perf record make bzImage + perf report regression you reported
  should be fixed."

* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (166 commits)
  tracing: Remove kernel_lock annotations
  tracing: Fix initial buffer_size_kb state
  ring-buffer: Merge separate resize loops
  perf evsel: Create events initially disabled -- again
  perf tools: Split term type into value type and term type
  perf hists: Fix callchain ip printf format
  perf target: Add uses_mmap field
  ftrace: Remove selecting FRAME_POINTER with FUNCTION_TRACER
  ftrace/x86: Have x86 ftrace use the ftrace_modify_all_code()
  ftrace: Make ftrace_modify_all_code() global for archs to use
  ftrace: Return record ip addr for ftrace_location()
  ftrace: Consolidate ftrace_location() and ftrace_text_reserved()
  ftrace: Speed up search by skipping pages by address
  ftrace: Remove extra helper functions
  ftrace: Sort all function addresses, not just per page
  tracing: change CPU ring buffer state from tracing_cpumask
  tracing: Check return value of tracing_dentry_percpu()
  ring-buffer: Reset head page before running self test
  ring-buffer: Add integrity check at end of iter read
  ring-buffer: Make addition of pages in ring buffer atomic
  ...

114 files changed:
Makefile
arch/alpha/kernel/perf_event.c
arch/arm/configs/bcmring_defconfig
arch/arm/kernel/perf_event_v6.c
arch/arm/kernel/perf_event_v7.c
arch/arm/kernel/perf_event_xscale.c
arch/mips/kernel/perf_event_mipsxx.c
arch/powerpc/configs/chroma_defconfig
arch/powerpc/configs/gamecube_defconfig
arch/powerpc/configs/wii_defconfig
arch/powerpc/perf/core-book3s.c
arch/powerpc/perf/core-fsl-emb.c
arch/sh/configs/sh7785lcr_32bit_defconfig
arch/sparc/configs/sparc64_defconfig
arch/sparc/kernel/perf_event.c
arch/x86/Kconfig
arch/x86/include/asm/ftrace.h
arch/x86/include/asm/msr-index.h
arch/x86/include/asm/perf_event.h
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_amd.c
arch/x86/kernel/cpu/perf_event_amd_ibs.c
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_intel_ds.c
arch/x86/kernel/cpu/perf_event_p4.c
arch/x86/kernel/ftrace.c
arch/x86/kernel/nmi.c
arch/x86/kernel/traps.c
include/asm-generic/vmlinux.lds.h
include/linux/ftrace.h
include/linux/kernel.h
include/linux/perf_event.h
include/linux/ring_buffer.h
init/Kconfig
kernel/events/core.c
kernel/sched/core.c
kernel/trace/Kconfig
kernel/trace/ftrace.c
kernel/trace/ring_buffer.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_printk.c
tools/Makefile [new file with mode: 0644]
tools/lib/traceevent/Makefile [new file with mode: 0644]
tools/lib/traceevent/event-parse.c [new file with mode: 0644]
tools/lib/traceevent/event-parse.h [new file with mode: 0644]
tools/lib/traceevent/event-utils.h [new file with mode: 0644]
tools/lib/traceevent/parse-filter.c [new file with mode: 0644]
tools/lib/traceevent/parse-utils.c [new file with mode: 0644]
tools/lib/traceevent/trace-seq.c [new file with mode: 0644]
tools/perf/Documentation/perfconfig.example
tools/perf/Makefile
tools/perf/builtin-kmem.c
tools/perf/builtin-lock.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-sched.c
tools/perf/builtin-script.c
tools/perf/builtin-stat.c
tools/perf/builtin-test.c
tools/perf/builtin-top.c
tools/perf/perf.h
tools/perf/ui/browser.c [moved from tools/perf/util/ui/browser.c with 81% similarity]
tools/perf/ui/browser.h [moved from tools/perf/util/ui/browser.h with 86% similarity]
tools/perf/ui/browsers/annotate.c [new file with mode: 0644]
tools/perf/ui/browsers/hists.c [moved from tools/perf/util/ui/browsers/hists.c with 98% similarity]
tools/perf/ui/browsers/map.c [moved from tools/perf/util/ui/browsers/map.c with 97% similarity]
tools/perf/ui/browsers/map.h [moved from tools/perf/util/ui/browsers/map.h with 100% similarity]
tools/perf/ui/gtk/browser.c [moved from tools/perf/util/gtk/browser.c with 85% similarity]
tools/perf/ui/gtk/gtk.h [moved from tools/perf/util/gtk/gtk.h with 100% similarity]
tools/perf/ui/gtk/setup.c [new file with mode: 0644]
tools/perf/ui/helpline.c [moved from tools/perf/util/ui/helpline.c with 100% similarity]
tools/perf/ui/helpline.h [moved from tools/perf/util/ui/helpline.h with 100% similarity]
tools/perf/ui/keysyms.h [moved from tools/perf/util/ui/keysyms.h with 100% similarity]
tools/perf/ui/libslang.h [moved from tools/perf/util/ui/libslang.h with 100% similarity]
tools/perf/ui/progress.c [moved from tools/perf/util/ui/progress.c with 100% similarity]
tools/perf/ui/progress.h [moved from tools/perf/util/ui/progress.h with 100% similarity]
tools/perf/ui/setup.c [new file with mode: 0644]
tools/perf/ui/tui/setup.c [moved from tools/perf/util/ui/setup.c with 74% similarity]
tools/perf/ui/ui.h [moved from tools/perf/util/ui/ui.h with 100% similarity]
tools/perf/ui/util.c [moved from tools/perf/util/ui/util.c with 100% similarity]
tools/perf/ui/util.h [moved from tools/perf/util/ui/util.h with 100% similarity]
tools/perf/util/annotate.c
tools/perf/util/annotate.h
tools/perf/util/cache.h
tools/perf/util/debug.c
tools/perf/util/debug.h
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/header.c
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/parse-events.y
tools/perf/util/pmu.c
tools/perf/util/scripting-engines/trace-event-python.c
tools/perf/util/session.c
tools/perf/util/symbol.h
tools/perf/util/target.c [new file with mode: 0644]
tools/perf/util/target.h [new file with mode: 0644]
tools/perf/util/thread_map.h
tools/perf/util/top.c
tools/perf/util/top.h
tools/perf/util/trace-event-info.c
tools/perf/util/trace-event-parse.c
tools/perf/util/trace-event-read.c
tools/perf/util/trace-event.h
tools/perf/util/ui/browsers/annotate.c [deleted file]
tools/perf/util/usage.c
tools/perf/util/util.c
tools/perf/util/util.h
tools/scripts/Makefile.include [new file with mode: 0644]

index a687963..0e7a44e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1471,6 +1471,13 @@ kernelrelease:
 kernelversion:
        @echo $(KERNELVERSION)
 
+# Clear a bunch of variables before executing the submake
+tools/: FORCE
+       $(Q)$(MAKE) LDFLAGS= MAKEFLAGS= -C $(src)/tools/
+
+tools/%: FORCE
+       $(Q)$(MAKE) LDFLAGS= MAKEFLAGS= -C $(src)/tools/ $*
+
 # Single targets
 # ---------------------------------------------------------------------------
 # Single targets are compatible with:
index 0dae252..d821b17 100644 (file)
@@ -824,7 +824,6 @@ static void alpha_perf_event_irq_handler(unsigned long la_ptr,
 
        idx = la_ptr;
 
-       perf_sample_data_init(&data, 0);
        for (j = 0; j < cpuc->n_events; j++) {
                if (cpuc->current_idx[j] == idx)
                        break;
@@ -848,7 +847,7 @@ static void alpha_perf_event_irq_handler(unsigned long la_ptr,
 
        hwc = &event->hw;
        alpha_perf_event_update(event, hwc, idx, alpha_pmu->pmc_max_period[idx]+1);
-       data.period = event->hw.last_period;
+       perf_sample_data_init(&data, 0, hwc->last_period);
 
        if (alpha_perf_event_set_period(event, hwc, idx)) {
                if (perf_event_overflow(event, &data, regs)) {
index 795374d..9e6a8fe 100644 (file)
@@ -11,7 +11,7 @@ CONFIG_KALLSYMS_EXTRA_PASS=y
 # CONFIG_TIMERFD is not set
 # CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
-CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
 # CONFIG_VM_EVENT_COUNTERS is not set
 # CONFIG_SLUB_DEBUG is not set
 # CONFIG_COMPAT_BRK is not set
index b78af0c..ab627a7 100644 (file)
@@ -489,8 +489,6 @@ armv6pmu_handle_irq(int irq_num,
         */
        armv6_pmcr_write(pmcr);
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
        for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
                struct perf_event *event = cpuc->events[idx];
@@ -509,7 +507,7 @@ armv6pmu_handle_irq(int irq_num,
 
                hwc = &event->hw;
                armpmu_event_update(event, hwc, idx);
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, hwc->last_period);
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;
 
index 00755d8..d3c5360 100644 (file)
@@ -1077,8 +1077,6 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
         */
        regs = get_irq_regs();
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
        for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
                struct perf_event *event = cpuc->events[idx];
@@ -1097,7 +1095,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
 
                hwc = &event->hw;
                armpmu_event_update(event, hwc, idx);
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, hwc->last_period);
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;
 
index 71a21e6..e34e725 100644 (file)
@@ -248,8 +248,6 @@ xscale1pmu_handle_irq(int irq_num, void *dev)
 
        regs = get_irq_regs();
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
        for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
                struct perf_event *event = cpuc->events[idx];
@@ -263,7 +261,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev)
 
                hwc = &event->hw;
                armpmu_event_update(event, hwc, idx);
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, hwc->last_period);
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;
 
@@ -588,8 +586,6 @@ xscale2pmu_handle_irq(int irq_num, void *dev)
 
        regs = get_irq_regs();
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
        for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
                struct perf_event *event = cpuc->events[idx];
@@ -603,7 +599,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev)
 
                hwc = &event->hw;
                armpmu_event_update(event, hwc, idx);
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, hwc->last_period);
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;
 
index 811084f..ab73fa2 100644 (file)
@@ -1325,7 +1325,7 @@ static int mipsxx_pmu_handle_shared_irq(void)
 
        regs = get_irq_regs();
 
-       perf_sample_data_init(&data, 0);
+       perf_sample_data_init(&data, 0, 0);
 
        switch (counters) {
 #define HANDLE_COUNTER(n)                                              \
index f104ccd..b1f9597 100644 (file)
@@ -32,7 +32,7 @@ CONFIG_RD_LZMA=y
 CONFIG_INITRAMFS_COMPRESSION_GZIP=y
 CONFIG_KALLSYMS_ALL=y
 CONFIG_EMBEDDED=y
-CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
 CONFIG_KPROBES=y
index e74d3a4..9ef2cc1 100644 (file)
@@ -8,7 +8,7 @@ CONFIG_BLK_DEV_INITRD=y
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 CONFIG_EXPERT=y
 # CONFIG_ELF_CORE is not set
-CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
 # CONFIG_VM_EVENT_COUNTERS is not set
 CONFIG_SLAB=y
 CONFIG_MODULES=y
index 175295f..1e2b7d0 100644 (file)
@@ -9,7 +9,7 @@ CONFIG_BLK_DEV_INITRD=y
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 CONFIG_EXPERT=y
 # CONFIG_ELF_CORE is not set
-CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
 # CONFIG_VM_EVENT_COUNTERS is not set
 CONFIG_SLAB=y
 CONFIG_MODULES=y
index 02aee03..8f84bcb 100644 (file)
@@ -1299,8 +1299,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
        if (record) {
                struct perf_sample_data data;
 
-               perf_sample_data_init(&data, ~0ULL);
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, ~0ULL, event->hw.last_period);
 
                if (event->attr.sample_type & PERF_SAMPLE_ADDR)
                        perf_get_data_addr(regs, &data.addr);
index 0a6d2a9..106c533 100644 (file)
@@ -613,8 +613,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
        if (record) {
                struct perf_sample_data data;
 
-               perf_sample_data_init(&data, 0);
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, event->hw.last_period);
 
                if (perf_event_overflow(event, &data, regs))
                        fsl_emb_pmu_stop(event, 0);
index 7b9c696..9bdcf72 100644 (file)
@@ -5,7 +5,7 @@ CONFIG_BSD_PROCESS_ACCT=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
-CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
 # CONFIG_COMPAT_BRK is not set
 CONFIG_SLAB=y
 CONFIG_PROFILING=y
index 3c1e858..9d8521b 100644 (file)
@@ -5,7 +5,7 @@ CONFIG_SYSVIPC=y
 CONFIG_POSIX_MQUEUE=y
 CONFIG_LOG_BUF_SHIFT=18
 CONFIG_BLK_DEV_INITRD=y
-CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
 # CONFIG_COMPAT_BRK is not set
 CONFIG_SLAB=y
 CONFIG_PROFILING=y
index 28559ce..5713957 100644 (file)
@@ -1296,8 +1296,6 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self,
 
        regs = args->regs;
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
 
        /* If the PMU has the TOE IRQ enable bits, we need to do a
@@ -1321,7 +1319,7 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self,
                if (val & (1ULL << 31))
                        continue;
 
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, hwc->last_period);
                if (!sparc_perf_event_set_period(event, hwc, idx))
                        continue;
 
index 2787fbe..7b383d8 100644 (file)
@@ -40,7 +40,6 @@ config X86
        select HAVE_FUNCTION_GRAPH_TRACER
        select HAVE_FUNCTION_GRAPH_FP_TEST
        select HAVE_FUNCTION_TRACE_MCOUNT_TEST
-       select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_KVM
        select HAVE_ARCH_KGDB
index 268c783..18d9005 100644 (file)
@@ -34,6 +34,7 @@
 
 #ifndef __ASSEMBLY__
 extern void mcount(void);
+extern int modifying_ftrace_code;
 
 static inline unsigned long ftrace_call_adjust(unsigned long addr)
 {
@@ -50,6 +51,8 @@ struct dyn_arch_ftrace {
        /* No extra data needed for x86 */
 };
 
+int ftrace_int3_handler(struct pt_regs *regs);
+
 #endif /*  CONFIG_DYNAMIC_FTRACE */
 #endif /* __ASSEMBLY__ */
 #endif /* CONFIG_FUNCTION_TRACER */
index ccb8059..957ec87 100644 (file)
 #define MSR_AMD64_IBSFETCHCTL          0xc0011030
 #define MSR_AMD64_IBSFETCHLINAD                0xc0011031
 #define MSR_AMD64_IBSFETCHPHYSAD       0xc0011032
+#define MSR_AMD64_IBSFETCH_REG_COUNT   3
+#define MSR_AMD64_IBSFETCH_REG_MASK    ((1UL<<MSR_AMD64_IBSFETCH_REG_COUNT)-1)
 #define MSR_AMD64_IBSOPCTL             0xc0011033
 #define MSR_AMD64_IBSOPRIP             0xc0011034
 #define MSR_AMD64_IBSOPDATA            0xc0011035
 #define MSR_AMD64_IBSOPDATA3           0xc0011037
 #define MSR_AMD64_IBSDCLINAD           0xc0011038
 #define MSR_AMD64_IBSDCPHYSAD          0xc0011039
+#define MSR_AMD64_IBSOP_REG_COUNT      7
+#define MSR_AMD64_IBSOP_REG_MASK       ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1)
 #define MSR_AMD64_IBSCTL               0xc001103a
 #define MSR_AMD64_IBSBRTARGET          0xc001103b
+#define MSR_AMD64_IBS_REG_COUNT_MAX    8 /* includes MSR_AMD64_IBSBRTARGET */
 
 /* Fam 15h MSRs */
 #define MSR_F15H_PERF_CTL              0xc0010200
index 2291895..588f52e 100644 (file)
@@ -158,6 +158,7 @@ struct x86_pmu_capability {
 #define IBS_CAPS_OPCNT                 (1U<<4)
 #define IBS_CAPS_BRNTRGT               (1U<<5)
 #define IBS_CAPS_OPCNTEXT              (1U<<6)
+#define IBS_CAPS_RIPINVALIDCHK         (1U<<7)
 
 #define IBS_CAPS_DEFAULT               (IBS_CAPS_AVAIL         \
                                         | IBS_CAPS_FETCHSAM    \
@@ -170,21 +171,28 @@ struct x86_pmu_capability {
 #define IBSCTL_LVT_OFFSET_VALID                (1ULL<<8)
 #define IBSCTL_LVT_OFFSET_MASK         0x0F
 
-/* IbsFetchCtl bits/masks */
+/* ibs fetch bits/masks */
 #define IBS_FETCH_RAND_EN      (1ULL<<57)
 #define IBS_FETCH_VAL          (1ULL<<49)
 #define IBS_FETCH_ENABLE       (1ULL<<48)
 #define IBS_FETCH_CNT          0xFFFF0000ULL
 #define IBS_FETCH_MAX_CNT      0x0000FFFFULL
 
-/* IbsOpCtl bits */
+/* ibs op bits/masks */
+/* lower 4 bits of the current count are ignored: */
+#define IBS_OP_CUR_CNT         (0xFFFF0ULL<<32)
 #define IBS_OP_CNT_CTL         (1ULL<<19)
 #define IBS_OP_VAL             (1ULL<<18)
 #define IBS_OP_ENABLE          (1ULL<<17)
 #define IBS_OP_MAX_CNT         0x0000FFFFULL
 #define IBS_OP_MAX_CNT_EXT     0x007FFFFFULL   /* not a register bit mask */
+#define IBS_RIP_INVALID                (1ULL<<38)
 
+#ifdef CONFIG_X86_LOCAL_APIC
 extern u32 get_ibs_caps(void);
+#else
+static inline u32 get_ibs_caps(void) { return 0; }
+#endif
 
 #ifdef CONFIG_PERF_EVENTS
 extern void perf_events_lapic_init(void);
index bb8e034..e049d6d 100644 (file)
@@ -484,9 +484,6 @@ static int __x86_pmu_event_init(struct perf_event *event)
 
        /* mark unused */
        event->hw.extra_reg.idx = EXTRA_REG_NONE;
-
-       /* mark not used */
-       event->hw.extra_reg.idx = EXTRA_REG_NONE;
        event->hw.branch_reg.idx = EXTRA_REG_NONE;
 
        return x86_pmu.hw_config(event);
@@ -1186,8 +1183,6 @@ int x86_pmu_handle_irq(struct pt_regs *regs)
        int idx, handled = 0;
        u64 val;
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
 
        /*
@@ -1222,7 +1217,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs)
                 * event overflow
                 */
                handled++;
-               data.period     = event->hw.last_period;
+               perf_sample_data_init(&data, 0, event->hw.last_period);
 
                if (!x86_perf_event_set_period(event))
                        continue;
index 95e7fe1..6565226 100644 (file)
@@ -134,8 +134,13 @@ static u64 amd_pmu_event_map(int hw_event)
 
 static int amd_pmu_hw_config(struct perf_event *event)
 {
-       int ret = x86_pmu_hw_config(event);
+       int ret;
 
+       /* pass precise event sampling to ibs: */
+       if (event->attr.precise_ip && get_ibs_caps())
+               return -ENOENT;
+
+       ret = x86_pmu_hw_config(event);
        if (ret)
                return ret;
 
@@ -205,10 +210,8 @@ static void amd_put_event_constraints(struct cpu_hw_events *cpuc,
         * when we come here
         */
        for (i = 0; i < x86_pmu.num_counters; i++) {
-               if (nb->owners[i] == event) {
-                       cmpxchg(nb->owners+i, event, NULL);
+               if (cmpxchg(nb->owners + i, event, NULL) == event)
                        break;
-               }
        }
 }
 
index 3b8a2d3..da9bcdc 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/perf_event.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/ptrace.h>
 
 #include <asm/apic.h>
 
@@ -16,36 +17,591 @@ static u32 ibs_caps;
 
 #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)
 
-static struct pmu perf_ibs;
+#include <linux/kprobes.h>
+#include <linux/hardirq.h>
+
+#include <asm/nmi.h>
+
+#define IBS_FETCH_CONFIG_MASK  (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT)
+#define IBS_OP_CONFIG_MASK     IBS_OP_MAX_CNT
+
+enum ibs_states {
+       IBS_ENABLED     = 0,
+       IBS_STARTED     = 1,
+       IBS_STOPPING    = 2,
+
+       IBS_MAX_STATES,
+};
+
+struct cpu_perf_ibs {
+       struct perf_event       *event;
+       unsigned long           state[BITS_TO_LONGS(IBS_MAX_STATES)];
+};
+
+struct perf_ibs {
+       struct pmu      pmu;
+       unsigned int    msr;
+       u64             config_mask;
+       u64             cnt_mask;
+       u64             enable_mask;
+       u64             valid_mask;
+       u64             max_period;
+       unsigned long   offset_mask[1];
+       int             offset_max;
+       struct cpu_perf_ibs __percpu *pcpu;
+       u64             (*get_count)(u64 config);
+};
+
+struct perf_ibs_data {
+       u32             size;
+       union {
+               u32     data[0];        /* data buffer starts here */
+               u32     caps;
+       };
+       u64             regs[MSR_AMD64_IBS_REG_COUNT_MAX];
+};
+
+static int
+perf_event_set_period(struct hw_perf_event *hwc, u64 min, u64 max, u64 *hw_period)
+{
+       s64 left = local64_read(&hwc->period_left);
+       s64 period = hwc->sample_period;
+       int overflow = 0;
+
+       /*
+        * If we are way outside a reasonable range then just skip forward:
+        */
+       if (unlikely(left <= -period)) {
+               left = period;
+               local64_set(&hwc->period_left, left);
+               hwc->last_period = period;
+               overflow = 1;
+       }
+
+       if (unlikely(left < (s64)min)) {
+               left += period;
+               local64_set(&hwc->period_left, left);
+               hwc->last_period = period;
+               overflow = 1;
+       }
+
+       /*
+        * If the hw period that triggers the sw overflow is too short
+        * we might hit the irq handler. This biases the results.
+        * Thus we shorten the next-to-last period and set the last
+        * period to the max period.
+        */
+       if (left > max) {
+               left -= max;
+               if (left > max)
+                       left = max;
+               else if (left < min)
+                       left = min;
+       }
+
+       *hw_period = (u64)left;
+
+       return overflow;
+}
+
+static  int
+perf_event_try_update(struct perf_event *event, u64 new_raw_count, int width)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       int shift = 64 - width;
+       u64 prev_raw_count;
+       u64 delta;
+
+       /*
+        * Careful: an NMI might modify the previous event value.
+        *
+        * Our tactic to handle this is to first atomically read and
+        * exchange a new raw count - then add that new-prev delta
+        * count to the generic event atomically:
+        */
+       prev_raw_count = local64_read(&hwc->prev_count);
+       if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+                                       new_raw_count) != prev_raw_count)
+               return 0;
+
+       /*
+        * Now we have the new raw value and have updated the prev
+        * timestamp already. We can now calculate the elapsed delta
+        * (event-)time and add that to the generic event.
+        *
+        * Careful, not all hw sign-extends above the physical width
+        * of the count.
+        */
+       delta = (new_raw_count << shift) - (prev_raw_count << shift);
+       delta >>= shift;
+
+       local64_add(delta, &event->count);
+       local64_sub(delta, &hwc->period_left);
+
+       return 1;
+}
+
+static struct perf_ibs perf_ibs_fetch;
+static struct perf_ibs perf_ibs_op;
+
+static struct perf_ibs *get_ibs_pmu(int type)
+{
+       if (perf_ibs_fetch.pmu.type == type)
+               return &perf_ibs_fetch;
+       if (perf_ibs_op.pmu.type == type)
+               return &perf_ibs_op;
+       return NULL;
+}
+
+/*
+ * Use IBS for precise event sampling:
+ *
+ *  perf record -a -e cpu-cycles:p ...    # use ibs op counting cycle count
+ *  perf record -a -e r076:p ...          # same as -e cpu-cycles:p
+ *  perf record -a -e r0C1:p ...          # use ibs op counting micro-ops
+ *
+ * IbsOpCntCtl (bit 19) of IBS Execution Control Register (IbsOpCtl,
+ * MSRC001_1033) is used to select either cycle or micro-ops counting
+ * mode.
+ *
+ * The rip of IBS samples has skid 0. Thus, IBS supports precise
+ * levels 1 and 2 and the PERF_EFLAGS_EXACT is set. In rare cases the
+ * rip is invalid when IBS was not able to record the rip correctly.
+ * We clear PERF_EFLAGS_EXACT and take the rip from pt_regs then.
+ *
+ */
+static int perf_ibs_precise_event(struct perf_event *event, u64 *config)
+{
+       switch (event->attr.precise_ip) {
+       case 0:
+               return -ENOENT;
+       case 1:
+       case 2:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       switch (event->attr.type) {
+       case PERF_TYPE_HARDWARE:
+               switch (event->attr.config) {
+               case PERF_COUNT_HW_CPU_CYCLES:
+                       *config = 0;
+                       return 0;
+               }
+               break;
+       case PERF_TYPE_RAW:
+               switch (event->attr.config) {
+               case 0x0076:
+                       *config = 0;
+                       return 0;
+               case 0x00C1:
+                       *config = IBS_OP_CNT_CTL;
+                       return 0;
+               }
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       return -EOPNOTSUPP;
+}
 
 static int perf_ibs_init(struct perf_event *event)
 {
-       if (perf_ibs.type != event->attr.type)
+       struct hw_perf_event *hwc = &event->hw;
+       struct perf_ibs *perf_ibs;
+       u64 max_cnt, config;
+       int ret;
+
+       perf_ibs = get_ibs_pmu(event->attr.type);
+       if (perf_ibs) {
+               config = event->attr.config;
+       } else {
+               perf_ibs = &perf_ibs_op;
+               ret = perf_ibs_precise_event(event, &config);
+               if (ret)
+                       return ret;
+       }
+
+       if (event->pmu != &perf_ibs->pmu)
                return -ENOENT;
+
+       if (config & ~perf_ibs->config_mask)
+               return -EINVAL;
+
+       if (hwc->sample_period) {
+               if (config & perf_ibs->cnt_mask)
+                       /* raw max_cnt may not be set */
+                       return -EINVAL;
+               if (!event->attr.sample_freq && hwc->sample_period & 0x0f)
+                       /*
+                        * lower 4 bits can not be set in ibs max cnt,
+                        * but allowing it in case we adjust the
+                        * sample period to set a frequency.
+                        */
+                       return -EINVAL;
+               hwc->sample_period &= ~0x0FULL;
+               if (!hwc->sample_period)
+                       hwc->sample_period = 0x10;
+       } else {
+               max_cnt = config & perf_ibs->cnt_mask;
+               config &= ~perf_ibs->cnt_mask;
+               event->attr.sample_period = max_cnt << 4;
+               hwc->sample_period = event->attr.sample_period;
+       }
+
+       if (!hwc->sample_period)
+               return -EINVAL;
+
+       /*
+        * If we modify hwc->sample_period, we also need to update
+        * hwc->last_period and hwc->period_left.
+        */
+       hwc->last_period = hwc->sample_period;
+       local64_set(&hwc->period_left, hwc->sample_period);
+
+       hwc->config_base = perf_ibs->msr;
+       hwc->config = config;
+
        return 0;
 }
 
+static int perf_ibs_set_period(struct perf_ibs *perf_ibs,
+                              struct hw_perf_event *hwc, u64 *period)
+{
+       int overflow;
+
+       /* ignore lower 4 bits in min count: */
+       overflow = perf_event_set_period(hwc, 1<<4, perf_ibs->max_period, period);
+       local64_set(&hwc->prev_count, 0);
+
+       return overflow;
+}
+
+static u64 get_ibs_fetch_count(u64 config)
+{
+       return (config & IBS_FETCH_CNT) >> 12;
+}
+
+static u64 get_ibs_op_count(u64 config)
+{
+       u64 count = 0;
+
+       if (config & IBS_OP_VAL)
+               count += (config & IBS_OP_MAX_CNT) << 4; /* cnt rolled over */
+
+       if (ibs_caps & IBS_CAPS_RDWROPCNT)
+               count += (config & IBS_OP_CUR_CNT) >> 32;
+
+       return count;
+}
+
+static void
+perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event,
+                     u64 *config)
+{
+       u64 count = perf_ibs->get_count(*config);
+
+       /*
+        * Set width to 64 since we do not overflow on max width but
+        * instead on max count. In perf_ibs_set_period() we clear
+        * prev count manually on overflow.
+        */
+       while (!perf_event_try_update(event, count, 64)) {
+               rdmsrl(event->hw.config_base, *config);
+               count = perf_ibs->get_count(*config);
+       }
+}
+
+static inline void perf_ibs_enable_event(struct perf_ibs *perf_ibs,
+                                        struct hw_perf_event *hwc, u64 config)
+{
+       wrmsrl(hwc->config_base, hwc->config | config | perf_ibs->enable_mask);
+}
+
+/*
+ * Erratum #420 Instruction-Based Sampling Engine May Generate
+ * Interrupt that Cannot Be Cleared:
+ *
+ * Must clear counter mask first, then clear the enable bit. See
+ * Revision Guide for AMD Family 10h Processors, Publication #41322.
+ */
+static inline void perf_ibs_disable_event(struct perf_ibs *perf_ibs,
+                                         struct hw_perf_event *hwc, u64 config)
+{
+       config &= ~perf_ibs->cnt_mask;
+       wrmsrl(hwc->config_base, config);
+       config &= ~perf_ibs->enable_mask;
+       wrmsrl(hwc->config_base, config);
+}
+
+/*
+ * We cannot restore the ibs pmu state, so we always needs to update
+ * the event while stopping it and then reset the state when starting
+ * again. Thus, ignoring PERF_EF_RELOAD and PERF_EF_UPDATE flags in
+ * perf_ibs_start()/perf_ibs_stop() and instead always do it.
+ */
+static void perf_ibs_start(struct perf_event *event, int flags)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
+       struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
+       u64 period;
+
+       if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
+               return;
+
+       WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+       hwc->state = 0;
+
+       perf_ibs_set_period(perf_ibs, hwc, &period);
+       set_bit(IBS_STARTED, pcpu->state);
+       perf_ibs_enable_event(perf_ibs, hwc, period >> 4);
+
+       perf_event_update_userpage(event);
+}
+
+static void perf_ibs_stop(struct perf_event *event, int flags)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
+       struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
+       u64 config;
+       int stopping;
+
+       stopping = test_and_clear_bit(IBS_STARTED, pcpu->state);
+
+       if (!stopping && (hwc->state & PERF_HES_UPTODATE))
+               return;
+
+       rdmsrl(hwc->config_base, config);
+
+       if (stopping) {
+               set_bit(IBS_STOPPING, pcpu->state);
+               perf_ibs_disable_event(perf_ibs, hwc, config);
+               WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+               hwc->state |= PERF_HES_STOPPED;
+       }
+
+       if (hwc->state & PERF_HES_UPTODATE)
+               return;
+
+       /*
+        * Clear valid bit to not count rollovers on update, rollovers
+        * are only updated in the irq handler.
+        */
+       config &= ~perf_ibs->valid_mask;
+
+       perf_ibs_event_update(perf_ibs, event, &config);
+       hwc->state |= PERF_HES_UPTODATE;
+}
+
 static int perf_ibs_add(struct perf_event *event, int flags)
 {
+       struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
+       struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
+
+       if (test_and_set_bit(IBS_ENABLED, pcpu->state))
+               return -ENOSPC;
+
+       event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+       pcpu->event = event;
+
+       if (flags & PERF_EF_START)
+               perf_ibs_start(event, PERF_EF_RELOAD);
+
        return 0;
 }
 
 static void perf_ibs_del(struct perf_event *event, int flags)
 {
+       struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
+       struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
+
+       if (!test_and_clear_bit(IBS_ENABLED, pcpu->state))
+               return;
+
+       perf_ibs_stop(event, PERF_EF_UPDATE);
+
+       pcpu->event = NULL;
+
+       perf_event_update_userpage(event);
 }
 
-static struct pmu perf_ibs = {
-       .event_init= perf_ibs_init,
-       .add= perf_ibs_add,
-       .del= perf_ibs_del,
+static void perf_ibs_read(struct perf_event *event) { }
+
+static struct perf_ibs perf_ibs_fetch = {
+       .pmu = {
+               .task_ctx_nr    = perf_invalid_context,
+
+               .event_init     = perf_ibs_init,
+               .add            = perf_ibs_add,
+               .del            = perf_ibs_del,
+               .start          = perf_ibs_start,
+               .stop           = perf_ibs_stop,
+               .read           = perf_ibs_read,
+       },
+       .msr                    = MSR_AMD64_IBSFETCHCTL,
+       .config_mask            = IBS_FETCH_CONFIG_MASK,
+       .cnt_mask               = IBS_FETCH_MAX_CNT,
+       .enable_mask            = IBS_FETCH_ENABLE,
+       .valid_mask             = IBS_FETCH_VAL,
+       .max_period             = IBS_FETCH_MAX_CNT << 4,
+       .offset_mask            = { MSR_AMD64_IBSFETCH_REG_MASK },
+       .offset_max             = MSR_AMD64_IBSFETCH_REG_COUNT,
+
+       .get_count              = get_ibs_fetch_count,
 };
 
+static struct perf_ibs perf_ibs_op = {
+       .pmu = {
+               .task_ctx_nr    = perf_invalid_context,
+
+               .event_init     = perf_ibs_init,
+               .add            = perf_ibs_add,
+               .del            = perf_ibs_del,
+               .start          = perf_ibs_start,
+               .stop           = perf_ibs_stop,
+               .read           = perf_ibs_read,
+       },
+       .msr                    = MSR_AMD64_IBSOPCTL,
+       .config_mask            = IBS_OP_CONFIG_MASK,
+       .cnt_mask               = IBS_OP_MAX_CNT,
+       .enable_mask            = IBS_OP_ENABLE,
+       .valid_mask             = IBS_OP_VAL,
+       .max_period             = IBS_OP_MAX_CNT << 4,
+       .offset_mask            = { MSR_AMD64_IBSOP_REG_MASK },
+       .offset_max             = MSR_AMD64_IBSOP_REG_COUNT,
+
+       .get_count              = get_ibs_op_count,
+};
+
+static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
+{
+       struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
+       struct perf_event *event = pcpu->event;
+       struct hw_perf_event *hwc = &event->hw;
+       struct perf_sample_data data;
+       struct perf_raw_record raw;
+       struct pt_regs regs;
+       struct perf_ibs_data ibs_data;
+       int offset, size, check_rip, offset_max, throttle = 0;
+       unsigned int msr;
+       u64 *buf, *config, period;
+
+       if (!test_bit(IBS_STARTED, pcpu->state)) {
+               /*
+                * Catch spurious interrupts after stopping IBS: After
+                * disabling IBS there could be still incomming NMIs
+                * with samples that even have the valid bit cleared.
+                * Mark all this NMIs as handled.
+                */
+               return test_and_clear_bit(IBS_STOPPING, pcpu->state) ? 1 : 0;
+       }
+
+       msr = hwc->config_base;
+       buf = ibs_data.regs;
+       rdmsrl(msr, *buf);
+       if (!(*buf++ & perf_ibs->valid_mask))
+               return 0;
+
+       config = &ibs_data.regs[0];
+       perf_ibs_event_update(perf_ibs, event, config);
+       perf_sample_data_init(&data, 0, hwc->last_period);
+       if (!perf_ibs_set_period(perf_ibs, hwc, &period))
+               goto out;       /* no sw counter overflow */
+
+       ibs_data.caps = ibs_caps;
+       size = 1;
+       offset = 1;
+       check_rip = (perf_ibs == &perf_ibs_op && (ibs_caps & IBS_CAPS_RIPINVALIDCHK));
+       if (event->attr.sample_type & PERF_SAMPLE_RAW)
+               offset_max = perf_ibs->offset_max;
+       else if (check_rip)
+               offset_max = 2;
+       else
+               offset_max = 1;
+       do {
+               rdmsrl(msr + offset, *buf++);
+               size++;
+               offset = find_next_bit(perf_ibs->offset_mask,
+                                      perf_ibs->offset_max,
+                                      offset + 1);
+       } while (offset < offset_max);
+       ibs_data.size = sizeof(u64) * size;
+
+       regs = *iregs;
+       if (check_rip && (ibs_data.regs[2] & IBS_RIP_INVALID)) {
+               regs.flags &= ~PERF_EFLAGS_EXACT;
+       } else {
+               instruction_pointer_set(&regs, ibs_data.regs[1]);
+               regs.flags |= PERF_EFLAGS_EXACT;
+       }
+
+       if (event->attr.sample_type & PERF_SAMPLE_RAW) {
+               raw.size = sizeof(u32) + ibs_data.size;
+               raw.data = ibs_data.data;
+               data.raw = &raw;
+       }
+
+       throttle = perf_event_overflow(event, &data, &regs);
+out:
+       if (throttle)
+               perf_ibs_disable_event(perf_ibs, hwc, *config);
+       else
+               perf_ibs_enable_event(perf_ibs, hwc, period >> 4);
+
+       perf_event_update_userpage(event);
+
+       return 1;
+}
+
+static int __kprobes
+perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
+{
+       int handled = 0;
+
+       handled += perf_ibs_handle_irq(&perf_ibs_fetch, regs);
+       handled += perf_ibs_handle_irq(&perf_ibs_op, regs);
+
+       if (handled)
+               inc_irq_stat(apic_perf_irqs);
+
+       return handled;
+}
+
+static __init int perf_ibs_pmu_init(struct perf_ibs *perf_ibs, char *name)
+{
+       struct cpu_perf_ibs __percpu *pcpu;
+       int ret;
+
+       pcpu = alloc_percpu(struct cpu_perf_ibs);
+       if (!pcpu)
+               return -ENOMEM;
+
+       perf_ibs->pcpu = pcpu;
+
+       ret = perf_pmu_register(&perf_ibs->pmu, name, -1);
+       if (ret) {
+               perf_ibs->pcpu = NULL;
+               free_percpu(pcpu);
+       }
+
+       return ret;
+}
+
 static __init int perf_event_ibs_init(void)
 {
        if (!ibs_caps)
                return -ENODEV; /* ibs not supported by the cpu */
 
-       perf_pmu_register(&perf_ibs, "ibs", -1);
+       perf_ibs_pmu_init(&perf_ibs_fetch, "ibs_fetch");
+       if (ibs_caps & IBS_CAPS_OPCNT)
+               perf_ibs_op.config_mask |= IBS_OP_CNT_CTL;
+       perf_ibs_pmu_init(&perf_ibs_op, "ibs_op");
+       register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs");
        printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps);
 
        return 0;
index 26b3e2f..166546e 100644 (file)
@@ -1027,8 +1027,6 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
        u64 status;
        int handled;
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
 
        /*
@@ -1082,7 +1080,7 @@ again:
                if (!intel_pmu_save_and_restart(event))
                        continue;
 
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, event->hw.last_period);
 
                if (has_branch_stack(event))
                        data.br_stack = &cpuc->lbr_stack;
index 7f64df1..5a3edc2 100644 (file)
@@ -316,8 +316,7 @@ int intel_pmu_drain_bts_buffer(void)
 
        ds->bts_index = ds->bts_buffer_base;
 
-       perf_sample_data_init(&data, 0);
-       data.period = event->hw.last_period;
+       perf_sample_data_init(&data, 0, event->hw.last_period);
        regs.ip     = 0;
 
        /*
@@ -564,8 +563,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
        if (!intel_pmu_save_and_restart(event))
                return;
 
-       perf_sample_data_init(&data, 0);
-       data.period = event->hw.last_period;
+       perf_sample_data_init(&data, 0, event->hw.last_period);
 
        /*
         * We use the interrupt regs as a base because the PEBS record
index a2dfacf..47124a7 100644 (file)
@@ -1005,8 +1005,6 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
        int idx, handled = 0;
        u64 val;
 
-       perf_sample_data_init(&data, 0);
-
        cpuc = &__get_cpu_var(cpu_hw_events);
 
        for (idx = 0; idx < x86_pmu.num_counters; idx++) {
@@ -1034,10 +1032,12 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
                handled += overflow;
 
                /* event overflow for sure */
-               data.period = event->hw.last_period;
+               perf_sample_data_init(&data, 0, hwc->last_period);
 
                if (!x86_perf_event_set_period(event))
                        continue;
+
+
                if (perf_event_overflow(event, &data, regs))
                        x86_pmu_stop(event, 0);
        }
index c9a281f..32ff365 100644 (file)
 #include <trace/syscall.h>
 
 #include <asm/cacheflush.h>
+#include <asm/kprobes.h>
 #include <asm/ftrace.h>
 #include <asm/nops.h>
-#include <asm/nmi.h>
-
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 
-/*
- * modifying_code is set to notify NMIs that they need to use
- * memory barriers when entering or exiting. But we don't want
- * to burden NMIs with unnecessary memory barriers when code
- * modification is not being done (which is most of the time).
- *
- * A mutex is already held when ftrace_arch_code_modify_prepare
- * and post_process are called. No locks need to be taken here.
- *
- * Stop machine will make sure currently running NMIs are done
- * and new NMIs will see the updated variable before we need
- * to worry about NMIs doing memory barriers.
- */
-static int modifying_code __read_mostly;
-static DEFINE_PER_CPU(int, save_modifying_code);
-
 int ftrace_arch_code_modify_prepare(void)
 {
        set_kernel_text_rw();
        set_all_modules_text_rw();
-       modifying_code = 1;
        return 0;
 }
 
 int ftrace_arch_code_modify_post_process(void)
 {
-       modifying_code = 0;
        set_all_modules_text_ro();
        set_kernel_text_ro();
        return 0;
@@ -90,134 +71,6 @@ static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
        return calc.code;
 }
 
-/*
- * Modifying code must take extra care. On an SMP machine, if
- * the code being modified is also being executed on another CPU
- * that CPU will have undefined results and possibly take a GPF.
- * We use kstop_machine to stop other CPUS from exectuing code.
- * But this does not stop NMIs from happening. We still need
- * to protect against that. We separate out the modification of
- * the code to take care of this.
- *
- * Two buffers are added: An IP buffer and a "code" buffer.
- *
- * 1) Put the instruction pointer into the IP buffer
- *    and the new code into the "code" buffer.
- * 2) Wait for any running NMIs to finish and set a flag that says
- *    we are modifying code, it is done in an atomic operation.
- * 3) Write the code
- * 4) clear the flag.
- * 5) Wait for any running NMIs to finish.
- *
- * If an NMI is executed, the first thing it does is to call
- * "ftrace_nmi_enter". This will check if the flag is set to write
- * and if it is, it will write what is in the IP and "code" buffers.
- *
- * The trick is, it does not matter if everyone is writing the same
- * content to the code location. Also, if a CPU is executing code
- * it is OK to write to that code location if the contents being written
- * are the same as what exists.
- */
-
-#define MOD_CODE_WRITE_FLAG (1 << 31)  /* set when NMI should do the write */
-static atomic_t nmi_running = ATOMIC_INIT(0);
-static int mod_code_status;            /* holds return value of text write */
-static void *mod_code_ip;              /* holds the IP to write to */
-static const void *mod_code_newcode;   /* holds the text to write to the IP */
-
-static unsigned nmi_wait_count;
-static atomic_t nmi_update_count = ATOMIC_INIT(0);
-
-int ftrace_arch_read_dyn_info(char *buf, int size)
-{
-       int r;
-
-       r = snprintf(buf, size, "%u %u",
-                    nmi_wait_count,
-                    atomic_read(&nmi_update_count));
-       return r;
-}
-
-static void clear_mod_flag(void)
-{
-       int old = atomic_read(&nmi_running);
-
-       for (;;) {
-               int new = old & ~MOD_CODE_WRITE_FLAG;
-
-               if (old == new)
-                       break;
-
-               old = atomic_cmpxchg(&nmi_running, old, new);
-       }
-}
-
-static void ftrace_mod_code(void)
-{
-       /*
-        * Yes, more than one CPU process can be writing to mod_code_status.
-        *    (and the code itself)
-        * But if one were to fail, then they all should, and if one were
-        * to succeed, then they all should.
-        */
-       mod_code_status = probe_kernel_write(mod_code_ip, mod_code_newcode,
-                                            MCOUNT_INSN_SIZE);
-
-       /* if we fail, then kill any new writers */
-       if (mod_code_status)
-               clear_mod_flag();
-}
-
-void ftrace_nmi_enter(void)
-{
-       __this_cpu_write(save_modifying_code, modifying_code);
-
-       if (!__this_cpu_read(save_modifying_code))
-               return;
-
-       if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) {
-               smp_rmb();
-               ftrace_mod_code();
-               atomic_inc(&nmi_update_count);
-       }
-       /* Must have previous changes seen before executions */
-       smp_mb();
-}
-
-void ftrace_nmi_exit(void)
-{
-       if (!__this_cpu_read(save_modifying_code))
-               return;
-
-       /* Finish all executions before clearing nmi_running */
-       smp_mb();
-       atomic_dec(&nmi_running);
-}
-
-static void wait_for_nmi_and_set_mod_flag(void)
-{
-       if (!atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG))
-               return;
-
-       do {
-               cpu_relax();
-       } while (atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG));
-
-       nmi_wait_count++;
-}
-
-static void wait_for_nmi(void)
-{
-       if (!atomic_read(&nmi_running))
-               return;
-
-       do {
-               cpu_relax();
-       } while (atomic_read(&nmi_running));
-
-       nmi_wait_count++;
-}
-
 static inline int
 within(unsigned long addr, unsigned long start, unsigned long end)
 {
@@ -238,26 +91,7 @@ do_ftrace_mod_code(unsigned long ip, const void *new_code)
        if (within(ip, (unsigned long)_text, (unsigned long)_etext))
                ip = (unsigned long)__va(__pa(ip));
 
-       mod_code_ip = (void *)ip;
-       mod_code_newcode = new_code;
-
-       /* The buffers need to be visible before we let NMIs write them */
-       smp_mb();
-
-       wait_for_nmi_and_set_mod_flag();
-
-       /* Make sure all running NMIs have finished before we write the code */
-       smp_mb();
-
-       ftrace_mod_code();
-
-       /* Make sure the write happens before clearing the bit */
-       smp_mb();
-
-       clear_mod_flag();
-       wait_for_nmi();
-
-       return mod_code_status;
+       return probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE);
 }
 
 static const unsigned char *ftrace_nop_replace(void)
@@ -334,6 +168,336 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
        return ret;
 }
 
+int modifying_ftrace_code __read_mostly;
+
+/*
+ * A breakpoint was added to the code address we are about to
+ * modify, and this is the handle that will just skip over it.
+ * We are either changing a nop into a trace call, or a trace
+ * call to a nop. While the change is taking place, we treat
+ * it just like it was a nop.
+ */
+int ftrace_int3_handler(struct pt_regs *regs)
+{
+       if (WARN_ON_ONCE(!regs))
+               return 0;
+
+       if (!ftrace_location(regs->ip - 1))
+               return 0;
+
+       regs->ip += MCOUNT_INSN_SIZE - 1;
+
+       return 1;
+}
+
+static int ftrace_write(unsigned long ip, const char *val, int size)
+{
+       /*
+        * On x86_64, kernel text mappings are mapped read-only with
+        * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead
+        * of the kernel text mapping to modify the kernel text.
+        *
+        * For 32bit kernels, these mappings are same and we can use
+        * kernel identity mapping to modify code.
+        */
+       if (within(ip, (unsigned long)_text, (unsigned long)_etext))
+               ip = (unsigned long)__va(__pa(ip));
+
+       return probe_kernel_write((void *)ip, val, size);
+}
+
+static int add_break(unsigned long ip, const char *old)
+{
+       unsigned char replaced[MCOUNT_INSN_SIZE];
+       unsigned char brk = BREAKPOINT_INSTRUCTION;
+
+       if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
+               return -EFAULT;
+
+       /* Make sure it is what we expect it to be */
+       if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0)
+               return -EINVAL;
+
+       if (ftrace_write(ip, &brk, 1))
+               return -EPERM;
+
+       return 0;
+}
+
+static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+       unsigned const char *old;
+       unsigned long ip = rec->ip;
+
+       old = ftrace_call_replace(ip, addr);
+
+       return add_break(rec->ip, old);
+}
+
+
+static int add_brk_on_nop(struct dyn_ftrace *rec)
+{
+       unsigned const char *old;
+
+       old = ftrace_nop_replace();
+
+       return add_break(rec->ip, old);
+}
+
+static int add_breakpoints(struct dyn_ftrace *rec, int enable)
+{
+       unsigned long ftrace_addr;
+       int ret;
+
+       ret = ftrace_test_record(rec, enable);
+
+       ftrace_addr = (unsigned long)FTRACE_ADDR;
+
+       switch (ret) {
+       case FTRACE_UPDATE_IGNORE:
+               return 0;
+
+       case FTRACE_UPDATE_MAKE_CALL:
+               /* converting nop to call */
+               return add_brk_on_nop(rec);
+
+       case FTRACE_UPDATE_MAKE_NOP:
+               /* converting a call to a nop */
+               return add_brk_on_call(rec, ftrace_addr);
+       }
+       return 0;
+}
+
+/*
+ * On error, we need to remove breakpoints. This needs to
+ * be done caefully. If the address does not currently have a
+ * breakpoint, we know we are done. Otherwise, we look at the
+ * remaining 4 bytes of the instruction. If it matches a nop
+ * we replace the breakpoint with the nop. Otherwise we replace
+ * it with the call instruction.
+ */
+static int remove_breakpoint(struct dyn_ftrace *rec)
+{
+       unsigned char ins[MCOUNT_INSN_SIZE];
+       unsigned char brk = BREAKPOINT_INSTRUCTION;
+       const unsigned char *nop;
+       unsigned long ftrace_addr;
+       unsigned long ip = rec->ip;
+
+       /* If we fail the read, just give up */
+       if (probe_kernel_read(ins, (void *)ip, MCOUNT_INSN_SIZE))
+               return -EFAULT;
+
+       /* If this does not have a breakpoint, we are done */
+       if (ins[0] != brk)
+               return -1;
+
+       nop = ftrace_nop_replace();
+
+       /*
+        * If the last 4 bytes of the instruction do not match
+        * a nop, then we assume that this is a call to ftrace_addr.
+        */
+       if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) {
+               /*
+                * For extra paranoidism, we check if the breakpoint is on
+                * a call that would actually jump to the ftrace_addr.
+                * If not, don't touch the breakpoint, we make just create
+                * a disaster.
+                */
+               ftrace_addr = (unsigned long)FTRACE_ADDR;
+               nop = ftrace_call_replace(ip, ftrace_addr);
+
+               if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0)
+                       return -EINVAL;
+       }
+
+       return probe_kernel_write((void *)ip, &nop[0], 1);
+}
+
+static int add_update_code(unsigned long ip, unsigned const char *new)
+{
+       /* skip breakpoint */
+       ip++;
+       new++;
+       if (ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1))
+               return -EPERM;
+       return 0;
+}
+
+static int add_update_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+       unsigned long ip = rec->ip;
+       unsigned const char *new;
+
+       new = ftrace_call_replace(ip, addr);
+       return add_update_code(ip, new);
+}
+
+static int add_update_nop(struct dyn_ftrace *rec)
+{
+       unsigned long ip = rec->ip;
+       unsigned const char *new;
+
+       new = ftrace_nop_replace();
+       return add_update_code(ip, new);
+}
+
+static int add_update(struct dyn_ftrace *rec, int enable)
+{
+       unsigned long ftrace_addr;
+       int ret;
+
+       ret = ftrace_test_record(rec, enable);
+
+       ftrace_addr = (unsigned long)FTRACE_ADDR;
+
+       switch (ret) {
+       case FTRACE_UPDATE_IGNORE:
+               return 0;
+
+       case FTRACE_UPDATE_MAKE_CALL:
+               /* converting nop to call */
+               return add_update_call(rec, ftrace_addr);
+
+       case FTRACE_UPDATE_MAKE_NOP:
+               /* converting a call to a nop */
+               return add_update_nop(rec);
+       }
+
+       return 0;
+}
+
+static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+       unsigned long ip = rec->ip;
+       unsigned const char *new;
+
+       new = ftrace_call_replace(ip, addr);
+
+       if (ftrace_write(ip, new, 1))
+               return -EPERM;
+
+       return 0;
+}
+
+static int finish_update_nop(struct dyn_ftrace *rec)
+{
+       unsigned long ip = rec->ip;
+       unsigned const char *new;
+
+       new = ftrace_nop_replace();
+
+       if (ftrace_write(ip, new, 1))
+               return -EPERM;
+       return 0;
+}
+
+static int finish_update(struct dyn_ftrace *rec, int enable)
+{
+       unsigned long ftrace_addr;
+       int ret;
+
+       ret = ftrace_update_record(rec, enable);
+
+       ftrace_addr = (unsigned long)FTRACE_ADDR;
+
+       switch (ret) {
+       case FTRACE_UPDATE_IGNORE:
+               return 0;
+
+       case FTRACE_UPDATE_MAKE_CALL:
+               /* converting nop to call */
+               return finish_update_call(rec, ftrace_addr);
+
+       case FTRACE_UPDATE_MAKE_NOP:
+               /* converting a call to a nop */
+               return finish_update_nop(rec);
+       }
+
+       return 0;
+}
+
+static void do_sync_core(void *data)
+{
+       sync_core();
+}
+
+static void run_sync(void)
+{
+       int enable_irqs = irqs_disabled();
+
+       /* We may be called with interrupts disbled (on bootup). */
+       if (enable_irqs)
+               local_irq_enable();
+       on_each_cpu(do_sync_core, NULL, 1);
+       if (enable_irqs)
+               local_irq_disable();
+}
+
+void ftrace_replace_code(int enable)
+{
+       struct ftrace_rec_iter *iter;
+       struct dyn_ftrace *rec;
+       const char *report = "adding breakpoints";
+       int count = 0;
+       int ret;
+
+       for_ftrace_rec_iter(iter) {
+               rec = ftrace_rec_iter_record(iter);
+
+               ret = add_breakpoints(rec, enable);
+               if (ret)
+                       goto remove_breakpoints;
+               count++;
+       }
+
+       run_sync();
+
+       report = "updating code";
+
+       for_ftrace_rec_iter(iter) {
+               rec = ftrace_rec_iter_record(iter);
+
+               ret = add_update(rec, enable);
+               if (ret)
+                       goto remove_breakpoints;
+       }
+
+       run_sync();
+
+       report = "removing breakpoints";
+
+       for_ftrace_rec_iter(iter) {
+               rec = ftrace_rec_iter_record(iter);
+
+               ret = finish_update(rec, enable);
+               if (ret)
+                       goto remove_breakpoints;
+       }
+
+       run_sync();
+
+       return;
+
+ remove_breakpoints:
+       ftrace_bug(ret, rec ? rec->ip : 0);
+       printk(KERN_WARNING "Failed on %s (%d):\n", report, count);
+       for_ftrace_rec_iter(iter) {
+               rec = ftrace_rec_iter_record(iter);
+               remove_breakpoint(rec);
+       }
+}
+
+void arch_ftrace_update_code(int command)
+{
+       modifying_ftrace_code++;
+
+       ftrace_modify_all_code(command);
+
+       modifying_ftrace_code--;
+}
+
 int __init ftrace_dyn_arch_init(void *data)
 {
        /* The return code is retured via data */
index 585be4b..a1faed5 100644 (file)
@@ -84,7 +84,7 @@ __setup("unknown_nmi_panic", setup_unknown_nmi_panic);
 
 #define nmi_to_desc(type) (&nmi_desc[type])
 
-static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)
+static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)
 {
        struct nmi_desc *desc = nmi_to_desc(type);
        struct nmiaction *a;
@@ -166,7 +166,7 @@ void unregister_nmi_handler(unsigned int type, const char *name)
 }
 EXPORT_SYMBOL_GPL(unregister_nmi_handler);
 
-static notrace __kprobes void
+static __kprobes void
 pci_serr_error(unsigned char reason, struct pt_regs *regs)
 {
        /* check to see if anyone registered against these types of errors */
@@ -197,7 +197,7 @@ pci_serr_error(unsigned char reason, struct pt_regs *regs)
        outb(reason, NMI_REASON_PORT);
 }
 
-static notrace __kprobes void
+static __kprobes void
 io_check_error(unsigned char reason, struct pt_regs *regs)
 {
        unsigned long i;
@@ -228,7 +228,7 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
        outb(reason, NMI_REASON_PORT);
 }
 
-static notrace __kprobes void
+static __kprobes void
 unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 {
        int handled;
@@ -270,7 +270,7 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 static DEFINE_PER_CPU(bool, swallow_nmi);
 static DEFINE_PER_CPU(unsigned long, last_nmi_rip);
 
-static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
+static __kprobes void default_do_nmi(struct pt_regs *regs)
 {
        unsigned char reason = 0;
        int handled;
index ff9281f..92d5756 100644 (file)
@@ -50,6 +50,7 @@
 #include <asm/processor.h>
 #include <asm/debugreg.h>
 #include <linux/atomic.h>
+#include <asm/ftrace.h>
 #include <asm/traps.h>
 #include <asm/desc.h>
 #include <asm/i387.h>
@@ -303,8 +304,13 @@ gp_in_kernel:
 }
 
 /* May run on IST stack. */
-dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
+dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code)
 {
+#ifdef CONFIG_DYNAMIC_FTRACE
+       /* ftrace must be first, everything else may cause a recursive crash */
+       if (unlikely(modifying_ftrace_code) && ftrace_int3_handler(regs))
+               return;
+#endif
 #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
        if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
                                SIGTRAP) == NOTIFY_STOP)
index 8aeadf6..4e2e1cc 100644 (file)
        CPU_DISCARD(init.data)                                          \
        MEM_DISCARD(init.data)                                          \
        KERNEL_CTORS()                                                  \
-       *(.init.rodata)                                                 \
        MCOUNT_REC()                                                    \
+       *(.init.rodata)                                                 \
        FTRACE_EVENTS()                                                 \
        TRACE_SYSCALLS()                                                \
        DEV_DISCARD(init.rodata)                                        \
index 72a6cab..55e6d63 100644 (file)
@@ -286,10 +286,16 @@ struct ftrace_rec_iter *ftrace_rec_iter_start(void);
 struct ftrace_rec_iter *ftrace_rec_iter_next(struct ftrace_rec_iter *iter);
 struct dyn_ftrace *ftrace_rec_iter_record(struct ftrace_rec_iter *iter);
 
+#define for_ftrace_rec_iter(iter)              \
+       for (iter = ftrace_rec_iter_start();    \
+            iter;                              \
+            iter = ftrace_rec_iter_next(iter))
+
+
 int ftrace_update_record(struct dyn_ftrace *rec, int enable);
 int ftrace_test_record(struct dyn_ftrace *rec, int enable);
 void ftrace_run_stop_machine(int command);
-int ftrace_location(unsigned long ip);
+unsigned long ftrace_location(unsigned long ip);
 
 extern ftrace_func_t ftrace_trace_function;
 
@@ -308,11 +314,14 @@ ftrace_set_early_filter(struct ftrace_ops *ops, char *buf, int enable);
 /* defined in arch */
 extern int ftrace_ip_converted(unsigned long ip);
 extern int ftrace_dyn_arch_init(void *data);
+extern void ftrace_replace_code(int enable);
 extern int ftrace_update_ftrace_func(ftrace_func_t func);
 extern void ftrace_caller(void);
 extern void ftrace_call(void);
 extern void mcount_call(void);
 
+void ftrace_modify_all_code(int command);
+
 #ifndef FTRACE_ADDR
 #define FTRACE_ADDR ((unsigned long)ftrace_caller)
 #endif
@@ -485,8 +494,12 @@ static inline void __ftrace_enabled_restore(int enabled)
   extern void trace_preempt_on(unsigned long a0, unsigned long a1);
   extern void trace_preempt_off(unsigned long a0, unsigned long a1);
 #else
-  static inline void trace_preempt_on(unsigned long a0, unsigned long a1) { }
-  static inline void trace_preempt_off(unsigned long a0, unsigned long a1) { }
+/*
+ * Use defines instead of static inlines because some arches will make code out
+ * of the CALLER_ADDR, when we really want these to be a real nop.
+ */
+# define trace_preempt_on(a0, a1) do { } while (0)
+# define trace_preempt_off(a0, a1) do { } while (0)
 #endif
 
 #ifdef CONFIG_FTRACE_MCOUNT_RECORD
index 645231c..c0d3442 100644 (file)
@@ -480,15 +480,16 @@ do {                                                                      \
 
 #define trace_printk(fmt, args...)                                     \
 do {                                                                   \
+       static const char *trace_printk_fmt                             \
+               __attribute__((section("__trace_printk_fmt"))) =        \
+               __builtin_constant_p(fmt) ? fmt : NULL;                 \
+                                                                       \
        __trace_printk_check_format(fmt, ##args);                       \
-       if (__builtin_constant_p(fmt)) {                                \
-               static const char *trace_printk_fmt                     \
-                 __attribute__((section("__trace_printk_fmt"))) =      \
-                       __builtin_constant_p(fmt) ? fmt : NULL;         \
                                                                        \
+       if (__builtin_constant_p(fmt))                                  \
                __trace_bprintk(_THIS_IP_, trace_printk_fmt, ##args);   \
-       } else                                                          \
-               __trace_printk(_THIS_IP_, fmt, ##args);         \
+       else                                                            \
+               __trace_printk(_THIS_IP_, fmt, ##args);                 \
 } while (0)
 
 extern __printf(2, 3)
index ddbb6a9..8adf70e 100644 (file)
@@ -1084,10 +1084,8 @@ extern void perf_pmu_unregister(struct pmu *pmu);
 
 extern int perf_num_counters(void);
 extern const char *perf_pmu_name(void);
-extern void __perf_event_task_sched_in(struct task_struct *prev,
-                                      struct task_struct *task);
-extern void __perf_event_task_sched_out(struct task_struct *prev,
-                                       struct task_struct *next);
+extern void __perf_event_task_sched(struct task_struct *prev,
+                                   struct task_struct *next);
 extern int perf_event_init_task(struct task_struct *child);
 extern void perf_event_exit_task(struct task_struct *child);
 extern void perf_event_free_task(struct task_struct *task);
@@ -1132,11 +1130,14 @@ struct perf_sample_data {
        struct perf_branch_stack        *br_stack;
 };
 
-static inline void perf_sample_data_init(struct perf_sample_data *data, u64 addr)
+static inline void perf_sample_data_init(struct perf_sample_data *data,
+                                        u64 addr, u64 period)
 {
+       /* remaining struct members initialized in perf_prepare_sample() */
        data->addr = addr;
        data->raw  = NULL;
        data->br_stack = NULL;
+       data->period    = period;
 }
 
 extern void perf_output_sample(struct perf_output_handle *handle,
@@ -1204,20 +1205,13 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
 
 extern struct static_key_deferred perf_sched_events;
 
-static inline void perf_event_task_sched_in(struct task_struct *prev,
+static inline void perf_event_task_sched(struct task_struct *prev,
                                            struct task_struct *task)
-{
-       if (static_key_false(&perf_sched_events.key))
-               __perf_event_task_sched_in(prev, task);
-}
-
-static inline void perf_event_task_sched_out(struct task_struct *prev,
-                                            struct task_struct *next)
 {
        perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0);
 
        if (static_key_false(&perf_sched_events.key))
-               __perf_event_task_sched_out(prev, next);
+               __perf_event_task_sched(prev, task);
 }
 
 extern void perf_event_mmap(struct vm_area_struct *vma);
@@ -1292,11 +1286,8 @@ extern void perf_event_disable(struct perf_event *event);
 extern void perf_event_task_tick(void);
 #else
 static inline void
-perf_event_task_sched_in(struct task_struct *prev,
-                        struct task_struct *task)                      { }
-static inline void
-perf_event_task_sched_out(struct task_struct *prev,
-                         struct task_struct *next)                     { }
+perf_event_task_sched(struct task_struct *prev,
+                     struct task_struct *task)                         { }
 static inline int perf_event_init_task(struct task_struct *child)      { return 0; }
 static inline void perf_event_exit_task(struct task_struct *child)     { }
 static inline void perf_event_free_task(struct task_struct *task)      { }
index 7be2e88..6c8835f 100644 (file)
@@ -96,9 +96,11 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k
        __ring_buffer_alloc((size), (flags), &__key);   \
 })
 
+#define RING_BUFFER_ALL_CPUS -1
+
 void ring_buffer_free(struct ring_buffer *buffer);
 
-int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size);
+int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size, int cpu);
 
 void ring_buffer_change_overwrite(struct ring_buffer *buffer, int val);
 
@@ -129,7 +131,7 @@ ring_buffer_read(struct ring_buffer_iter *iter, u64 *ts);
 void ring_buffer_iter_reset(struct ring_buffer_iter *iter);
 int ring_buffer_iter_empty(struct ring_buffer_iter *iter);
 
-unsigned long ring_buffer_size(struct ring_buffer *buffer);
+unsigned long ring_buffer_size(struct ring_buffer *buffer, int cpu);
 
 void ring_buffer_reset_cpu(struct ring_buffer *buffer, int cpu);
 void ring_buffer_reset(struct ring_buffer *buffer);
index 6d18ef8..da2d2d2 100644 (file)
@@ -1198,7 +1198,7 @@ menu "Kernel Performance Events And Counters"
 
 config PERF_EVENTS
        bool "Kernel performance events and counters"
-       default y if (PROFILING || PERF_COUNTERS)
+       default y if PROFILING
        depends on HAVE_PERF_EVENTS
        select ANON_INODES
        select IRQ_WORK
@@ -1225,18 +1225,6 @@ config PERF_EVENTS
 
          Say Y if unsure.
 
-config PERF_COUNTERS
-       bool "Kernel performance counters (old config option)"
-       depends on HAVE_PERF_EVENTS
-       help
-         This config has been obsoleted by the PERF_EVENTS
-         config option - please see that one for details.
-
-         It has no effect on the kernel whether you enable
-         it or not, it is a compatibility placeholder.
-
-         Say N if unsure.
-
 config DEBUG_PERF_USE_VMALLOC
        default n
        bool "Debug: use vmalloc to back perf mmap() buffers"
index fd126f8..91a4459 100644 (file)
@@ -2039,8 +2039,8 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
  * accessing the event control register. If a NMI hits, then it will
  * not restart the event.
  */
-void __perf_event_task_sched_out(struct task_struct *task,
-                                struct task_struct *next)
+static void __perf_event_task_sched_out(struct task_struct *task,
+                                       struct task_struct *next)
 {
        int ctxn;
 
@@ -2279,8 +2279,8 @@ static void perf_branch_stack_sched_in(struct task_struct *prev,
  * accessing the event control register. If a NMI hits, then it will
  * keep the event running.
  */
-void __perf_event_task_sched_in(struct task_struct *prev,
-                               struct task_struct *task)
+static void __perf_event_task_sched_in(struct task_struct *prev,
+                                      struct task_struct *task)
 {
        struct perf_event_context *ctx;
        int ctxn;
@@ -2305,6 +2305,12 @@ void __perf_event_task_sched_in(struct task_struct *prev,
                perf_branch_stack_sched_in(prev, task);
 }
 
+void __perf_event_task_sched(struct task_struct *prev, struct task_struct *next)
+{
+       __perf_event_task_sched_out(prev, next);
+       __perf_event_task_sched_in(prev, next);
+}
+
 static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count)
 {
        u64 frequency = event->attr.sample_freq;
@@ -4957,7 +4963,7 @@ void __perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
        if (rctx < 0)
                return;
 
-       perf_sample_data_init(&data, addr);
+       perf_sample_data_init(&data, addr, 0);
 
        do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, &data, regs);
 
@@ -5215,7 +5221,7 @@ void perf_tp_event(u64 addr, u64 count, void *record, int entry_size,
                .data = record,
        };
 
-       perf_sample_data_init(&data, addr);
+       perf_sample_data_init(&data, addr, 0);
        data.raw = &raw;
 
        hlist_for_each_entry_rcu(event, node, head, hlist_entry) {
@@ -5318,7 +5324,7 @@ void perf_bp_event(struct perf_event *bp, void *data)
        struct perf_sample_data sample;
        struct pt_regs *regs = data;
 
-       perf_sample_data_init(&sample, bp->attr.bp_addr);
+       perf_sample_data_init(&sample, bp->attr.bp_addr, 0);
 
        if (!bp->hw.state && !perf_exclude_event(bp, regs))
                perf_swevent_event(bp, 1, &sample, regs);
@@ -5344,13 +5350,12 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
 
        event->pmu->read(event);
 
-       perf_sample_data_init(&data, 0);
-       data.period = event->hw.last_period;
+       perf_sample_data_init(&data, 0, event->hw.last_period);
        regs = get_irq_regs();
 
        if (regs && !perf_exclude_event(event, regs)) {
                if (!(event->attr.exclude_idle && is_idle_task(current)))
-                       if (perf_event_overflow(event, &data, regs))
+                       if (__perf_event_overflow(event, 1, &data, regs))
                                ret = HRTIMER_NORESTART;
        }
 
index 03667c3..d2e2e17 100644 (file)
@@ -1914,7 +1914,7 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev,
                    struct task_struct *next)
 {
        sched_info_switch(prev, next);
-       perf_event_task_sched_out(prev, next);
+       perf_event_task_sched(prev, next);
        fire_sched_out_preempt_notifiers(prev, next);
        prepare_lock_switch(rq, next);
        prepare_arch_switch(next);
@@ -1957,13 +1957,6 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
         */
        prev_state = prev->state;
        finish_arch_switch(prev);
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
-       local_irq_disable();
-#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
-       perf_event_task_sched_in(prev, current);
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
-       local_irq_enable();
-#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
        finish_lock_switch(rq, prev);
        finish_arch_post_lock_switch();
 
index a1d2849..d81a1a5 100644 (file)
@@ -141,7 +141,6 @@ if FTRACE
 config FUNCTION_TRACER
        bool "Kernel Function Tracer"
        depends on HAVE_FUNCTION_TRACER
-       select FRAME_POINTER if !ARM_UNWIND && !PPC && !S390 && !MICROBLAZE
        select KALLSYMS
        select GENERIC_TRACER
        select CONTEXT_SWITCH_TRACER
index 0fa92f6..a008663 100644 (file)
@@ -1383,44 +1383,73 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip)
 
 static int ftrace_cmp_recs(const void *a, const void *b)
 {
-       const struct dyn_ftrace *reca = a;
-       const struct dyn_ftrace *recb = b;
+       const struct dyn_ftrace *key = a;
+       const struct dyn_ftrace *rec = b;
 
-       if (reca->ip > recb->ip)
-               return 1;
-       if (reca->ip < recb->ip)
+       if (key->flags < rec->ip)
                return -1;
+       if (key->ip >= rec->ip + MCOUNT_INSN_SIZE)
+               return 1;
        return 0;
 }
 
-/**
- * ftrace_location - return true if the ip giving is a traced location
- * @ip: the instruction pointer to check
- *
- * Returns 1 if @ip given is a pointer to a ftrace location.
- * That is, the instruction that is either a NOP or call to
- * the function tracer. It checks the ftrace internal tables to
- * determine if the address belongs or not.
- */
-int ftrace_location(unsigned long ip)
+static unsigned long ftrace_location_range(unsigned long start, unsigned long end)
 {
        struct ftrace_page *pg;
        struct dyn_ftrace *rec;
        struct dyn_ftrace key;
 
-       key.ip = ip;
+       key.ip = start;
+       key.flags = end;        /* overload flags, as it is unsigned long */
 
        for (pg = ftrace_pages_start; pg; pg = pg->next) {
+               if (end < pg->records[0].ip ||
+                   start >= (pg->records[pg->index - 1].ip + MCOUNT_INSN_SIZE))
+                       continue;
                rec = bsearch(&key, pg->records, pg->index,
                              sizeof(struct dyn_ftrace),
                              ftrace_cmp_recs);
                if (rec)
-                       return 1;
+                       return rec->ip;
        }
 
        return 0;
 }
 
+/**
+ * ftrace_location - return true if the ip giving is a traced location
+ * @ip: the instruction pointer to check
+ *
+ * Returns rec->ip if @ip given is a pointer to a ftrace location.
+ * That is, the instruction that is either a NOP or call to
+ * the function tracer. It checks the ftrace internal tables to
+ * determine if the address belongs or not.
+ */
+unsigned long ftrace_location(unsigned long ip)
+{
+       return ftrace_location_range(ip, ip);
+}
+
+/**
+ * ftrace_text_reserved - return true if range contains an ftrace location
+ * @start: start of range to search
+ * @end: end of range to search (inclusive). @end points to the last byte to check.
+ *
+ * Returns 1 if @start and @end contains a ftrace location.
+ * That is, the instruction that is either a NOP or call to
+ * the function tracer. It checks the ftrace internal tables to
+ * determine if the address belongs or not.
+ */
+int ftrace_text_reserved(void *start, void *end)
+{
+       unsigned long ret;
+
+       ret = ftrace_location_range((unsigned long)start,
+                                   (unsigned long)end);
+
+       return (int)!!ret;
+}
+
 static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
                                     int filter_hash,
                                     bool inc)
@@ -1520,35 +1549,6 @@ static void ftrace_hash_rec_enable(struct ftrace_ops *ops,
        __ftrace_hash_rec_update(ops, filter_hash, 1);
 }
 
-static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip)
-{
-       if (ftrace_pages->index == ftrace_pages->size) {
-               /* We should have allocated enough */
-               if (WARN_ON(!ftrace_pages->next))
-                       return NULL;
-               ftrace_pages = ftrace_pages->next;
-       }
-
-       return &ftrace_pages->records[ftrace_pages->index++];
-}
-
-static struct dyn_ftrace *
-ftrace_record_ip(unsigned long ip)
-{
-       struct dyn_ftrace *rec;
-
-       if (ftrace_disabled)
-               return NULL;
-
-       rec = ftrace_alloc_dyn_node(ip);
-       if (!rec)
-               return NULL;
-
-       rec->ip = ip;
-
-       return rec;
-}
-
 static void print_ip_ins(const char *fmt, unsigned char *p)
 {
        int i;
@@ -1598,21 +1598,6 @@ void ftrace_bug(int failed, unsigned long ip)
        }
 }
 
-
-/* Return 1 if the address range is reserved for ftrace */
-int ftrace_text_reserved(void *start, void *end)
-{
-       struct dyn_ftrace *rec;
-       struct ftrace_page *pg;
-
-       do_for_each_ftrace_rec(pg, rec) {
-               if (rec->ip <= (unsigned long)end &&
-                   rec->ip + MCOUNT_INSN_SIZE > (unsigned long)start)
-                       return 1;
-       } while_for_each_ftrace_rec();
-       return 0;
-}
-
 static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
 {
        unsigned long flag = 0UL;
@@ -1698,7 +1683,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
        return -1; /* unknow ftrace bug */
 }
 
-static void ftrace_replace_code(int update)
+void __weak ftrace_replace_code(int enable)
 {
        struct dyn_ftrace *rec;
        struct ftrace_page *pg;
@@ -1708,7 +1693,7 @@ static void ftrace_replace_code(int update)
                return;
 
        do_for_each_ftrace_rec(pg, rec) {
-               failed = __ftrace_replace_code(rec, update);
+               failed = __ftrace_replace_code(rec, enable);
                if (failed) {
                        ftrace_bug(failed, rec->ip);
                        /* Stop processing */
@@ -1826,22 +1811,27 @@ int __weak ftrace_arch_code_modify_post_process(void)
        return 0;
 }
 
-static int __ftrace_modify_code(void *data)
+void ftrace_modify_all_code(int command)
 {
-       int *command = data;
-
-       if (*command & FTRACE_UPDATE_CALLS)
+       if (command & FTRACE_UPDATE_CALLS)
                ftrace_replace_code(1);
-       else if (*command & FTRACE_DISABLE_CALLS)
+       else if (command & FTRACE_DISABLE_CALLS)
                ftrace_replace_code(0);
 
-       if (*command & FTRACE_UPDATE_TRACE_FUNC)
+       if (command & FTRACE_UPDATE_TRACE_FUNC)
                ftrace_update_ftrace_func(ftrace_trace_function);
 
-       if (*command & FTRACE_START_FUNC_RET)
+       if (command & FTRACE_START_FUNC_RET)
                ftrace_enable_ftrace_graph_caller();
-       else if (*command & FTRACE_STOP_FUNC_RET)
+       else if (command & FTRACE_STOP_FUNC_RET)
                ftrace_disable_ftrace_graph_caller();
+}
+
+static int __ftrace_modify_code(void *data)
+{
+       int *command = data;
+
+       ftrace_modify_all_code(*command);
 
        return 0;
 }
@@ -2469,57 +2459,35 @@ static int
 ftrace_avail_open(struct inode *inode, struct file *file)
 {
        struct ftrace_iterator *iter;
-       int ret;
 
        if (unlikely(ftrace_disabled))
                return -ENODEV;
 
-       iter = kzalloc(sizeof(*iter), GFP_KERNEL);
-       if (!iter)
-               return -ENOMEM;
-
-       iter->pg = ftrace_pages_start;
-       iter->ops = &global_ops;
-
-       ret = seq_open(file, &show_ftrace_seq_ops);
-       if (!ret) {
-               struct seq_file *m = file->private_data;
-
-               m->private = iter;
-       } else {
-               kfree(iter);
+       iter = __seq_open_private(file, &show_ftrace_seq_ops, sizeof(*iter));
+       if (iter) {
+               iter->pg = ftrace_pages_start;
+               iter->ops = &global_ops;
        }
 
-       return ret;
+       return iter ? 0 : -ENOMEM;
 }
 
 static int
 ftrace_enabled_open(struct inode *inode, struct file *file)
 {
        struct ftrace_iterator *iter;
-       int ret;
 
        if (unlikely(ftrace_disabled))
                return -ENODEV;
 
-       iter = kzalloc(sizeof(*iter), GFP_KERNEL);
-       if (!iter)
-               return -ENOMEM;
-
-       iter->pg = ftrace_pages_start;
-       iter->flags = FTRACE_ITER_ENABLED;
-       iter->ops = &global_ops;
-
-       ret = seq_open(file, &show_ftrace_seq_ops);
-       if (!ret) {
-               struct seq_file *m = file->private_data;
-
-               m->private = iter;
-       } else {
-               kfree(iter);
+       iter = __seq_open_private(file, &show_ftrace_seq_ops, sizeof(*iter));
+       if (iter) {
+               iter->pg = ftrace_pages_start;
+               iter->flags = FTRACE_ITER_ENABLED;
+               iter->ops = &global_ops;
        }
 
-       return ret;
+       return iter ? 0 : -ENOMEM;
 }
 
 static void ftrace_filter_reset(struct ftrace_hash *hash)
@@ -3688,22 +3656,36 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
        return 0;
 }
 
-static void ftrace_swap_recs(void *a, void *b, int size)
+static int ftrace_cmp_ips(const void *a, const void *b)
+{
+       const unsigned long *ipa = a;
+       const unsigned long *ipb = b;
+
+       if (*ipa > *ipb)
+               return 1;
+       if (*ipa < *ipb)
+               return -1;
+       return 0;
+}
+
+static void ftrace_swap_ips(void *a, void *b, int size)
 {
-       struct dyn_ftrace *reca = a;
-       struct dyn_ftrace *recb = b;
-       struct dyn_ftrace t;
+       unsigned long *ipa = a;
+       unsigned long *ipb = b;
+       unsigned long t;
 
-       t = *reca;
-       *reca = *recb;
-       *recb = t;
+       t = *ipa;
+       *ipa = *ipb;
+       *ipb = t;
 }
 
 static int ftrace_process_locs(struct module *mod,
                               unsigned long *start,
                               unsigned long *end)
 {
+       struct ftrace_page *start_pg;
        struct ftrace_page *pg;
+       struct dyn_ftrace *rec;
        unsigned long count;
        unsigned long *p;
        unsigned long addr;
@@ -3715,8 +3697,11 @@ static int ftrace_process_locs(struct module *mod,
        if (!count)
                return 0;
 
-       pg = ftrace_allocate_pages(count);
-       if (!pg)
+       sort(start, count, sizeof(*start),
+            ftrace_cmp_ips, ftrace_swap_ips);
+
+       start_pg = ftrace_allocate_pages(count);
+       if (!start_pg)
                return -ENOMEM;
 
        mutex_lock(&ftrace_lock);
@@ -3729,7 +3714,7 @@ static int ftrace_process_locs(struct module *mod,
        if (!mod) {
                WARN_ON(ftrace_pages || ftrace_pages_start);
                /* First initialization */
-               ftrace_pages = ftrace_pages_start = pg;
+               ftrace_pages = ftrace_pages_start = start_pg;
        } else {
                if (!ftrace_pages)
                        goto out;
@@ -3740,11 +3725,11 @@ static int ftrace_process_locs(struct module *mod,
                                ftrace_pages = ftrace_pages->next;
                }
 
-               ftrace_pages->next = pg;
-               ftrace_pages = pg;
+               ftrace_pages->next = start_pg;
        }
 
        p = start;
+       pg = start_pg;
        while (p < end) {
                addr = ftrace_call_adjust(*p++);
                /*
@@ -3755,17 +3740,26 @@ static int ftrace_process_locs(struct module *mod,
                 */
                if (!addr)
                        continue;
-               if (!ftrace_record_ip(addr))
-                       break;
+
+               if (pg->index == pg->size) {
+                       /* We should have allocated enough */
+                       if (WARN_ON(!pg->next))
+                               break;
+                       pg = pg->next;
+               }
+
+               rec = &pg->records[pg->index++];
+               rec->ip = addr;
        }
 
-       /* These new locations need to be initialized */
-       ftrace_new_pgs = pg;
+       /* We should have used all pages */
+       WARN_ON(pg->next);
+
+       /* Assign the last page to ftrace_pages */
+       ftrace_pages = pg;
 
-       /* Make each individual set of pages sorted by ips */
-       for (; pg; pg = pg->next)
-               sort(pg->records, pg->index, sizeof(struct dyn_ftrace),
-                    ftrace_cmp_recs, ftrace_swap_recs);
+       /* These new locations need to be initialized */
+       ftrace_new_pgs = start_pg;
 
        /*
         * We only need to disable interrupts on start up
index cf8d11e..6420cda 100644 (file)
@@ -23,6 +23,8 @@
 #include <asm/local.h>
 #include "trace.h"
 
+static void update_pages_handler(struct work_struct *work);
+
 /*
  * The ring buffer header is special. We must manually up keep it.
  */
@@ -449,6 +451,7 @@ struct ring_buffer_per_cpu {
        raw_spinlock_t                  reader_lock;    /* serialize readers */
        arch_spinlock_t                 lock;
        struct lock_class_key           lock_key;
+       unsigned int                    nr_pages;
        struct list_head                *pages;
        struct buffer_page              *head_page;     /* read from head */
        struct buffer_page              *tail_page;     /* write to tail */
@@ -466,13 +469,18 @@ struct ring_buffer_per_cpu {
        unsigned long                   read_bytes;
        u64                             write_stamp;
        u64                             read_stamp;
+       /* ring buffer pages to update, > 0 to add, < 0 to remove */
+       int                             nr_pages_to_update;
+       struct list_head                new_pages; /* new pages to add */
+       struct work_struct              update_pages_work;
+       struct completion               update_done;
 };
 
 struct ring_buffer {
-       unsigned                        pages;
        unsigned                        flags;
        int                             cpus;
        atomic_t                        record_disabled;
+       atomic_t                        resize_disabled;
        cpumask_var_t                   cpumask;
 
        struct lock_class_key           *reader_lock_key;
@@ -937,6 +945,10 @@ static int rb_check_pages(struct ring_buffer_per_cpu *cpu_buffer)
        struct list_head *head = cpu_buffer->pages;
        struct buffer_page *bpage, *tmp;
 
+       /* Reset the head page if it exists */
+       if (cpu_buffer->head_page)
+               rb_set_head_page(cpu_buffer);
+
        rb_head_page_deactivate(cpu_buffer);
 
        if (RB_WARN_ON(cpu_buffer, head->next->prev != head))
@@ -963,14 +975,10 @@ static int rb_check_pages(struct ring_buffer_per_cpu *cpu_buffer)
        return 0;
 }
 
-static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
-                            unsigned nr_pages)
+static int __rb_allocate_pages(int nr_pages, struct list_head *pages, int cpu)
 {
+       int i;
        struct buffer_page *bpage, *tmp;
-       LIST_HEAD(pages);
-       unsigned i;
-
-       WARN_ON(!nr_pages);
 
        for (i = 0; i < nr_pages; i++) {
                struct page *page;
@@ -981,15 +989,13 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
                 */
                bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()),
                                    GFP_KERNEL | __GFP_NORETRY,
-                                   cpu_to_node(cpu_buffer->cpu));
+                                   cpu_to_node(cpu));
                if (!bpage)
                        goto free_pages;
 
-               rb_check_bpage(cpu_buffer, bpage);
+               list_add(&bpage->list, pages);
 
-               list_add(&bpage->list, &pages);
-
-               page = alloc_pages_node(cpu_to_node(cpu_buffer->cpu),
+               page = alloc_pages_node(cpu_to_node(cpu),
                                        GFP_KERNEL | __GFP_NORETRY, 0);
                if (!page)
                        goto free_pages;
@@ -997,6 +1003,27 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
                rb_init_page(bpage->page);
        }
 
+       return 0;
+
+free_pages:
+       list_for_each_entry_safe(bpage, tmp, pages, list) {
+               list_del_init(&bpage->list);
+               free_buffer_page(bpage);
+       }
+
+       return -ENOMEM;
+}
+
+static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
+                            unsigned nr_pages)
+{
+       LIST_HEAD(pages);
+
+       WARN_ON(!nr_pages);
+
+       if (__rb_allocate_pages(nr_pages, &pages, cpu_buffer->cpu))
+               return -ENOMEM;
+
        /*
         * The ring buffer page list is a circular list that does not
         * start and end with a list head. All page list items point to
@@ -1005,20 +1032,15 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
        cpu_buffer->pages = pages.next;
        list_del(&pages);
 
+       cpu_buffer->nr_pages = nr_pages;
+
        rb_check_pages(cpu_buffer);
 
        return 0;
-
- free_pages:
-       list_for_each_entry_safe(bpage, tmp, &pages, list) {
-               list_del_init(&bpage->list);
-               free_buffer_page(bpage);
-       }
-       return -ENOMEM;
 }
 
 static struct ring_buffer_per_cpu *
-rb_allocate_cpu_buffer(struct ring_buffer *buffer, int cpu)
+rb_allocate_cpu_buffer(struct ring_buffer *buffer, int nr_pages, int cpu)
 {
        struct ring_buffer_per_cpu *cpu_buffer;
        struct buffer_page *bpage;
@@ -1035,6 +1057,8 @@ rb_allocate_cpu_buffer(struct ring_buffer *buffer, int cpu)
        raw_spin_lock_init(&cpu_buffer->reader_lock);
        lockdep_set_class(&cpu_buffer->reader_lock, buffer->reader_lock_key);
        cpu_buffer->lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+       INIT_WORK(&cpu_buffer->update_pages_work, update_pages_handler);
+       init_completion(&cpu_buffer->update_done);
 
        bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()),
                            GFP_KERNEL, cpu_to_node(cpu));
@@ -1052,7 +1076,7 @@ rb_allocate_cpu_buffer(struct ring_buffer *buffer, int cpu)
 
        INIT_LIST_HEAD(&cpu_buffer->reader_page->list);
 
-       ret = rb_allocate_pages(cpu_buffer, buffer->pages);
+       ret = rb_allocate_pages(cpu_buffer, nr_pages);
        if (ret < 0)
                goto fail_free_reader;
 
@@ -1113,7 +1137,7 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags,
 {
        struct ring_buffer *buffer;
        int bsize;
-       int cpu;
+       int cpu, nr_pages;
 
        /* keep it in its own cache line */
        buffer = kzalloc(ALIGN(sizeof(*buffer), cache_line_size()),
@@ -1124,14 +1148,14 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags,
        if (!alloc_cpumask_var(&buffer->cpumask, GFP_KERNEL))
                goto fail_free_buffer;
 
-       buffer->pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
+       nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
        buffer->flags = flags;
        buffer->clock = trace_clock_local;
        buffer->reader_lock_key = key;
 
        /* need at least two pages */
-       if (buffer->pages < 2)
-               buffer->pages = 2;
+       if (nr_pages < 2)
+               nr_pages = 2;
 
        /*
         * In case of non-hotplug cpu, if the ring-buffer is allocated
@@ -1154,7 +1178,7 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags,
 
        for_each_buffer_cpu(buffer, cpu) {
                buffer->buffers[cpu] =
-                       rb_allocate_cpu_buffer(buffer, cpu);
+                       rb_allocate_cpu_buffer(buffer, nr_pages, cpu);
                if (!buffer->buffers[cpu])
                        goto fail_free_buffers;
        }
@@ -1222,58 +1246,222 @@ void ring_buffer_set_clock(struct ring_buffer *buffer,
 
 static void rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer);
 
-static void
-rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned nr_pages)
+static inline unsigned long rb_page_entries(struct buffer_page *bpage)
 {
-       struct buffer_page *bpage;
-       struct list_head *p;
-       unsigned i;
+       return local_read(&bpage->entries) & RB_WRITE_MASK;
+}
+
+static inline unsigned long rb_page_write(struct buffer_page *bpage)
+{
+       return local_read(&bpage->write) & RB_WRITE_MASK;
+}
+
+static int
+rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned int nr_pages)
+{
+       struct list_head *tail_page, *to_remove, *next_page;
+       struct buffer_page *to_remove_page, *tmp_iter_page;
+       struct buffer_page *last_page, *first_page;
+       unsigned int nr_removed;
+       unsigned long head_bit;
+       int page_entries;
+
+       head_bit = 0;
 
        raw_spin_lock_irq(&cpu_buffer->reader_lock);
-       rb_head_page_deactivate(cpu_buffer);
+       atomic_inc(&cpu_buffer->record_disabled);
+       /*
+        * We don't race with the readers since we have acquired the reader
+        * lock. We also don't race with writers after disabling recording.
+        * This makes it easy to figure out the first and the last page to be
+        * removed from the list. We unlink all the pages in between including
+        * the first and last pages. This is done in a busy loop so that we
+        * lose the least number of traces.
+        * The pages are freed after we restart recording and unlock readers.
+        */
+       tail_page = &cpu_buffer->tail_page->list;
 
-       for (i = 0; i < nr_pages; i++) {
-               if (RB_WARN_ON(cpu_buffer, list_empty(cpu_buffer->pages)))
-                       goto out;
-               p = cpu_buffer->pages->next;
-               bpage = list_entry(p, struct buffer_page, list);
-               list_del_init(&bpage->list);
-               free_buffer_page(bpage);
+       /*
+        * tail page might be on reader page, we remove the next page
+        * from the ring buffer
+        */
+       if (cpu_buffer->tail_page == cpu_buffer->reader_page)
+               tail_page = rb_list_head(tail_page->next);
+       to_remove = tail_page;
+
+       /* start of pages to remove */
+       first_page = list_entry(rb_list_head(to_remove->next),
+                               struct buffer_page, list);
+
+       for (nr_removed = 0; nr_removed < nr_pages; nr_removed++) {
+               to_remove = rb_list_head(to_remove)->next;
+               head_bit |= (unsigned long)to_remove & RB_PAGE_HEAD;
        }
-       if (RB_WARN_ON(cpu_buffer, list_empty(cpu_buffer->pages)))
-               goto out;
 
-       rb_reset_cpu(cpu_buffer);
-       rb_check_pages(cpu_buffer);
+       next_page = rb_list_head(to_remove)->next;
 
-out:
+       /*
+        * Now we remove all pages between tail_page and next_page.
+        * Make sure that we have head_bit value preserved for the
+        * next page
+        */
+       tail_page->next = (struct list_head *)((unsigned long)next_page |
+                                               head_bit);
+       next_page = rb_list_head(next_page);
+       next_page->prev = tail_page;
+
+       /* make sure pages points to a valid page in the ring buffer */
+       cpu_buffer->pages = next_page;
+
+       /* update head page */
+       if (head_bit)
+               cpu_buffer->head_page = list_entry(next_page,
+                                               struct buffer_page, list);
+
+       /*
+        * change read pointer to make sure any read iterators reset
+        * themselves
+        */
+       cpu_buffer->read = 0;
+
+       /* pages are removed, resume tracing and then free the pages */
+       atomic_dec(&cpu_buffer->record_disabled);
        raw_spin_unlock_irq(&cpu_buffer->reader_lock);
+
+       RB_WARN_ON(cpu_buffer, list_empty(cpu_buffer->pages));
+
+       /* last buffer page to remove */
+       last_page = list_entry(rb_list_head(to_remove), struct buffer_page,
+                               list);
+       tmp_iter_page = first_page;
+
+       do {
+               to_remove_page = tmp_iter_page;
+               rb_inc_page(cpu_buffer, &tmp_iter_page);
+
+               /* update the counters */
+               page_entries = rb_page_entries(to_remove_page);
+               if (page_entries) {
+                       /*
+                        * If something was added to this page, it was full
+                        * since it is not the tail page. So we deduct the
+                        * bytes consumed in ring buffer from here.
+                        * No need to update overruns, since this page is
+                        * deleted from ring buffer and its entries are
+                        * already accounted for.
+                        */
+                       local_sub(BUF_PAGE_SIZE, &cpu_buffer->entries_bytes);
+               }
+
+               /*
+                * We have already removed references to this list item, just
+                * free up the buffer_page and its page
+                */
+               free_buffer_page(to_remove_page);
+               nr_removed--;
+
+       } while (to_remove_page != last_page);
+
+       RB_WARN_ON(cpu_buffer, nr_removed);
+
+       return nr_removed == 0;
 }
 
-static void
-rb_insert_pages(struct ring_buffer_per_cpu *cpu_buffer,
-               struct list_head *pages, unsigned nr_pages)
+static int
+rb_insert_pages(struct ring_buffer_per_cpu *cpu_buffer)
 {
-       struct buffer_page *bpage;
-       struct list_head *p;
-       unsigned i;
+       struct list_head *pages = &cpu_buffer->new_pages;
+       int retries, success;
 
        raw_spin_lock_irq(&cpu_buffer->reader_lock);
-       rb_head_page_deactivate(cpu_buffer);
+       /*
+        * We are holding the reader lock, so the reader page won't be swapped
+        * in the ring buffer. Now we are racing with the writer trying to
+        * move head page and the tail page.
+        * We are going to adapt the reader page update process where:
+        * 1. We first splice the start and end of list of new pages between
+        *    the head page and its previous page.
+        * 2. We cmpxchg the prev_page->next to point from head page to the
+        *    start of new pages list.
+        * 3. Finally, we update the head->prev to the end of new list.
+        *
+        * We will try this process 10 times, to make sure that we don't keep
+        * spinning.
+        */
+       retries = 10;
+       success = 0;
+       while (retries--) {
+               struct list_head *head_page, *prev_page, *r;
+               struct list_head *last_page, *first_page;
+               struct list_head *head_page_with_bit;
 
-       for (i = 0; i < nr_pages; i++) {
-               if (RB_WARN_ON(cpu_buffer, list_empty(pages)))
-                       goto out;
-               p = pages->next;
-               bpage = list_entry(p, struct buffer_page, list);
-               list_del_init(&bpage->list);
-               list_add_tail(&bpage->list, cpu_buffer->pages);
+               head_page = &rb_set_head_page(cpu_buffer)->list;
+               prev_page = head_page->prev;
+
+               first_page = pages->next;
+               last_page  = pages->prev;
+
+               head_page_with_bit = (struct list_head *)
+                                    ((unsigned long)head_page | RB_PAGE_HEAD);
+
+               last_page->next = head_page_with_bit;
+               first_page->prev = prev_page;
+
+               r = cmpxchg(&prev_page->next, head_page_with_bit, first_page);
+
+               if (r == head_page_with_bit) {
+                       /*
+                        * yay, we replaced the page pointer to our new list,
+                        * now, we just have to update to head page's prev
+                        * pointer to point to end of list
+                        */
+                       head_page->prev = last_page;
+                       success = 1;
+                       break;
+               }
        }
-       rb_reset_cpu(cpu_buffer);
-       rb_check_pages(cpu_buffer);
 
-out:
+       if (success)
+               INIT_LIST_HEAD(pages);
+       /*
+        * If we weren't successful in adding in new pages, warn and stop
+        * tracing
+        */
+       RB_WARN_ON(cpu_buffer, !success);
        raw_spin_unlock_irq(&cpu_buffer->reader_lock);
+
+       /* free pages if they weren't inserted */
+       if (!success) {
+               struct buffer_page *bpage, *tmp;
+               list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages,
+                                        list) {
+                       list_del_init(&bpage->list);
+                       free_buffer_page(bpage);
+               }
+       }
+       return success;
+}
+
+static void rb_update_pages(struct ring_buffer_per_cpu *cpu_buffer)
+{
+       int success;
+
+       if (cpu_buffer->nr_pages_to_update > 0)
+               success = rb_insert_pages(cpu_buffer);
+       else
+               success = rb_remove_pages(cpu_buffer,
+                                       -cpu_buffer->nr_pages_to_update);
+
+       if (success)
+               cpu_buffer->nr_pages += cpu_buffer->nr_pages_to_update;
+}
+
+static void update_pages_handler(struct work_struct *work)
+{
+       struct ring_buffer_per_cpu *cpu_buffer = container_of(work,
+                       struct ring_buffer_per_cpu, update_pages_work);
+       rb_update_pages(cpu_buffer);
+       complete(&cpu_buffer->update_done);
 }
 
 /**
@@ -1283,16 +1471,14 @@ out:
  *
  * Minimum size is 2 * BUF_PAGE_SIZE.
  *
- * Returns -1 on failure.
+ * Returns 0 on success and < 0 on failure.
  */
-int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size)
+int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size,
+                       int cpu_id)
 {
        struct ring_buffer_per_cpu *cpu_buffer;
-       unsigned nr_pages, rm_pages, new_pages;
-       struct buffer_page *bpage, *tmp;
-       unsigned long buffer_size;
-       LIST_HEAD(pages);
-       int i, cpu;
+       unsigned nr_pages;
+       int cpu, err = 0;
 
        /*
         * Always succeed at resizing a non-existent buffer:
@@ -1302,113 +1488,154 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size)
 
        size = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
        size *= BUF_PAGE_SIZE;
-       buffer_size = buffer->pages * BUF_PAGE_SIZE;
 
        /* we need a minimum of two pages */
        if (size < BUF_PAGE_SIZE * 2)
                size = BUF_PAGE_SIZE * 2;
 
-       if (size == buffer_size)
-               return size;
-
-       atomic_inc(&buffer->record_disabled);
+       nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
 
-       /* Make sure all writers are done with this buffer. */
-       synchronize_sched();
+       /*
+        * Don't succeed if resizing is disabled, as a reader might be
+        * manipulating the ring buffer and is expecting a sane state while
+        * this is true.
+        */
+       if (atomic_read(&buffer->resize_disabled))
+               return -EBUSY;
 
+       /* prevent another thread from changing buffer sizes */
        mutex_lock(&buffer->mutex);
-       get_online_cpus();
-
-       nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
 
-       if (size < buffer_size) {
+       if (cpu_id == RING_BUFFER_ALL_CPUS) {
+               /* calculate the pages to update */
+               for_each_buffer_cpu(buffer, cpu) {
+                       cpu_buffer = buffer->buffers[cpu];
 
-               /* easy case, just free pages */
-               if (RB_WARN_ON(buffer, nr_pages >= buffer->pages))
-                       goto out_fail;
+                       cpu_buffer->nr_pages_to_update = nr_pages -
+                                                       cpu_buffer->nr_pages;
+                       /*
+                        * nothing more to do for removing pages or no update
+                        */
+                       if (cpu_buffer->nr_pages_to_update <= 0)
+                               continue;
+                       /*
+                        * to add pages, make sure all new pages can be
+                        * allocated without receiving ENOMEM
+                        */
+                       INIT_LIST_HEAD(&cpu_buffer->new_pages);
+                       if (__rb_allocate_pages(cpu_buffer->nr_pages_to_update,
+                                               &cpu_buffer->new_pages, cpu)) {
+                               /* not enough memory for new pages */
+                               err = -ENOMEM;
+                               goto out_err;
+                       }
+               }
 
-               rm_pages = buffer->pages - nr_pages;
+               get_online_cpus();
+               /*
+                * Fire off all the required work handlers
+                * We can't schedule on offline CPUs, but it's not necessary
+                * since we can change their buffer sizes without any race.
+                */
+               for_each_buffer_cpu(buffer, cpu) {
+                       cpu_buffer = buffer->buffers[cpu];
+                       if (!cpu_buffer->nr_pages_to_update)
+                               continue;
+
+                       if (cpu_online(cpu))
+                               schedule_work_on(cpu,
+                                               &cpu_buffer->update_pages_work);
+                       else
+                               rb_update_pages(cpu_buffer);
+               }
 
+               /* wait for all the updates to complete */
                for_each_buffer_cpu(buffer, cpu) {
                        cpu_buffer = buffer->buffers[cpu];
-                       rb_remove_pages(cpu_buffer, rm_pages);
+                       if (!cpu_buffer->nr_pages_to_update)
+                               continue;
+
+                       if (cpu_online(cpu))
+                               wait_for_completion(&cpu_buffer->update_done);
+                       cpu_buffer->nr_pages_to_update = 0;
                }
-               goto out;
-       }
 
-       /*
-        * This is a bit more difficult. We only want to add pages
-        * when we can allocate enough for all CPUs. We do this
-        * by allocating all the pages and storing them on a local
-        * link list. If we succeed in our allocation, then we
-        * 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))
-               goto out_fail;
+               put_online_cpus();
+       } else {
+               cpu_buffer = buffer->buffers[cpu_id];
 
-       new_pages = nr_pages - buffer->pages;
+               if (nr_pages == cpu_buffer->nr_pages)
+                       goto out;
 
-       for_each_buffer_cpu(buffer, cpu) {
-               for (i = 0; i < new_pages; i++) {
-                       struct page *page;
-                       /*
-                        * __GFP_NORETRY flag makes sure that the allocation
-                        * fails gracefully without invoking oom-killer and
-                        * the system is not destabilized.
-                        */
-                       bpage = kzalloc_node(ALIGN(sizeof(*bpage),
-                                                 cache_line_size()),
-                                           GFP_KERNEL | __GFP_NORETRY,
-                                           cpu_to_node(cpu));
-                       if (!bpage)
-                               goto free_pages;
-                       list_add(&bpage->list, &pages);
-                       page = alloc_pages_node(cpu_to_node(cpu),
-                                               GFP_KERNEL | __GFP_NORETRY, 0);
-                       if (!page)
-                               goto free_pages;
-                       bpage->page = page_address(page);
-                       rb_init_page(bpage->page);
+               cpu_buffer->nr_pages_to_update = nr_pages -
+                                               cpu_buffer->nr_pages;
+
+               INIT_LIST_HEAD(&cpu_buffer->new_pages);
+               if (cpu_buffer->nr_pages_to_update > 0 &&
+                       __rb_allocate_pages(cpu_buffer->nr_pages_to_update,
+                                           &cpu_buffer->new_pages, cpu_id)) {
+                       err = -ENOMEM;
+                       goto out_err;
                }
-       }
 
-       for_each_buffer_cpu(buffer, cpu) {
-               cpu_buffer = buffer->buffers[cpu];
-               rb_insert_pages(cpu_buffer, &pages, new_pages);
-       }
+               get_online_cpus();
 
-       if (RB_WARN_ON(buffer, !list_empty(&pages)))
-               goto out_fail;
+               if (cpu_online(cpu_id)) {
+                       schedule_work_on(cpu_id,
+                                        &cpu_buffer->update_pages_work);
+                       wait_for_completion(&cpu_buffer->update_done);
+               } else
+                       rb_update_pages(cpu_buffer);
+
+               cpu_buffer->nr_pages_to_update = 0;
+               put_online_cpus();
+       }
 
  out:
-       buffer->pages = nr_pages;
-       put_online_cpus();
+       /*
+        * The ring buffer resize can happen with the ring buffer
+        * enabled, so that the update disturbs the tracing as little
+        * as possible. But if the buffer is disabled, we do not need
+        * to worry about that, and we can take the time to verify
+        * that the buffer is not corrupt.
+        */
+       if (atomic_read(&buffer->record_disabled)) {
+               atomic_inc(&buffer->record_disabled);
+               /*
+                * Even though the buffer was disabled, we must make sure
+                * that it is truly disabled before calling rb_check_pages.
+                * There could have been a race between checking
+                * record_disable and incrementing it.
+                */
+               synchronize_sched();
+               for_each_buffer_cpu(buffer, cpu) {
+                       cpu_buffer = buffer->buffers[cpu];
+                       rb_check_pages(cpu_buffer);
+               }
+               atomic_dec(&buffer->record_disabled);
+       }
+
        mutex_unlock(&buffer->mutex);
+       return size;
 
-       atomic_dec(&buffer->record_disabled);
+ out_err:
+       for_each_buffer_cpu(buffer, cpu) {
+               struct buffer_page *bpage, *tmp;
 
-       return size;
+               cpu_buffer = buffer->buffers[cpu];
+               cpu_buffer->nr_pages_to_update = 0;
 
- free_pages:
-       list_for_each_entry_safe(bpage, tmp, &pages, list) {
-               list_del_init(&bpage->list);
-               free_buffer_page(bpage);
-       }
-       put_online_cpus();
-       mutex_unlock(&buffer->mutex);
-       atomic_dec(&buffer->record_disabled);
-       return -ENOMEM;
+               if (list_empty(&cpu_buffer->new_pages))
+                       continue;
 
-       /*
-        * Something went totally wrong, and we are too paranoid
-        * to even clean up the mess.
-        */
- out_fail:
-       put_online_cpus();
+               list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages,
+                                       list) {
+                       list_del_init(&bpage->list);
+                       free_buffer_page(bpage);
+               }
+       }
        mutex_unlock(&buffer->mutex);
-       atomic_dec(&buffer->record_disabled);
-       return -1;
+       return err;
 }
 EXPORT_SYMBOL_GPL(ring_buffer_resize);
 
@@ -1447,21 +1674,11 @@ rb_iter_head_event(struct ring_buffer_iter *iter)
        return __rb_page_index(iter->head_page, iter->head);
 }
 
-static inline unsigned long rb_page_write(struct buffer_page *bpage)
-{
-       return local_read(&bpage->write) & RB_WRITE_MASK;
-}
-
 static inline unsigned rb_page_commit(struct buffer_page *bpage)
 {
        return local_read(&bpage->page->commit);
 }
 
-static inline unsigned long rb_page_entries(struct buffer_page *bpage)
-{
-       return local_read(&bpage->entries) & RB_WRITE_MASK;
-}
-
 /* Size is determined by what has been committed */
 static inline unsigned rb_page_size(struct buffer_page *bpage)
 {
@@ -1510,7 +1727,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
         * assign the commit to the tail.
         */
  again:
-       max_count = cpu_buffer->buffer->pages * 100;
+       max_count = cpu_buffer->nr_pages * 100;
 
        while (cpu_buffer->commit_page != cpu_buffer->tail_page) {
                if (RB_WARN_ON(cpu_buffer, !(--max_count)))
@@ -3486,6 +3703,7 @@ ring_buffer_read_prepare(struct ring_buffer *buffer, int cpu)
 
        iter->cpu_buffer = cpu_buffer;
 
+       atomic_inc(&buffer->resize_disabled);
        atomic_inc(&cpu_buffer->record_disabled);
 
        return iter;
@@ -3548,7 +3766,14 @@ ring_buffer_read_finish(struct ring_buffer_iter *iter)
 {
        struct ring_buffer_per_cpu *cpu_buffer = iter->cpu_buffer;
 
+       /*
+        * Ring buffer is disabled from recording, here's a good place
+        * to check the integrity of the ring buffer. 
+        */
+       rb_check_pages(cpu_buffer);
+
        atomic_dec(&cpu_buffer->record_disabled);
+       atomic_dec(&cpu_buffer->buffer->resize_disabled);
        kfree(iter);
 }
 EXPORT_SYMBOL_GPL(ring_buffer_read_finish);
@@ -3588,9 +3813,18 @@ EXPORT_SYMBOL_GPL(ring_buffer_read);
  * ring_buffer_size - return the size of the ring buffer (in bytes)
  * @buffer: The ring buffer.
  */
-unsigned long ring_buffer_size(struct ring_buffer *buffer)
+unsigned long ring_buffer_size(struct ring_buffer *buffer, int cpu)
 {
-       return BUF_PAGE_SIZE * buffer->pages;
+       /*
+        * Earlier, this method returned
+        *      BUF_PAGE_SIZE * buffer->nr_pages
+        * Since the nr_pages field is now removed, we have converted this to
+        * return the per cpu buffer value.
+        */
+       if (!cpumask_test_cpu(cpu, buffer->cpumask))
+               return 0;
+
+       return BUF_PAGE_SIZE * buffer->buffers[cpu]->nr_pages;
 }
 EXPORT_SYMBOL_GPL(ring_buffer_size);
 
@@ -3611,6 +3845,7 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer)
        cpu_buffer->commit_page = cpu_buffer->head_page;
 
        INIT_LIST_HEAD(&cpu_buffer->reader_page->list);
+       INIT_LIST_HEAD(&cpu_buffer->new_pages);
        local_set(&cpu_buffer->reader_page->write, 0);
        local_set(&cpu_buffer->reader_page->entries, 0);
        local_set(&cpu_buffer->reader_page->page->commit, 0);
@@ -3647,8 +3882,12 @@ void ring_buffer_reset_cpu(struct ring_buffer *buffer, int cpu)
        if (!cpumask_test_cpu(cpu, buffer->cpumask))
                return;
 
+       atomic_inc(&buffer->resize_disabled);
        atomic_inc(&cpu_buffer->record_disabled);
 
+       /* Make sure all commits have finished */
+       synchronize_sched();
+
        raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
 
        if (RB_WARN_ON(cpu_buffer, local_read(&cpu_buffer->committing)))
@@ -3664,6 +3903,7 @@ void ring_buffer_reset_cpu(struct ring_buffer *buffer, int cpu)
        raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
 
        atomic_dec(&cpu_buffer->record_disabled);
+       atomic_dec(&buffer->resize_disabled);
 }
 EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu);
 
@@ -3765,8 +4005,11 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a,
            !cpumask_test_cpu(cpu, buffer_b->cpumask))
                goto out;
 
+       cpu_buffer_a = buffer_a->buffers[cpu];
+       cpu_buffer_b = buffer_b->buffers[cpu];
+
        /* At least make sure the two buffers are somewhat the same */
-       if (buffer_a->pages != buffer_b->pages)
+       if (cpu_buffer_a->nr_pages != cpu_buffer_b->nr_pages)
                goto out;
 
        ret = -EAGAIN;
@@ -3780,9 +4023,6 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a,
        if (atomic_read(&buffer_b->record_disabled))
                goto out;
 
-       cpu_buffer_a = buffer_a->buffers[cpu];
-       cpu_buffer_b = buffer_b->buffers[cpu];
-
        if (atomic_read(&cpu_buffer_a->record_disabled))
                goto out;
 
@@ -4071,6 +4311,8 @@ static int rb_cpu_notify(struct notifier_block *self,
        struct ring_buffer *buffer =
                container_of(self, struct ring_buffer, cpu_notify);
        long cpu = (long)hcpu;
+       int cpu_i, nr_pages_same;
+       unsigned int nr_pages;
 
        switch (action) {
        case CPU_UP_PREPARE:
@@ -4078,8 +4320,23 @@ static int rb_cpu_notify(struct notifier_block *self,
                if (cpumask_test_cpu(cpu, buffer->cpumask))
                        return NOTIFY_OK;
 
+               nr_pages = 0;
+               nr_pages_same = 1;
+               /* check if all cpu sizes are same */
+               for_each_buffer_cpu(buffer, cpu_i) {
+                       /* fill in the size from first enabled cpu */
+                       if (nr_pages == 0)
+                               nr_pages = buffer->buffers[cpu_i]->nr_pages;
+                       if (nr_pages != buffer->buffers[cpu_i]->nr_pages) {
+                               nr_pages_same = 0;
+                               break;
+                       }
+               }
+               /* allocate minimum pages, user can later expand it */
+               if (!nr_pages_same)
+                       nr_pages = 2;
                buffer->buffers[cpu] =
-                       rb_allocate_cpu_buffer(buffer, cpu);
+                       rb_allocate_cpu_buffer(buffer, nr_pages, cpu);
                if (!buffer->buffers[cpu]) {
                        WARN(1, "failed to allocate ring buffer on CPU %ld\n",
                             cpu);
index 2a22255..68032c6 100644 (file)
@@ -87,18 +87,6 @@ static int tracing_disabled = 1;
 
 DEFINE_PER_CPU(int, ftrace_cpu_disabled);
 
-static inline void ftrace_disable_cpu(void)
-{
-       preempt_disable();
-       __this_cpu_inc(ftrace_cpu_disabled);
-}
-
-static inline void ftrace_enable_cpu(void)
-{
-       __this_cpu_dec(ftrace_cpu_disabled);
-       preempt_enable();
-}
-
 cpumask_var_t __read_mostly    tracing_buffer_mask;
 
 /*
@@ -629,7 +617,6 @@ ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt)
 static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt)
 {
        int len;
-       void *ret;
 
        if (s->len <= s->readpos)
                return -EBUSY;
@@ -637,9 +624,7 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt)
        len = s->len - s->readpos;
        if (cnt > len)
                cnt = len;
-       ret = memcpy(buf, s->buffer + s->readpos, cnt);
-       if (!ret)
-               return -EFAULT;
+       memcpy(buf, s->buffer + s->readpos, cnt);
 
        s->readpos += cnt;
        return cnt;
@@ -751,8 +736,6 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
 
        arch_spin_lock(&ftrace_max_lock);
 
-       ftrace_disable_cpu();
-
        ret = ring_buffer_swap_cpu(max_tr.buffer, tr->buffer, cpu);
 
        if (ret == -EBUSY) {
@@ -766,8 +749,6 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
                        "Failed to swap buffers due to commit in progress\n");
        }
 
-       ftrace_enable_cpu();
-
        WARN_ON_ONCE(ret && ret != -EAGAIN && ret != -EBUSY);
 
        __update_max_tr(tr, tsk, cpu);
@@ -782,8 +763,6 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
  * Register a new plugin tracer.
  */
 int register_tracer(struct tracer *type)
-__releases(kernel_lock)
-__acquires(kernel_lock)
 {
        struct tracer *t;
        int ret = 0;
@@ -841,7 +820,8 @@ __acquires(kernel_lock)
 
                /* If we expanded the buffers, make sure the max is expanded too */
                if (ring_buffer_expanded && type->use_max_tr)
-                       ring_buffer_resize(max_tr.buffer, trace_buf_size);
+                       ring_buffer_resize(max_tr.buffer, trace_buf_size,
+                                               RING_BUFFER_ALL_CPUS);
 
                /* the test is responsible for initializing and enabling */
                pr_info("Testing tracer %s: ", type->name);
@@ -857,7 +837,8 @@ __acquires(kernel_lock)
 
                /* Shrink the max buffer again */
                if (ring_buffer_expanded && type->use_max_tr)
-                       ring_buffer_resize(max_tr.buffer, 1);
+                       ring_buffer_resize(max_tr.buffer, 1,
+                                               RING_BUFFER_ALL_CPUS);
 
                printk(KERN_CONT "PASSED\n");
        }
@@ -917,13 +898,6 @@ out:
        mutex_unlock(&trace_types_lock);
 }
 
-static void __tracing_reset(struct ring_buffer *buffer, int cpu)
-{
-       ftrace_disable_cpu();
-       ring_buffer_reset_cpu(buffer, cpu);
-       ftrace_enable_cpu();
-}
-
 void tracing_reset(struct trace_array *tr, int cpu)
 {
        struct ring_buffer *buffer = tr->buffer;
@@ -932,7 +906,7 @@ void tracing_reset(struct trace_array *tr, int cpu)
 
        /* Make sure all commits have finished */
        synchronize_sched();
-       __tracing_reset(buffer, cpu);
+       ring_buffer_reset_cpu(buffer, cpu);
 
        ring_buffer_record_enable(buffer);
 }
@@ -950,7 +924,7 @@ void tracing_reset_online_cpus(struct trace_array *tr)
        tr->time_start = ftrace_now(tr->cpu);
 
        for_each_online_cpu(cpu)
-               __tracing_reset(buffer, cpu);
+               ring_buffer_reset_cpu(buffer, cpu);
 
        ring_buffer_record_enable(buffer);
 }
@@ -1498,25 +1472,119 @@ static void __trace_userstack(struct trace_array *tr, unsigned long flags)
 
 #endif /* CONFIG_STACKTRACE */
 
+/* created for use with alloc_percpu */
+struct trace_buffer_struct {
+       char buffer[TRACE_BUF_SIZE];
+};
+
+static struct trace_buffer_struct *trace_percpu_buffer;
+static struct trace_buffer_struct *trace_percpu_sirq_buffer;
+static struct trace_buffer_struct *trace_percpu_irq_buffer;
+static struct trace_buffer_struct *trace_percpu_nmi_buffer;
+
+/*
+ * The buffer used is dependent on the context. There is a per cpu
+ * buffer for normal context, softirq contex, hard irq context and
+ * for NMI context. Thise allows for lockless recording.
+ *
+ * Note, if the buffers failed to be allocated, then this returns NULL
+ */
+static char *get_trace_buf(void)
+{
+       struct trace_buffer_struct *percpu_buffer;
+       struct trace_buffer_struct *buffer;
+
+       /*
+        * If we have allocated per cpu buffers, then we do not
+        * need to do any locking.
+        */
+       if (in_nmi())
+               percpu_buffer = trace_percpu_nmi_buffer;
+       else if (in_irq())
+               percpu_buffer = trace_percpu_irq_buffer;
+       else if (in_softirq())
+               percpu_buffer = trace_percpu_sirq_buffer;
+       else
+               percpu_buffer = trace_percpu_buffer;
+
+       if (!percpu_buffer)
+               return NULL;
+
+       buffer = per_cpu_ptr(percpu_buffer, smp_processor_id());
+
+       return buffer->buffer;
+}
+
+static int alloc_percpu_trace_buffer(void)
+{
+       struct trace_buffer_struct *buffers;
+       struct trace_buffer_struct *sirq_buffers;
+       struct trace_buffer_struct *irq_buffers;
+       struct trace_buffer_struct *nmi_buffers;
+
+       buffers = alloc_percpu(struct trace_buffer_struct);
+       if (!buffers)
+               goto err_warn;
+
+       sirq_buffers = alloc_percpu(struct trace_buffer_struct);
+       if (!sirq_buffers)
+               goto err_sirq;
+
+       irq_buffers = alloc_percpu(struct trace_buffer_struct);
+       if (!irq_buffers)
+               goto err_irq;
+
+       nmi_buffers = alloc_percpu(struct trace_buffer_struct);
+       if (!nmi_buffers)
+               goto err_nmi;
+
+       trace_percpu_buffer = buffers;
+       trace_percpu_sirq_buffer = sirq_buffers;
+       trace_percpu_irq_buffer = irq_buffers;
+       trace_percpu_nmi_buffer = nmi_buffers;
+
+       return 0;
+
+ err_nmi:
+       free_percpu(irq_buffers);
+ err_irq:
+       free_percpu(sirq_buffers);
+ err_sirq:
+       free_percpu(buffers);
+ err_warn:
+       WARN(1, "Could not allocate percpu trace_printk buffer");
+       return -ENOMEM;
+}
+
+void trace_printk_init_buffers(void)
+{
+       static int buffers_allocated;
+
+       if (buffers_allocated)
+               return;
+
+       if (alloc_percpu_trace_buffer())
+               return;
+
+       pr_info("ftrace: Allocated trace_printk buffers\n");
+
+       buffers_allocated = 1;
+}
+
 /**
  * trace_vbprintk - write binary msg to tracing buffer
  *
  */
 int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
 {
-       static arch_spinlock_t trace_buf_lock =
-               (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
-       static u32 trace_buf[TRACE_BUF_SIZE];
-
        struct ftrace_event_call *call = &event_bprint;
        struct ring_buffer_event *event;
        struct ring_buffer *buffer;
        struct trace_array *tr = &global_trace;
-       struct trace_array_cpu *data;
        struct bprint_entry *entry;
        unsigned long flags;
-       int disable;
-       int cpu, len = 0, size, pc;
+       char *tbuffer;
+       int len = 0, size, pc;
 
        if (unlikely(tracing_selftest_running || tracing_disabled))
                return 0;
@@ -1526,43 +1594,36 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
 
        pc = preempt_count();
        preempt_disable_notrace();
-       cpu = raw_smp_processor_id();
-       data = tr->data[cpu];
 
-       disable = atomic_inc_return(&data->disabled);
-       if (unlikely(disable != 1))
+       tbuffer = get_trace_buf();
+       if (!tbuffer) {
+               len = 0;
                goto out;
+       }
 
-       /* Lockdep uses trace_printk for lock tracing */
-       local_irq_save(flags);
-       arch_spin_lock(&trace_buf_lock);
-       len = vbin_printf(trace_buf, TRACE_BUF_SIZE, fmt, args);
+       len = vbin_printf((u32 *)tbuffer, TRACE_BUF_SIZE/sizeof(int), fmt, args);
 
-       if (len > TRACE_BUF_SIZE || len < 0)
-               goto out_unlock;
+       if (len > TRACE_BUF_SIZE/sizeof(int) || len < 0)
+               goto out;
 
+       local_save_flags(flags);
        size = sizeof(*entry) + sizeof(u32) * len;
        buffer = tr->buffer;
        event = trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size,
                                          flags, pc);
        if (!event)
-               goto out_unlock;
+               goto out;
        entry = ring_buffer_event_data(event);
        entry->ip                       = ip;
        entry->fmt                      = fmt;
 
-       memcpy(entry->buf, trace_buf, sizeof(u32) * len);
+       memcpy(entry->buf, tbuffer, sizeof(u32) * len);
        if (!filter_check_discard(call, entry, buffer, event)) {
                ring_buffer_unlock_commit(buffer, event);
                ftrace_trace_stack(buffer, flags, 6, pc);
        }
 
-out_unlock:
-       arch_spin_unlock(&trace_buf_lock);
-       local_irq_restore(flags);
-
 out:
-       atomic_dec_return(&data->disabled);
        preempt_enable_notrace();
        unpause_graph_tracing();
 
@@ -1588,58 +1649,53 @@ int trace_array_printk(struct trace_array *tr,
 int trace_array_vprintk(struct trace_array *tr,
                        unsigned long ip, const char *fmt, va_list args)
 {
-       static arch_spinlock_t trace_buf_lock = __ARCH_SPIN_LOCK_UNLOCKED;
-       static char trace_buf[TRACE_BUF_SIZE];
-
        struct ftrace_event_call *call = &event_print;
        struct ring_buffer_event *event;
        struct ring_buffer *buffer;
-       struct trace_array_cpu *data;
-       int cpu, len = 0, size, pc;
+       int len = 0, size, pc;
        struct print_entry *entry;
-       unsigned long irq_flags;
-       int disable;
+       unsigned long flags;
+       char *tbuffer;
 
        if (tracing_disabled || tracing_selftest_running)
                return 0;
 
+       /* Don't pollute graph traces with trace_vprintk internals */
+       pause_graph_tracing();
+
        pc = preempt_count();
        preempt_disable_notrace();
-       cpu = raw_smp_processor_id();
-       data = tr->data[cpu];
 
-       disable = atomic_inc_return(&data->disabled);
-       if (unlikely(disable != 1))
+
+       tbuffer = get_trace_buf();
+       if (!tbuffer) {
+               len = 0;
                goto out;
+       }
 
-       pause_graph_tracing();
-       raw_local_irq_save(irq_flags);
-       arch_spin_lock(&trace_buf_lock);
-       len = vsnprintf(trace_buf, TRACE_BUF_SIZE, fmt, args);
+       len = vsnprintf(tbuffer, TRACE_BUF_SIZE, fmt, args);
+       if (len > TRACE_BUF_SIZE)
+               goto out;
 
+       local_save_flags(flags);
        size = sizeof(*entry) + len + 1;
        buffer = tr->buffer;
        event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
-                                         irq_flags, pc);
+                                         flags, pc);
        if (!event)
-               goto out_unlock;
+               goto out;
        entry = ring_buffer_event_data(event);
        entry->ip = ip;
 
-       memcpy(&entry->buf, trace_buf, len);
+       memcpy(&entry->buf, tbuffer, len);
        entry->buf[len] = '\0';
        if (!filter_check_discard(call, entry, buffer, event)) {
                ring_buffer_unlock_commit(buffer, event);
-               ftrace_trace_stack(buffer, irq_flags, 6, pc);
+               ftrace_trace_stack(buffer, flags, 6, pc);
        }
-
- out_unlock:
-       arch_spin_unlock(&trace_buf_lock);
-       raw_local_irq_restore(irq_flags);
-       unpause_graph_tracing();
  out:
-       atomic_dec_return(&data->disabled);
        preempt_enable_notrace();
+       unpause_graph_tracing();
 
        return len;
 }
@@ -1652,14 +1708,9 @@ EXPORT_SYMBOL_GPL(trace_vprintk);
 
 static void trace_iterator_increment(struct trace_iterator *iter)
 {
-       /* Don't allow ftrace to trace into the ring buffers */
-       ftrace_disable_cpu();
-
        iter->idx++;
        if (iter->buffer_iter[iter->cpu])
                ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
-
-       ftrace_enable_cpu();
 }
 
 static struct trace_entry *
@@ -1669,17 +1720,12 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
        struct ring_buffer_event *event;
        struct ring_buffer_iter *buf_iter = iter->buffer_iter[cpu];
 
-       /* Don't allow ftrace to trace into the ring buffers */
-       ftrace_disable_cpu();
-
        if (buf_iter)
                event = ring_buffer_iter_peek(buf_iter, ts);
        else
                event = ring_buffer_peek(iter->tr->buffer, cpu, ts,
                                         lost_events);
 
-       ftrace_enable_cpu();
-
        if (event) {
                iter->ent_size = ring_buffer_event_length(event);
                return ring_buffer_event_data(event);
@@ -1769,11 +1815,8 @@ void *trace_find_next_entry_inc(struct trace_iterator *iter)
 
 static void trace_consume(struct trace_iterator *iter)
 {
-       /* Don't allow ftrace to trace into the ring buffers */
-       ftrace_disable_cpu();
        ring_buffer_consume(iter->tr->buffer, iter->cpu, &iter->ts,
                            &iter->lost_events);
-       ftrace_enable_cpu();
 }
 
 static void *s_next(struct seq_file *m, void *v, loff_t *pos)
@@ -1862,16 +1905,12 @@ static void *s_start(struct seq_file *m, loff_t *pos)
                iter->cpu = 0;
                iter->idx = -1;
 
-               ftrace_disable_cpu();
-
                if (cpu_file == TRACE_PIPE_ALL_CPU) {
                        for_each_tracing_cpu(cpu)
                                tracing_iter_reset(iter, cpu);
                } else
                        tracing_iter_reset(iter, cpu_file);
 
-               ftrace_enable_cpu();
-
                iter->leftover = 0;
                for (p = iter; p && l < *pos; p = s_next(m, p, &l))
                        ;
@@ -2332,15 +2371,13 @@ static struct trace_iterator *
 __tracing_open(struct inode *inode, struct file *file)
 {
        long cpu_file = (long) inode->i_private;
-       void *fail_ret = ERR_PTR(-ENOMEM);
        struct trace_iterator *iter;
-       struct seq_file *m;
-       int cpu, ret;
+       int cpu;
 
        if (tracing_disabled)
                return ERR_PTR(-ENODEV);
 
-       iter = kzalloc(sizeof(*iter), GFP_KERNEL);
+       iter = __seq_open_private(file, &tracer_seq_ops, sizeof(*iter));
        if (!iter)
                return ERR_PTR(-ENOMEM);
 
@@ -2397,32 +2434,15 @@ __tracing_open(struct inode *inode, struct file *file)
                tracing_iter_reset(iter, cpu);
        }
 
-       ret = seq_open(file, &tracer_seq_ops);
-       if (ret < 0) {
-               fail_ret = ERR_PTR(ret);
-               goto fail_buffer;
-       }
-
-       m = file->private_data;
-       m->private = iter;
-
        mutex_unlock(&trace_types_lock);
 
        return iter;
 
- fail_buffer:
-       for_each_tracing_cpu(cpu) {
-               if (iter->buffer_iter[cpu])
-                       ring_buffer_read_finish(iter->buffer_iter[cpu]);
-       }
-       free_cpumask_var(iter->started);
-       tracing_start();
  fail:
        mutex_unlock(&trace_types_lock);
        kfree(iter->trace);
-       kfree(iter);
-
-       return fail_ret;
+       seq_release_private(inode, file);
+       return ERR_PTR(-ENOMEM);
 }
 
 int tracing_open_generic(struct inode *inode, struct file *filp)
@@ -2458,11 +2478,10 @@ static int tracing_release(struct inode *inode, struct file *file)
        tracing_start();
        mutex_unlock(&trace_types_lock);
 
-       seq_release(inode, file);
        mutex_destroy(&iter->mutex);
        free_cpumask_var(iter->started);
        kfree(iter->trace);
-       kfree(iter);
+       seq_release_private(inode, file);
        return 0;
 }
 
@@ -2648,10 +2667,12 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf,
                if (cpumask_test_cpu(cpu, tracing_cpumask) &&
                                !cpumask_test_cpu(cpu, tracing_cpumask_new)) {
                        atomic_inc(&global_trace.data[cpu]->disabled);
+                       ring_buffer_record_disable_cpu(global_trace.buffer, cpu);
                }
                if (!cpumask_test_cpu(cpu, tracing_cpumask) &&
                                cpumask_test_cpu(cpu, tracing_cpumask_new)) {
                        atomic_dec(&global_trace.data[cpu]->disabled);
+                       ring_buffer_record_enable_cpu(global_trace.buffer, cpu);
                }
        }
        arch_spin_unlock(&ftrace_max_lock);
@@ -2974,7 +2995,14 @@ int tracer_init(struct tracer *t, struct trace_array *tr)
        return t->init(tr);
 }
 
-static int __tracing_resize_ring_buffer(unsigned long size)
+static void set_buffer_entries(struct trace_array *tr, unsigned long val)
+{
+       int cpu;
+       for_each_tracing_cpu(cpu)
+               tr->data[cpu]->entries = val;
+}
+
+static int __tracing_resize_ring_buffer(unsigned long size, int cpu)
 {
        int ret;
 
@@ -2985,19 +3013,32 @@ static int __tracing_resize_ring_buffer(unsigned long size)
         */
        ring_buffer_expanded = 1;
 
-       ret = ring_buffer_resize(global_trace.buffer, size);
+       ret = ring_buffer_resize(global_trace.buffer, size, cpu);
        if (ret < 0)
                return ret;
 
        if (!current_trace->use_max_tr)
                goto out;
 
-       ret = ring_buffer_resize(max_tr.buffer, size);
+       ret = ring_buffer_resize(max_tr.buffer, size, cpu);
        if (ret < 0) {
-               int r;
+               int r = 0;
+
+               if (cpu == RING_BUFFER_ALL_CPUS) {
+                       int i;
+                       for_each_tracing_cpu(i) {
+                               r = ring_buffer_resize(global_trace.buffer,
+                                               global_trace.data[i]->entries,
+                                               i);
+                               if (r < 0)
+                                       break;
+                       }
+               } else {
+                       r = ring_buffer_resize(global_trace.buffer,
+                                               global_trace.data[cpu]->entries,
+                                               cpu);
+               }
 
-               r = ring_buffer_resize(global_trace.buffer,
-                                      global_trace.entries);
                if (r < 0) {
                        /*
                         * AARGH! We are left with different
@@ -3019,43 +3060,39 @@ static int __tracing_resize_ring_buffer(unsigned long size)
                return ret;
        }
 
-       max_tr.entries = size;
+       if (cpu == RING_BUFFER_ALL_CPUS)
+               set_buffer_entries(&max_tr, size);
+       else
+               max_tr.data[cpu]->entries = size;
+
  out:
-       global_trace.entries = size;
+       if (cpu == RING_BUFFER_ALL_CPUS)
+               set_buffer_entries(&global_trace, size);
+       else
+               global_trace.data[cpu]->entries = size;
 
        return ret;
 }
 
-static ssize_t tracing_resize_ring_buffer(unsigned long size)
+static ssize_t tracing_resize_ring_buffer(unsigned long size, int cpu_id)
 {
-       int cpu, ret = size;
+       int ret = size;
 
        mutex_lock(&trace_types_lock);
 
-       tracing_stop();
-
-       /* disable all cpu buffers */
-       for_each_tracing_cpu(cpu) {
-               if (global_trace.data[cpu])
-                       atomic_inc(&global_trace.data[cpu]->disabled);
-               if (max_tr.data[cpu])
-                       atomic_inc(&max_tr.data[cpu]->disabled);
+       if (cpu_id != RING_BUFFER_ALL_CPUS) {
+               /* make sure, this cpu is enabled in the mask */
+               if (!cpumask_test_cpu(cpu_id, tracing_buffer_mask)) {
+                       ret = -EINVAL;
+                       goto out;
+               }
        }
 
-       if (size != global_trace.entries)
-               ret = __tracing_resize_ring_buffer(size);
-
+       ret = __tracing_resize_ring_buffer(size, cpu_id);
        if (ret < 0)
                ret = -ENOMEM;
 
-       for_each_tracing_cpu(cpu) {
-               if (global_trace.data[cpu])
-                       atomic_dec(&global_trace.data[cpu]->disabled);
-               if (max_tr.data[cpu])
-                       atomic_dec(&max_tr.data[cpu]->disabled);
-       }
-
-       tracing_start();
+out:
        mutex_unlock(&trace_types_lock);
 
        return ret;
@@ -3078,7 +3115,8 @@ int tracing_update_buffers(void)
 
        mutex_lock(&trace_types_lock);
        if (!ring_buffer_expanded)
-               ret = __tracing_resize_ring_buffer(trace_buf_size);
+               ret = __tracing_resize_ring_buffer(trace_buf_size,
+                                               RING_BUFFER_ALL_CPUS);
        mutex_unlock(&trace_types_lock);
 
        return ret;
@@ -3102,7 +3140,8 @@ static int tracing_set_tracer(const char *buf)
        mutex_lock(&trace_types_lock);
 
        if (!ring_buffer_expanded) {
-               ret = __tracing_resize_ring_buffer(trace_buf_size);
+               ret = __tracing_resize_ring_buffer(trace_buf_size,
+                                               RING_BUFFER_ALL_CPUS);
                if (ret < 0)
                        goto out;
                ret = 0;
@@ -3128,8 +3167,8 @@ static int tracing_set_tracer(const char *buf)
                 * The max_tr ring buffer has some state (e.g. ring->clock) and
                 * we want preserve it.
                 */
-               ring_buffer_resize(max_tr.buffer, 1);
-               max_tr.entries = 1;
+               ring_buffer_resize(max_tr.buffer, 1, RING_BUFFER_ALL_CPUS);
+               set_buffer_entries(&max_tr, 1);
        }
        destroy_trace_option_files(topts);
 
@@ -3137,10 +3176,17 @@ static int tracing_set_tracer(const char *buf)
 
        topts = create_trace_option_files(current_trace);
        if (current_trace->use_max_tr) {
-               ret = ring_buffer_resize(max_tr.buffer, global_trace.entries);
-               if (ret < 0)
-                       goto out;
-               max_tr.entries = global_trace.entries;
+               int cpu;
+               /* we need to make per cpu buffer sizes equivalent */
+               for_each_tracing_cpu(cpu) {
+                       ret = ring_buffer_resize(max_tr.buffer,
+                                               global_trace.data[cpu]->entries,
+                                               cpu);
+                       if (ret < 0)
+                               goto out;
+                       max_tr.data[cpu]->entries =
+                                       global_trace.data[cpu]->entries;
+               }
        }
 
        if (t->init) {
@@ -3642,30 +3688,82 @@ out_err:
        goto out;
 }
 
+struct ftrace_entries_info {
+       struct trace_array      *tr;
+       int                     cpu;
+};
+
+static int tracing_entries_open(struct inode *inode, struct file *filp)
+{
+       struct ftrace_entries_info *info;
+
+       if (tracing_disabled)
+               return -ENODEV;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->tr = &global_trace;
+       info->cpu = (unsigned long)inode->i_private;
+
+       filp->private_data = info;
+
+       return 0;
+}
+
 static ssize_t
 tracing_entries_read(struct file *filp, char __user *ubuf,
                     size_t cnt, loff_t *ppos)
 {
-       struct trace_array *tr = filp->private_data;
-       char buf[96];
-       int r;
+       struct ftrace_entries_info *info = filp->private_data;
+       struct trace_array *tr = info->tr;
+       char buf[64];
+       int r = 0;
+       ssize_t ret;
 
        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);
+
+       if (info->cpu == RING_BUFFER_ALL_CPUS) {
+               int cpu, buf_size_same;
+               unsigned long size;
+
+               size = 0;
+               buf_size_same = 1;
+               /* check if all cpu sizes are same */
+               for_each_tracing_cpu(cpu) {
+                       /* fill in the size from first enabled cpu */
+                       if (size == 0)
+                               size = tr->data[cpu]->entries;
+                       if (size != tr->data[cpu]->entries) {
+                               buf_size_same = 0;
+                               break;
+                       }
+               }
+
+               if (buf_size_same) {
+                       if (!ring_buffer_expanded)
+                               r = sprintf(buf, "%lu (expanded: %lu)\n",
+                                           size >> 10,
+                                           trace_buf_size >> 10);
+                       else
+                               r = sprintf(buf, "%lu\n", size >> 10);
+               } else
+                       r = sprintf(buf, "X\n");
+       } else
+               r = sprintf(buf, "%lu\n", tr->data[info->cpu]->entries >> 10);
+
        mutex_unlock(&trace_types_lock);
 
-       return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+       ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+       return ret;
 }
 
 static ssize_t
 tracing_entries_write(struct file *filp, const char __user *ubuf,
                      size_t cnt, loff_t *ppos)
 {
+       struct ftrace_entries_info *info = filp->private_data;
        unsigned long val;
        int ret;
 
@@ -3680,7 +3778,7 @@ tracing_entries_write(struct file *filp, const char __user *ubuf,
        /* value is in KB */
        val <<= 10;
 
-       ret = tracing_resize_ring_buffer(val);
+       ret = tracing_resize_ring_buffer(val, info->cpu);
        if (ret < 0)
                return ret;
 
@@ -3689,6 +3787,16 @@ tracing_entries_write(struct file *filp, const char __user *ubuf,
        return cnt;
 }
 
+static int
+tracing_entries_release(struct inode *inode, struct file *filp)
+{
+       struct ftrace_entries_info *info = filp->private_data;
+
+       kfree(info);
+
+       return 0;
+}
+
 static ssize_t
 tracing_total_entries_read(struct file *filp, char __user *ubuf,
                                size_t cnt, loff_t *ppos)
@@ -3700,7 +3808,7 @@ tracing_total_entries_read(struct file *filp, char __user *ubuf,
 
        mutex_lock(&trace_types_lock);
        for_each_tracing_cpu(cpu) {
-               size += tr->entries >> 10;
+               size += tr->data[cpu]->entries >> 10;
                if (!ring_buffer_expanded)
                        expanded_size += trace_buf_size >> 10;
        }
@@ -3734,7 +3842,7 @@ tracing_free_buffer_release(struct inode *inode, struct file *filp)
        if (trace_flags & TRACE_ITER_STOP_ON_FREE)
                tracing_off();
        /* resize the ring buffer to 0 */
-       tracing_resize_ring_buffer(0);
+       tracing_resize_ring_buffer(0, RING_BUFFER_ALL_CPUS);
 
        return 0;
 }
@@ -3749,14 +3857,14 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
        struct print_entry *entry;
        unsigned long irq_flags;
        struct page *pages[2];
+       void *map_page[2];
        int nr_pages = 1;
        ssize_t written;
-       void *page1;
-       void *page2;
        int offset;
        int size;
        int len;
        int ret;
+       int i;
 
        if (tracing_disabled)
                return -EINVAL;
@@ -3795,9 +3903,8 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
                goto out;
        }
 
-       page1 = kmap_atomic(pages[0]);
-       if (nr_pages == 2)
-               page2 = kmap_atomic(pages[1]);
+       for (i = 0; i < nr_pages; i++)
+               map_page[i] = kmap_atomic(pages[i]);
 
        local_save_flags(irq_flags);
        size = sizeof(*entry) + cnt + 2; /* possible \n added */
@@ -3815,10 +3922,10 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
 
        if (nr_pages == 2) {
                len = PAGE_SIZE - offset;
-               memcpy(&entry->buf, page1 + offset, len);
-               memcpy(&entry->buf[len], page2, cnt - len);
+               memcpy(&entry->buf, map_page[0] + offset, len);
+               memcpy(&entry->buf[len], map_page[1], cnt - len);
        } else
-               memcpy(&entry->buf, page1 + offset, cnt);
+               memcpy(&entry->buf, map_page[0] + offset, cnt);
 
        if (entry->buf[cnt - 1] != '\n') {
                entry->buf[cnt] = '\n';
@@ -3833,11 +3940,10 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
        *fpos += written;
 
  out_unlock:
-       if (nr_pages == 2)
-               kunmap_atomic(page2);
-       kunmap_atomic(page1);
-       while (nr_pages > 0)
-               put_page(pages[--nr_pages]);
+       for (i = 0; i < nr_pages; i++){
+               kunmap_atomic(map_page[i]);
+               put_page(pages[i]);
+       }
  out:
        return written;
 }
@@ -3933,9 +4039,10 @@ static const struct file_operations tracing_pipe_fops = {
 };
 
 static const struct file_operations tracing_entries_fops = {
-       .open           = tracing_open_generic,
+       .open           = tracing_entries_open,
        .read           = tracing_entries_read,
        .write          = tracing_entries_write,
+       .release        = tracing_entries_release,
        .llseek         = generic_file_llseek,
 };
 
@@ -4367,6 +4474,9 @@ static void tracing_init_debugfs_percpu(long cpu)
        struct dentry *d_cpu;
        char cpu_dir[30]; /* 30 characters should be more than enough */
 
+       if (!d_percpu)
+               return;
+
        snprintf(cpu_dir, 30, "cpu%ld", cpu);
        d_cpu = debugfs_create_dir(cpu_dir, d_percpu);
        if (!d_cpu) {
@@ -4387,6 +4497,9 @@ static void tracing_init_debugfs_percpu(long cpu)
 
        trace_create_file("stats", 0444, d_cpu,
                        (void *) cpu, &tracing_stats_fops);
+
+       trace_create_file("buffer_size_kb", 0444, d_cpu,
+                       (void *) cpu, &tracing_entries_fops);
 }
 
 #ifdef CONFIG_FTRACE_SELFTEST
@@ -4718,7 +4831,7 @@ static __init int tracer_init_debugfs(void)
                        (void *) TRACE_PIPE_ALL_CPU, &tracing_pipe_fops);
 
        trace_create_file("buffer_size_kb", 0644, d_tracer,
-                       &global_trace, &tracing_entries_fops);
+                       (void *) RING_BUFFER_ALL_CPUS, &tracing_entries_fops);
 
        trace_create_file("buffer_total_size_kb", 0444, d_tracer,
                        &global_trace, &tracing_total_entries_fops);
@@ -4957,6 +5070,10 @@ __init static int tracer_alloc_buffers(void)
        if (!alloc_cpumask_var(&tracing_cpumask, GFP_KERNEL))
                goto out_free_buffer_mask;
 
+       /* Only allocate trace_printk buffers if a trace_printk exists */
+       if (__stop___trace_bprintk_fmt != __start___trace_bprintk_fmt)
+               trace_printk_init_buffers();
+
        /* To save memory, keep the ring buffer size to its minimum */
        if (ring_buffer_expanded)
                ring_buf_size = trace_buf_size;
@@ -4975,7 +5092,6 @@ __init static int tracer_alloc_buffers(void)
                WARN_ON(1);
                goto out_free_cpumask;
        }
-       global_trace.entries = ring_buffer_size(global_trace.buffer);
        if (global_trace.buffer_disabled)
                tracing_off();
 
@@ -4988,7 +5104,6 @@ __init static int tracer_alloc_buffers(void)
                ring_buffer_free(global_trace.buffer);
                goto out_free_cpumask;
        }
-       max_tr.entries = 1;
 #endif
 
        /* Allocate the first page for all buffers */
@@ -4997,6 +5112,12 @@ __init static int tracer_alloc_buffers(void)
                max_tr.data[i] = &per_cpu(max_tr_data, i);
        }
 
+       set_buffer_entries(&global_trace,
+                          ring_buffer_size(global_trace.buffer, 0));
+#ifdef CONFIG_TRACER_MAX_TRACE
+       set_buffer_entries(&max_tr, 1);
+#endif
+
        trace_init_cmdlines();
 
        register_tracer(&nop_trace);
index f95d65d..6c6f793 100644 (file)
@@ -131,6 +131,7 @@ struct trace_array_cpu {
        atomic_t                disabled;
        void                    *buffer_page;   /* ring buffer spare */
 
+       unsigned long           entries;
        unsigned long           saved_latency;
        unsigned long           critical_start;
        unsigned long           critical_end;
@@ -152,7 +153,6 @@ struct trace_array_cpu {
  */
 struct trace_array {
        struct ring_buffer      *buffer;
-       unsigned long           entries;
        int                     cpu;
        int                     buffer_disabled;
        cycle_t                 time_start;
@@ -826,6 +826,8 @@ extern struct list_head ftrace_events;
 extern const char *__start___trace_bprintk_fmt[];
 extern const char *__stop___trace_bprintk_fmt[];
 
+void trace_printk_init_buffers(void);
+
 #undef FTRACE_ENTRY
 #define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter)    \
        extern struct ftrace_event_call                                 \
index 6fd4ffd..a9077c1 100644 (file)
@@ -51,6 +51,10 @@ void hold_module_trace_bprintk_format(const char **start, const char **end)
        const char **iter;
        char *fmt;
 
+       /* allocate the trace_printk per cpu buffers */
+       if (start != end)
+               trace_printk_init_buffers();
+
        mutex_lock(&btrace_mutex);
        for (iter = start; iter < end; iter++) {
                struct trace_bprintk_fmt *tb_fmt = lookup_format(*iter);
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644 (file)
index 0000000..3ae4394
--- /dev/null
@@ -0,0 +1,77 @@
+include scripts/Makefile.include
+
+help:
+       @echo 'Possible targets:'
+       @echo ''
+       @echo '  cpupower   - a tool for all things x86 CPU power'
+       @echo '  firewire   - the userspace part of nosy, an IEEE-1394 traffic sniffer'
+       @echo '  lguest     - a minimal 32-bit x86 hypervisor'
+       @echo '  perf       - Linux performance measurement and analysis tool'
+       @echo '  selftests  - various kernel selftests'
+       @echo '  turbostat  - Intel CPU idle stats and freq reporting tool'
+       @echo '  usb        - USB testing tools'
+       @echo '  virtio     - vhost test module'
+       @echo '  vm         - misc vm tools'
+       @echo '  x86_energy_perf_policy - Intel energy policy tool'
+       @echo ''
+       @echo 'You can do:'
+       @echo ' $$ make -C tools/<tool>_install'
+       @echo ''
+       @echo '  from the kernel command line to build and install one of'
+       @echo '  the tools above'
+       @echo ''
+       @echo '  $$ make tools/install'
+       @echo ''
+       @echo '  installs all tools.'
+       @echo ''
+       @echo 'Cleaning targets:'
+       @echo ''
+       @echo '  all of the above with the "_clean" string appended cleans'
+       @echo '    the respective build directory.'
+       @echo '  clean: a summary clean target to clean _all_ folders'
+
+cpupower: FORCE
+       $(QUIET_SUBDIR0)power/$@/ $(QUIET_SUBDIR1)
+
+firewire lguest perf usb virtio vm: FORCE
+       $(QUIET_SUBDIR0)$@/ $(QUIET_SUBDIR1)
+
+selftests: FORCE
+       $(QUIET_SUBDIR0)testing/$@/ $(QUIET_SUBDIR1)
+
+turbostat x86_energy_perf_policy: FORCE
+       $(QUIET_SUBDIR0)power/x86/$@/ $(QUIET_SUBDIR1)
+
+cpupower_install:
+       $(QUIET_SUBDIR0)power/$(@:_install=)/ $(QUIET_SUBDIR1) install
+
+firewire_install lguest_install perf_install usb_install virtio_install vm_install:
+       $(QUIET_SUBDIR0)$(@:_install=)/ $(QUIET_SUBDIR1) install
+
+selftests_install:
+       $(QUIET_SUBDIR0)testing/$(@:_clean=)/ $(QUIET_SUBDIR1) install
+
+turbostat_install x86_energy_perf_policy_install:
+       $(QUIET_SUBDIR0)power/x86/$(@:_install=)/ $(QUIET_SUBDIR1) install
+
+install: cpupower_install firewire_install lguest_install perf_install \
+               selftests_install turbostat_install usb_install virtio_install \
+               vm_install x86_energy_perf_policy_install
+
+cpupower_clean:
+       $(QUIET_SUBDIR0)power/cpupower/ $(QUIET_SUBDIR1) clean
+
+firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean:
+       $(QUIET_SUBDIR0)$(@:_clean=)/ $(QUIET_SUBDIR1) clean
+
+selftests_clean:
+       $(QUIET_SUBDIR0)testing/$(@:_clean=)/ $(QUIET_SUBDIR1) clean
+
+turbostat_clean x86_energy_perf_policy_clean:
+       $(QUIET_SUBDIR0)power/x86/$(@:_clean=)/ $(QUIET_SUBDIR1) clean
+
+clean: cpupower_clean firewire_clean lguest_clean perf_clean selftests_clean \
+               turbostat_clean usb_clean virtio_clean vm_clean \
+               x86_energy_perf_policy_clean
+
+.PHONY: FORCE
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile
new file mode 100644 (file)
index 0000000..3d69aa9
--- /dev/null
@@ -0,0 +1,303 @@
+# trace-cmd version
+EP_VERSION = 1
+EP_PATCHLEVEL = 1
+EP_EXTRAVERSION = 0
+
+# file format version
+FILE_VERSION = 6
+
+MAKEFLAGS += --no-print-directory
+
+
+# Makefiles suck: This macro sets a default value of $(2) for the
+# variable named by $(1), unless the variable has been set by
+# environment or command line. This is necessary for CC and AR
+# because make sets default values, so the simpler ?= approach
+# won't work as expected.
+define allow-override
+  $(if $(or $(findstring environment,$(origin $(1))),\
+            $(findstring command line,$(origin $(1)))),,\
+    $(eval $(1) = $(2)))
+endef
+
+# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
+$(call allow-override,CC,$(CROSS_COMPILE)gcc)
+$(call allow-override,AR,$(CROSS_COMPILE)ar)
+
+EXT = -std=gnu99
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+prefix ?= /usr/local
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+man_dir = $(prefix)/share/man
+man_dir_SQ = '$(subst ','\'',$(man_dir))'
+html_install = $(prefix)/share/kernelshark/html
+html_install_SQ = '$(subst ','\'',$(html_install))'
+img_install = $(prefix)/share/kernelshark/html/images
+img_install_SQ = '$(subst ','\'',$(img_install))'
+
+export man_dir man_dir_SQ html_install html_install_SQ INSTALL
+export img_install img_install_SQ
+export DESTDIR DESTDIR_SQ
+
+# copy a bit from Linux kbuild
+
+ifeq ("$(origin V)", "command line")
+  VERBOSE = $(V)
+endif
+ifndef VERBOSE
+  VERBOSE = 0
+endif
+
+ifeq ("$(origin O)", "command line")
+  BUILD_OUTPUT := $(O)
+endif
+
+ifeq ($(BUILD_SRC),)
+ifneq ($(BUILD_OUTPUT),)
+
+define build_output
+       $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT)         \
+       BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1
+endef
+
+saved-output := $(BUILD_OUTPUT)
+BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd)
+$(if $(BUILD_OUTPUT),, \
+     $(error output directory "$(saved-output)" does not exist))
+
+all: sub-make
+
+gui: force
+       $(call build_output, all_cmd)
+
+$(filter-out gui,$(MAKECMDGOALS)): sub-make
+
+sub-make: force
+       $(call build_output, $(MAKECMDGOALS))
+
+
+# Leave processing to above invocation of make
+skip-makefile := 1
+
+endif # BUILD_OUTPUT
+endif # BUILD_SRC
+
+# We process the rest of the Makefile if this is the final invocation of make
+ifeq ($(skip-makefile),)
+
+srctree                := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR))
+objtree                := $(CURDIR)
+src            := $(srctree)
+obj            := $(objtree)
+
+export prefix bindir src obj
+
+# Shell quotes
+bindir_SQ = $(subst ','\'',$(bindir))
+bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
+
+LIB_FILE = libtraceevent.a libtraceevent.so
+
+CONFIG_INCLUDES = 
+CONFIG_LIBS    =
+CONFIG_FLAGS   =
+
+VERSION                = $(EP_VERSION)
+PATCHLEVEL     = $(EP_PATCHLEVEL)
+EXTRAVERSION   = $(EP_EXTRAVERSION)
+
+OBJ            = $@
+N              =
+
+export Q VERBOSE
+
+EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION)
+
+INCLUDES = -I. -I/usr/local/include $(CONFIG_INCLUDES)
+
+# Set compile option CFLAGS if not set elsewhere
+CFLAGS ?= -g -Wall
+
+# Append required CFLAGS
+override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
+override CFLAGS += $(udis86-flags)
+
+ifeq ($(VERBOSE),1)
+  Q =
+  print_compile =
+  print_app_build =
+  print_fpic_compile =
+  print_shared_lib_compile =
+  print_plugin_obj_compile =
+  print_plugin_build =
+  print_install =
+else
+  Q = @
+  print_compile =              echo '  CC                 '$(OBJ);
+  print_app_build =            echo '  BUILD              '$(OBJ);
+  print_fpic_compile =         echo '  CC FPIC            '$(OBJ);
+  print_shared_lib_compile =   echo '  BUILD SHARED LIB   '$(OBJ);
+  print_plugin_obj_compile =   echo '  CC PLUGIN OBJ      '$(OBJ);
+  print_plugin_build =         echo '  CC PLUGI           '$(OBJ);
+  print_static_lib_build =     echo '  BUILD STATIC LIB   '$(OBJ);
+  print_install =              echo '  INSTALL     '$1'        to      $(DESTDIR_SQ)$2';
+endif
+
+do_fpic_compile =                                      \
+       ($(print_fpic_compile)                          \
+       $(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@)
+
+do_app_build =                                         \
+       ($(print_app_build)                             \
+       $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS))
+
+do_compile_shared_library =                    \
+       ($(print_shared_lib_compile)            \
+       $(CC) --shared $^ -o $@)
+
+do_compile_plugin_obj =                                \
+       ($(print_plugin_obj_compile)            \
+       $(CC) -c $(CFLAGS) -fPIC -o $@ $<)
+
+do_plugin_build =                              \
+       ($(print_plugin_build)                  \
+       $(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<)
+
+do_build_static_lib =                          \
+       ($(print_static_lib_build)              \
+       $(RM) $@;  $(AR) rcs $@ $^)
+
+
+define do_compile
+       $(print_compile)                                                \
+       $(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
+endef
+
+$(obj)/%.o: $(src)/%.c
+       $(Q)$(call do_compile)
+
+%.o: $(src)/%.c
+       $(Q)$(call do_compile)
+
+PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o
+
+ALL_OBJS = $(PEVENT_LIB_OBJS)
+
+CMD_TARGETS = $(LIB_FILE)
+
+TARGETS = $(CMD_TARGETS)
+
+
+all: all_cmd
+
+all_cmd: $(CMD_TARGETS)
+
+libtraceevent.so: $(PEVENT_LIB_OBJS)
+       $(Q)$(do_compile_shared_library)
+
+libtraceevent.a: $(PEVENT_LIB_OBJS)
+       $(Q)$(do_build_static_lib)
+
+$(PEVENT_LIB_OBJS): %.o: $(src)/%.c
+       $(Q)$(do_fpic_compile)
+
+define make_version.h
+       (echo '/* This file is automatically generated. Do not modify. */';             \
+       echo \#define VERSION_CODE $(shell                                              \
+       expr $(VERSION) \* 256 + $(PATCHLEVEL));                                        \
+       echo '#define EXTRAVERSION ' $(EXTRAVERSION);                                   \
+       echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"';     \
+       echo '#define FILE_VERSION '$(FILE_VERSION);                                    \
+       ) > $1
+endef
+
+define update_version.h
+       ($(call make_version.h, $@.tmp);                \
+       if [ -r $@ ] && cmp -s $@ $@.tmp; then          \
+               rm -f $@.tmp;                           \
+       else                                            \
+               echo '  UPDATE                 $@';     \
+               mv -f $@.tmp $@;                        \
+       fi);
+endef
+
+ep_version.h: force
+       $(Q)$(N)$(call update_version.h)
+
+VERSION_FILES = ep_version.h
+
+define update_dir
+       (echo $1 > $@.tmp;      \
+       if [ -r $@ ] && cmp -s $@ $@.tmp; then          \
+               rm -f $@.tmp;                           \
+       else                                            \
+               echo '  UPDATE                 $@';     \
+               mv -f $@.tmp $@;                        \
+       fi);
+endef
+
+## make deps
+
+all_objs := $(sort $(ALL_OBJS))
+all_deps := $(all_objs:%.o=.%.d)
+
+define check_deps
+               $(CC) -M $(CFLAGS) $< > $@;
+endef
+
+$(gui_deps): ks_version.h
+$(non_gui_deps): tc_version.h
+
+$(all_deps): .%.d: $(src)/%.c
+       $(Q)$(call check_deps)
+
+$(all_objs) : %.o : .%.d
+
+dep_includes := $(wildcard $(all_deps))
+
+ifneq ($(dep_includes),)
+ include $(dep_includes)
+endif
+
+tags:  force
+       $(RM) tags
+       find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px
+
+TAGS:  force
+       $(RM) TAGS
+       find . -name '*.[ch]' | xargs etags
+
+define do_install
+       $(print_install)                                \
+       if [ ! -d '$(DESTDIR_SQ)$2' ]; then             \
+               $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
+       fi;                                             \
+       $(INSTALL) $1 '$(DESTDIR_SQ)$2'
+endef
+
+install_lib: all_cmd install_plugins install_python
+       $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ))
+
+install: install_lib
+
+clean:
+       $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES).*.d
+       $(RM) tags TAGS
+
+endif # skip-makefile
+
+PHONY += force
+force:
+
+# Declare the contents of the .PHONY variable as phony.  We keep that
+# information in a variable so we can use it in if_changed and friends.
+.PHONY: $(PHONY)
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
new file mode 100644 (file)
index 0000000..9985349
--- /dev/null
@@ -0,0 +1,5065 @@
+/*
+ * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  The parts for function graph printing was taken and modified from the
+ *  Linux Kernel that were written by
+ *    - Copyright (C) 2009  Frederic Weisbecker,
+ *  Frederic Weisbecker gave his permission to relicense the code to
+ *  the Lesser General Public License.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "event-parse.h"
+#include "event-utils.h"
+
+static const char *input_buf;
+static unsigned long long input_buf_ptr;
+static unsigned long long input_buf_siz;
+
+static int is_flag_field;
+static int is_symbolic_field;
+
+static int show_warning = 1;
+
+#define do_warning(fmt, ...)                           \
+       do {                                            \
+               if (show_warning)                       \
+                       warning(fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+static void init_input_buf(const char *buf, unsigned long long size)
+{
+       input_buf = buf;
+       input_buf_siz = size;
+       input_buf_ptr = 0;
+}
+
+const char *pevent_get_input_buf(void)
+{
+       return input_buf;
+}
+
+unsigned long long pevent_get_input_buf_ptr(void)
+{
+       return input_buf_ptr;
+}
+
+struct event_handler {
+       struct event_handler            *next;
+       int                             id;
+       const char                      *sys_name;
+       const char                      *event_name;
+       pevent_event_handler_func       func;
+       void                            *context;
+};
+
+struct pevent_func_params {
+       struct pevent_func_params       *next;
+       enum pevent_func_arg_type       type;
+};
+
+struct pevent_function_handler {
+       struct pevent_function_handler  *next;
+       enum pevent_func_arg_type       ret_type;
+       char                            *name;
+       pevent_func_handler             func;
+       struct pevent_func_params       *params;
+       int                             nr_args;
+};
+
+static unsigned long long
+process_defined_func(struct trace_seq *s, void *data, int size,
+                    struct event_format *event, struct print_arg *arg);
+
+static void free_func_handle(struct pevent_function_handler *func);
+
+/**
+ * pevent_buffer_init - init buffer for parsing
+ * @buf: buffer to parse
+ * @size: the size of the buffer
+ *
+ * For use with pevent_read_token(), this initializes the internal
+ * buffer that pevent_read_token() will parse.
+ */
+void pevent_buffer_init(const char *buf, unsigned long long size)
+{
+       init_input_buf(buf, size);
+}
+
+void breakpoint(void)
+{
+       static int x;
+       x++;
+}
+
+struct print_arg *alloc_arg(void)
+{
+       struct print_arg *arg;
+
+       arg = malloc_or_die(sizeof(*arg));
+       if (!arg)
+               return NULL;
+       memset(arg, 0, sizeof(*arg));
+
+       return arg;
+}
+
+struct cmdline {
+       char *comm;
+       int pid;
+};
+
+static int cmdline_cmp(const void *a, const void *b)
+{
+       const struct cmdline *ca = a;
+       const struct cmdline *cb = b;
+
+       if (ca->pid < cb->pid)
+               return -1;
+       if (ca->pid > cb->pid)
+               return 1;
+
+       return 0;
+}
+
+struct cmdline_list {
+       struct cmdline_list     *next;
+       char                    *comm;
+       int                     pid;
+};
+
+static int cmdline_init(struct pevent *pevent)
+{
+       struct cmdline_list *cmdlist = pevent->cmdlist;
+       struct cmdline_list *item;
+       struct cmdline *cmdlines;
+       int i;
+
+       cmdlines = malloc_or_die(sizeof(*cmdlines) * pevent->cmdline_count);
+
+       i = 0;
+       while (cmdlist) {
+               cmdlines[i].pid = cmdlist->pid;
+               cmdlines[i].comm = cmdlist->comm;
+               i++;
+               item = cmdlist;
+               cmdlist = cmdlist->next;
+               free(item);
+       }
+
+       qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+
+       pevent->cmdlines = cmdlines;
+       pevent->cmdlist = NULL;
+
+       return 0;
+}
+
+static char *find_cmdline(struct pevent *pevent, int pid)
+{
+       const struct cmdline *comm;
+       struct cmdline key;
+
+       if (!pid)
+               return "<idle>";
+
+       if (!pevent->cmdlines)
+               cmdline_init(pevent);
+
+       key.pid = pid;
+
+       comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count,
+                      sizeof(*pevent->cmdlines), cmdline_cmp);
+
+       if (comm)
+               return comm->comm;
+       return "<...>";
+}
+
+/**
+ * pevent_pid_is_registered - return if a pid has a cmdline registered
+ * @pevent: handle for the pevent
+ * @pid: The pid to check if it has a cmdline registered with.
+ *
+ * Returns 1 if the pid has a cmdline mapped to it
+ * 0 otherwise.
+ */
+int pevent_pid_is_registered(struct pevent *pevent, int pid)
+{
+       const struct cmdline *comm;
+       struct cmdline key;
+
+       if (!pid)
+               return 1;
+
+       if (!pevent->cmdlines)
+               cmdline_init(pevent);
+
+       key.pid = pid;
+
+       comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count,
+                      sizeof(*pevent->cmdlines), cmdline_cmp);
+
+       if (comm)
+               return 1;
+       return 0;
+}
+
+/*
+ * If the command lines have been converted to an array, then
+ * we must add this pid. This is much slower than when cmdlines
+ * are added before the array is initialized.
+ */
+static int add_new_comm(struct pevent *pevent, const char *comm, int pid)
+{
+       struct cmdline *cmdlines = pevent->cmdlines;
+       const struct cmdline *cmdline;
+       struct cmdline key;
+
+       if (!pid)
+               return 0;
+
+       /* avoid duplicates */
+       key.pid = pid;
+
+       cmdline = bsearch(&key, pevent->cmdlines, pevent->cmdline_count,
+                      sizeof(*pevent->cmdlines), cmdline_cmp);
+       if (cmdline) {
+               errno = EEXIST;
+               return -1;
+       }
+
+       cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (pevent->cmdline_count + 1));
+       if (!cmdlines) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       cmdlines[pevent->cmdline_count].pid = pid;
+       cmdlines[pevent->cmdline_count].comm = strdup(comm);
+       if (!cmdlines[pevent->cmdline_count].comm)
+               die("malloc comm");
+               
+       if (cmdlines[pevent->cmdline_count].comm)
+               pevent->cmdline_count++;
+
+       qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+       pevent->cmdlines = cmdlines;
+
+       return 0;
+}
+
+/**
+ * pevent_register_comm - register a pid / comm mapping
+ * @pevent: handle for the pevent
+ * @comm: the command line to register
+ * @pid: the pid to map the command line to
+ *
+ * This adds a mapping to search for command line names with
+ * a given pid. The comm is duplicated.
+ */
+int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
+{
+       struct cmdline_list *item;
+
+       if (pevent->cmdlines)
+               return add_new_comm(pevent, comm, pid);
+
+       item = malloc_or_die(sizeof(*item));
+       item->comm = strdup(comm);
+       if (!item->comm)
+               die("malloc comm");
+       item->pid = pid;
+       item->next = pevent->cmdlist;
+
+       pevent->cmdlist = item;
+       pevent->cmdline_count++;
+
+       return 0;
+}
+
+struct func_map {
+       unsigned long long              addr;
+       char                            *func;
+       char                            *mod;
+};
+
+struct func_list {
+       struct func_list        *next;
+       unsigned long long      addr;
+       char                    *func;
+       char                    *mod;
+};
+
+static int func_cmp(const void *a, const void *b)
+{
+       const struct func_map *fa = a;
+       const struct func_map *fb = b;
+
+       if (fa->addr < fb->addr)
+               return -1;
+       if (fa->addr > fb->addr)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * We are searching for a record in between, not an exact
+ * match.
+ */
+static int func_bcmp(const void *a, const void *b)
+{
+       const struct func_map *fa = a;
+       const struct func_map *fb = b;
+
+       if ((fa->addr == fb->addr) ||
+
+           (fa->addr > fb->addr &&
+            fa->addr < (fb+1)->addr))
+               return 0;
+
+       if (fa->addr < fb->addr)
+               return -1;
+
+       return 1;
+}
+
+static int func_map_init(struct pevent *pevent)
+{
+       struct func_list *funclist;
+       struct func_list *item;
+       struct func_map *func_map;
+       int i;
+
+       func_map = malloc_or_die(sizeof(*func_map) * (pevent->func_count + 1));
+       funclist = pevent->funclist;
+
+       i = 0;
+       while (funclist) {
+               func_map[i].func = funclist->func;
+               func_map[i].addr = funclist->addr;
+               func_map[i].mod = funclist->mod;
+               i++;
+               item = funclist;
+               funclist = funclist->next;
+               free(item);
+       }
+
+       qsort(func_map, pevent->func_count, sizeof(*func_map), func_cmp);
+
+       /*
+        * Add a special record at the end.
+        */
+       func_map[pevent->func_count].func = NULL;
+       func_map[pevent->func_count].addr = 0;
+       func_map[pevent->func_count].mod = NULL;
+
+       pevent->func_map = func_map;
+       pevent->funclist = NULL;
+
+       return 0;
+}
+
+static struct func_map *
+find_func(struct pevent *pevent, unsigned long long addr)
+{
+       struct func_map *func;
+       struct func_map key;
+
+       if (!pevent->func_map)
+               func_map_init(pevent);
+
+       key.addr = addr;
+
+       func = bsearch(&key, pevent->func_map, pevent->func_count,
+                      sizeof(*pevent->func_map), func_bcmp);
+
+       return func;
+}
+
+/**
+ * pevent_find_function - find a function by a given address
+ * @pevent: handle for the pevent
+ * @addr: the address to find the function with
+ *
+ * Returns a pointer to the function stored that has the given
+ * address. Note, the address does not have to be exact, it
+ * will select the function that would contain the address.
+ */
+const char *pevent_find_function(struct pevent *pevent, unsigned long long addr)
+{
+       struct func_map *map;
+
+       map = find_func(pevent, addr);
+       if (!map)
+               return NULL;
+
+       return map->func;
+}
+
+/**
+ * pevent_find_function_address - find a function address by a given address
+ * @pevent: handle for the pevent
+ * @addr: the address to find the function with
+ *
+ * Returns the address the function starts at. This can be used in
+ * conjunction with pevent_find_function to print both the function
+ * name and the function offset.
+ */
+unsigned long long
+pevent_find_function_address(struct pevent *pevent, unsigned long long addr)
+{
+       struct func_map *map;
+
+       map = find_func(pevent, addr);
+       if (!map)
+               return 0;
+
+       return map->addr;
+}
+
+/**
+ * pevent_register_function - register a function with a given address
+ * @pevent: handle for the pevent
+ * @function: the function name to register
+ * @addr: the address the function starts at
+ * @mod: the kernel module the function may be in (NULL for none)
+ *
+ * This registers a function name with an address and module.
+ * The @func passed in is duplicated.
+ */
+int pevent_register_function(struct pevent *pevent, char *func,
+                            unsigned long long addr, char *mod)
+{
+       struct func_list *item;
+
+       item = malloc_or_die(sizeof(*item));
+
+       item->next = pevent->funclist;
+       item->func = strdup(func);
+       if (mod)
+               item->mod = strdup(mod);
+       else
+               item->mod = NULL;
+       item->addr = addr;
+
+       pevent->funclist = item;
+
+       pevent->func_count++;
+
+       return 0;
+}
+
+/**
+ * pevent_print_funcs - print out the stored functions
+ * @pevent: handle for the pevent
+ *
+ * This prints out the stored functions.
+ */
+void pevent_print_funcs(struct pevent *pevent)
+{
+       int i;
+
+       if (!pevent->func_map)
+               func_map_init(pevent);
+
+       for (i = 0; i < (int)pevent->func_count; i++) {
+               printf("%016llx %s",
+                      pevent->func_map[i].addr,
+                      pevent->func_map[i].func);
+               if (pevent->func_map[i].mod)
+                       printf(" [%s]\n", pevent->func_map[i].mod);
+               else
+                       printf("\n");
+       }
+}
+
+struct printk_map {
+       unsigned long long              addr;
+       char                            *printk;
+};
+
+struct printk_list {
+       struct printk_list      *next;
+       unsigned long long      addr;
+       char                    *printk;
+};
+
+static int printk_cmp(const void *a, const void *b)
+{
+       const struct func_map *fa = a;
+       const struct func_map *fb = b;
+
+       if (fa->addr < fb->addr)
+               return -1;
+       if (fa->addr > fb->addr)
+               return 1;
+
+       return 0;
+}
+
+static void printk_map_init(struct pevent *pevent)
+{
+       struct printk_list *printklist;
+       struct printk_list *item;
+       struct printk_map *printk_map;
+       int i;
+
+       printk_map = malloc_or_die(sizeof(*printk_map) * (pevent->printk_count + 1));
+
+       printklist = pevent->printklist;
+
+       i = 0;
+       while (printklist) {
+               printk_map[i].printk = printklist->printk;
+               printk_map[i].addr = printklist->addr;
+               i++;
+               item = printklist;
+               printklist = printklist->next;
+               free(item);
+       }
+
+       qsort(printk_map, pevent->printk_count, sizeof(*printk_map), printk_cmp);
+
+       pevent->printk_map = printk_map;
+       pevent->printklist = NULL;
+}
+
+static struct printk_map *
+find_printk(struct pevent *pevent, unsigned long long addr)
+{
+       struct printk_map *printk;
+       struct printk_map key;
+
+       if (!pevent->printk_map)
+               printk_map_init(pevent);
+
+       key.addr = addr;
+
+       printk = bsearch(&key, pevent->printk_map, pevent->printk_count,
+                        sizeof(*pevent->printk_map), printk_cmp);
+
+       return printk;
+}
+
+/**
+ * pevent_register_print_string - register a string by its address
+ * @pevent: handle for the pevent
+ * @fmt: the string format to register
+ * @addr: the address the string was located at
+ *
+ * This registers a string by the address it was stored in the kernel.
+ * The @fmt passed in is duplicated.
+ */
+int pevent_register_print_string(struct pevent *pevent, char *fmt,
+                                unsigned long long addr)
+{
+       struct printk_list *item;
+
+       item = malloc_or_die(sizeof(*item));
+
+       item->next = pevent->printklist;
+       pevent->printklist = item;
+       item->printk = strdup(fmt);
+       item->addr = addr;
+
+       pevent->printk_count++;
+
+       return 0;
+}
+
+/**
+ * pevent_print_printk - print out the stored strings
+ * @pevent: handle for the pevent
+ *
+ * This prints the string formats that were stored.
+ */
+void pevent_print_printk(struct pevent *pevent)
+{
+       int i;
+
+       if (!pevent->printk_map)
+               printk_map_init(pevent);
+
+       for (i = 0; i < (int)pevent->printk_count; i++) {
+               printf("%016llx %s\n",
+                      pevent->printk_map[i].addr,
+                      pevent->printk_map[i].printk);
+       }
+}
+
+static struct event_format *alloc_event(void)
+{
+       struct event_format *event;
+
+       event = malloc_or_die(sizeof(*event));
+       memset(event, 0, sizeof(*event));
+
+       return event;
+}
+
+static void add_event(struct pevent *pevent, struct event_format *event)
+{
+       int i;
+
+       if (!pevent->events)
+               pevent->events = malloc_or_die(sizeof(event));
+       else
+               pevent->events =
+                       realloc(pevent->events, sizeof(event) *
+                               (pevent->nr_events + 1));
+       if (!pevent->events)
+               die("Can not allocate events");
+
+       for (i = 0; i < pevent->nr_events; i++) {
+               if (pevent->events[i]->id > event->id)
+                       break;
+       }
+       if (i < pevent->nr_events)
+               memmove(&pevent->events[i + 1],
+                       &pevent->events[i],
+                       sizeof(event) * (pevent->nr_events - i));
+
+       pevent->events[i] = event;
+       pevent->nr_events++;
+
+       event->pevent = pevent;
+}
+
+static int event_item_type(enum event_type type)
+{
+       switch (type) {
+       case EVENT_ITEM ... EVENT_SQUOTE:
+               return 1;
+       case EVENT_ERROR ... EVENT_DELIM:
+       default:
+               return 0;
+       }
+}
+
+static void free_flag_sym(struct print_flag_sym *fsym)
+{
+       struct print_flag_sym *next;
+
+       while (fsym) {
+               next = fsym->next;
+               free(fsym->value);
+               free(fsym->str);
+               free(fsym);
+               fsym = next;
+       }
+}
+
+static void free_arg(struct print_arg *arg)
+{
+       struct print_arg *farg;
+
+       if (!arg)
+               return;
+
+       switch (arg->type) {
+       case PRINT_ATOM:
+               free(arg->atom.atom);
+               break;
+       case PRINT_FIELD:
+               free(arg->field.name);
+               break;
+       case PRINT_FLAGS:
+               free_arg(arg->flags.field);
+               free(arg->flags.delim);
+               free_flag_sym(arg->flags.flags);
+               break;
+       case PRINT_SYMBOL:
+               free_arg(arg->symbol.field);
+               free_flag_sym(arg->symbol.symbols);
+               break;
+       case PRINT_TYPE:
+               free(arg->typecast.type);
+               free_arg(arg->typecast.item);
+               break;
+       case PRINT_STRING:
+       case PRINT_BSTRING:
+               free(arg->string.string);
+               break;
+       case PRINT_DYNAMIC_ARRAY:
+               free(arg->dynarray.index);
+               break;
+       case PRINT_OP:
+               free(arg->op.op);
+               free_arg(arg->op.left);
+               free_arg(arg->op.right);
+               break;
+       case PRINT_FUNC:
+               while (arg->func.args) {
+                       farg = arg->func.args;
+                       arg->func.args = farg->next;
+                       free_arg(farg);
+               }
+               break;
+
+       case PRINT_NULL:
+       default:
+               break;
+       }
+
+       free(arg);
+}
+
+static enum event_type get_type(int ch)
+{
+       if (ch == '\n')
+               return EVENT_NEWLINE;
+       if (isspace(ch))
+               return EVENT_SPACE;
+       if (isalnum(ch) || ch == '_')
+               return EVENT_ITEM;
+       if (ch == '\'')
+               return EVENT_SQUOTE;
+       if (ch == '"')
+               return EVENT_DQUOTE;
+       if (!isprint(ch))
+               return EVENT_NONE;
+       if (ch == '(' || ch == ')' || ch == ',')
+               return EVENT_DELIM;
+
+       return EVENT_OP;
+}
+
+static int __read_char(void)
+{
+       if (input_buf_ptr >= input_buf_siz)
+               return -1;
+
+       return input_buf[input_buf_ptr++];
+}
+
+static int __peek_char(void)
+{
+       if (input_buf_ptr >= input_buf_siz)
+               return -1;
+
+       return input_buf[input_buf_ptr];
+}
+
+/**
+ * pevent_peek_char - peek at the next character that will be read
+ *
+ * Returns the next character read, or -1 if end of buffer.
+ */
+int pevent_peek_char(void)
+{
+       return __peek_char();
+}
+
+static enum event_type force_token(const char *str, char **tok);
+
+static enum event_type __read_token(char **tok)
+{
+       char buf[BUFSIZ];
+       int ch, last_ch, quote_ch, next_ch;
+       int i = 0;
+       int tok_size = 0;
+       enum event_type type;
+
+       *tok = NULL;
+
+
+       ch = __read_char();
+       if (ch < 0)
+               return EVENT_NONE;
+
+       type = get_type(ch);
+       if (type == EVENT_NONE)
+               return type;
+
+       buf[i++] = ch;
+
+       switch (type) {
+       case EVENT_NEWLINE:
+       case EVENT_DELIM:
+               *tok = malloc_or_die(2);
+               (*tok)[0] = ch;
+               (*tok)[1] = 0;
+               return type;
+
+       case EVENT_OP:
+               switch (ch) {
+               case '-':
+                       next_ch = __peek_char();
+                       if (next_ch == '>') {
+                               buf[i++] = __read_char();
+                               break;
+                       }
+                       /* fall through */
+               case '+':
+               case '|':
+               case '&':
+               case '>':
+               case '<':
+                       last_ch = ch;
+                       ch = __peek_char();
+                       if (ch != last_ch)
+                               goto test_equal;
+                       buf[i++] = __read_char();
+                       switch (last_ch) {
+                       case '>':
+                       case '<':
+                               goto test_equal;
+                       default:
+                               break;
+                       }
+                       break;
+               case '!':
+               case '=':
+                       goto test_equal;
+               default: /* what should we do instead? */
+                       break;
+               }
+               buf[i] = 0;
+               *tok = strdup(buf);
+               return type;
+
+ test_equal:
+               ch = __peek_char();
+               if (ch == '=')
+                       buf[i++] = __read_char();
+               goto out;
+
+       case EVENT_DQUOTE:
+       case EVENT_SQUOTE:
+               /* don't keep quotes */
+               i--;
+               quote_ch = ch;
+               last_ch = 0;
+ concat:
+               do {
+                       if (i == (BUFSIZ - 1)) {
+                               buf[i] = 0;
+                               if (*tok) {
+                                       *tok = realloc(*tok, tok_size + BUFSIZ);
+                                       if (!*tok)
+                                               return EVENT_NONE;
+                                       strcat(*tok, buf);
+                               } else
+                                       *tok = strdup(buf);
+
+                               if (!*tok)
+                                       return EVENT_NONE;
+                               tok_size += BUFSIZ;
+                               i = 0;
+                       }
+                       last_ch = ch;
+                       ch = __read_char();
+                       buf[i++] = ch;
+                       /* the '\' '\' will cancel itself */
+                       if (ch == '\\' && last_ch == '\\')
+                               last_ch = 0;
+               } while (ch != quote_ch || last_ch == '\\');
+               /* remove the last quote */
+               i--;
+
+               /*
+                * For strings (double quotes) check the next token.
+                * If it is another string, concatinate the two.
+                */
+               if (type == EVENT_DQUOTE) {
+                       unsigned long long save_input_buf_ptr = input_buf_ptr;
+
+                       do {
+                               ch = __read_char();
+                       } while (isspace(ch));
+                       if (ch == '"')
+                               goto concat;
+                       input_buf_ptr = save_input_buf_ptr;
+               }
+
+               goto out;
+
+       case EVENT_ERROR ... EVENT_SPACE:
+       case EVENT_ITEM:
+       default:
+               break;
+       }
+
+       while (get_type(__peek_char()) == type) {
+               if (i == (BUFSIZ - 1)) {
+                       buf[i] = 0;
+                       if (*tok) {
+                               *tok = realloc(*tok, tok_size + BUFSIZ);
+                               if (!*tok)
+                                       return EVENT_NONE;
+                               strcat(*tok, buf);
+                       } else
+                               *tok = strdup(buf);
+
+                       if (!*tok)
+                               return EVENT_NONE;
+                       tok_size += BUFSIZ;
+                       i = 0;
+               }
+               ch = __read_char();
+               buf[i++] = ch;
+       }
+
+ out:
+       buf[i] = 0;
+       if (*tok) {
+               *tok = realloc(*tok, tok_size + i);
+               if (!*tok)
+                       return EVENT_NONE;
+               strcat(*tok, buf);
+       } else
+               *tok = strdup(buf);
+       if (!*tok)
+               return EVENT_NONE;
+
+       if (type == EVENT_ITEM) {
+               /*
+                * Older versions of the kernel has a bug that
+                * creates invalid symbols and will break the mac80211
+                * parsing. This is a work around to that bug.
+                *
+                * See Linux kernel commit:
+                *  811cb50baf63461ce0bdb234927046131fc7fa8b
+                */
+               if (strcmp(*tok, "LOCAL_PR_FMT") == 0) {
+                       free(*tok);
+                       *tok = NULL;
+                       return force_token("\"\%s\" ", tok);
+               } else if (strcmp(*tok, "STA_PR_FMT") == 0) {
+                       free(*tok);
+                       *tok = NULL;
+                       return force_token("\" sta:%pM\" ", tok);
+               } else if (strcmp(*tok, "VIF_PR_FMT") == 0) {
+                       free(*tok);
+                       *tok = NULL;
+                       return force_token("\" vif:%p(%d)\" ", tok);
+               }
+       }
+
+       return type;
+}
+
+static enum event_type force_token(const char *str, char **tok)
+{
+       const char *save_input_buf;
+       unsigned long long save_input_buf_ptr;
+       unsigned long long save_input_buf_siz;
+       enum event_type type;
+       
+       /* save off the current input pointers */
+       save_input_buf = input_buf;
+       save_input_buf_ptr = input_buf_ptr;
+       save_input_buf_siz = input_buf_siz;
+
+       init_input_buf(str, strlen(str));
+
+       type = __read_token(tok);
+
+       /* reset back to original token */
+       input_buf = save_input_buf;
+       input_buf_ptr = save_input_buf_ptr;
+       input_buf_siz = save_input_buf_siz;
+
+       return type;
+}
+
+static void free_token(char *tok)
+{
+       if (tok)
+               free(tok);
+}
+
+static enum event_type read_token(char **tok)
+{
+       enum event_type type;
+
+       for (;;) {
+               type = __read_token(tok);
+               if (type != EVENT_SPACE)
+                       return type;
+
+               free_token(*tok);
+       }
+
+       /* not reached */
+       *tok = NULL;
+       return EVENT_NONE;
+}
+
+/**
+ * pevent_read_token - access to utilites to use the pevent parser
+ * @tok: The token to return
+ *
+ * This will parse tokens from the string given by
+ * pevent_init_data().
+ *
+ * Returns the token type.
+ */
+enum event_type pevent_read_token(char **tok)
+{
+       return read_token(tok);
+}
+
+/**
+ * pevent_free_token - free a token returned by pevent_read_token
+ * @token: the token to free
+ */
+void pevent_free_token(char *token)
+{
+       free_token(token);
+}
+
+/* no newline */
+static enum event_type read_token_item(char **tok)
+{
+       enum event_type type;
+
+       for (;;) {
+               type = __read_token(tok);
+               if (type != EVENT_SPACE && type != EVENT_NEWLINE)
+                       return type;
+               free_token(*tok);
+               *tok = NULL;
+       }
+
+       /* not reached */
+       *tok = NULL;
+       return EVENT_NONE;
+}
+
+static int test_type(enum event_type type, enum event_type expect)
+{
+       if (type != expect) {
+               do_warning("Error: expected type %d but read %d",
+                   expect, type);
+               return -1;
+       }
+       return 0;
+}
+
+static int test_type_token(enum event_type type, const char *token,
+                   enum event_type expect, const char *expect_tok)
+{
+       if (type != expect) {
+               do_warning("Error: expected type %d but read %d",
+                   expect, type);
+               return -1;
+       }
+
+       if (strcmp(token, expect_tok) != 0) {
+               do_warning("Error: expected '%s' but read '%s'",
+                   expect_tok, token);
+               return -1;
+       }
+       return 0;
+}
+
+static int __read_expect_type(enum event_type expect, char **tok, int newline_ok)
+{
+       enum event_type type;
+
+       if (newline_ok)
+               type = read_token(tok);
+       else
+               type = read_token_item(tok);
+       return test_type(type, expect);
+}
+
+static int read_expect_type(enum event_type expect, char **tok)
+{
+       return __read_expect_type(expect, tok, 1);
+}
+
+static int __read_expected(enum event_type expect, const char *str,
+                          int newline_ok)
+{
+       enum event_type type;
+       char *token;
+       int ret;
+
+       if (newline_ok)
+               type = read_token(&token);
+       else
+               type = read_token_item(&token);
+
+       ret = test_type_token(type, token, expect, str);
+
+       free_token(token);
+
+       return ret;
+}
+
+static int read_expected(enum event_type expect, const char *str)
+{
+       return __read_expected(expect, str, 1);
+}
+
+static int read_expected_item(enum event_type expect, const char *str)
+{
+       return __read_expected(expect, str, 0);
+}
+
+static char *event_read_name(void)
+{
+       char *token;
+
+       if (read_expected(EVENT_ITEM, "name") < 0)
+               return NULL;
+
+       if (read_expected(EVENT_OP, ":") < 0)
+               return NULL;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+
+       return token;
+
+ fail:
+       free_token(token);
+       return NULL;
+}
+
+static int event_read_id(void)
+{
+       char *token;
+       int id;
+
+       if (read_expected_item(EVENT_ITEM, "ID") < 0)
+               return -1;
+
+       if (read_expected(EVENT_OP, ":") < 0)
+               return -1;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+
+       id = strtoul(token, NULL, 0);
+       free_token(token);
+       return id;
+
+ fail:
+       free_token(token);
+       return -1;
+}
+
+static int field_is_string(struct format_field *field)
+{
+       if ((field->flags & FIELD_IS_ARRAY) &&
+           (strstr(field->type, "char") || strstr(field->type, "u8") ||
+            strstr(field->type, "s8")))
+               return 1;
+
+       return 0;
+}
+
+static int field_is_dynamic(struct format_field *field)
+{
+       if (strncmp(field->type, "__data_loc", 10) == 0)
+               return 1;
+
+       return 0;
+}
+
+static int field_is_long(struct format_field *field)
+{
+       /* includes long long */
+       if (strstr(field->type, "long"))
+               return 1;
+
+       return 0;
+}
+
+static int event_read_fields(struct event_format *event, struct format_field **fields)
+{
+       struct format_field *field = NULL;
+       enum event_type type;
+       char *token;
+       char *last_token;
+       int count = 0;
+
+       do {
+               type = read_token(&token);
+               if (type == EVENT_NEWLINE) {
+                       free_token(token);
+                       return count;
+               }
+
+               count++;
+
+               if (test_type_token(type, token, EVENT_ITEM, "field"))
+                       goto fail;
+               free_token(token);
+
+               type = read_token(&token);
+               /*
+                * The ftrace fields may still use the "special" name.
+                * Just ignore it.
+                */
+               if (event->flags & EVENT_FL_ISFTRACE &&
+                   type == EVENT_ITEM && strcmp(token, "special") == 0) {
+                       free_token(token);
+                       type = read_token(&token);
+               }
+
+               if (test_type_token(type, token, EVENT_OP, ":") < 0)
+                       goto fail;
+
+               free_token(token);
+               if (read_expect_type(EVENT_ITEM, &token) < 0)
+                       goto fail;
+
+               last_token = token;
+
+               field = malloc_or_die(sizeof(*field));
+               memset(field, 0, sizeof(*field));
+               field->event = event;
+
+               /* read the rest of the type */
+               for (;;) {
+                       type = read_token(&token);
+                       if (type == EVENT_ITEM ||
+                           (type == EVENT_OP && strcmp(token, "*") == 0) ||
+                           /*
+                            * Some of the ftrace fields are broken and have
+                            * an illegal "." in them.
+                            */
+                           (event->flags & EVENT_FL_ISFTRACE &&
+                            type == EVENT_OP && strcmp(token, ".") == 0)) {
+
+                               if (strcmp(token, "*") == 0)
+                                       field->flags |= FIELD_IS_POINTER;
+
+                               if (field->type) {
+                                       field->type = realloc(field->type,
+                                                             strlen(field->type) +
+                                                             strlen(last_token) + 2);
+                                       strcat(field->type, " ");
+                                       strcat(field->type, last_token);
+                                       free(last_token);
+                               } else
+                                       field->type = last_token;
+                               last_token = token;
+                               continue;
+                       }
+
+                       break;
+               }
+
+               if (!field->type) {
+                       die("no type found");
+                       goto fail;
+               }
+               field->name = last_token;
+
+               if (test_type(type, EVENT_OP))
+                       goto fail;
+
+               if (strcmp(token, "[") == 0) {
+                       enum event_type last_type = type;
+                       char *brackets = token;
+                       int len;
+
+                       field->flags |= FIELD_IS_ARRAY;
+
+                       type = read_token(&token);
+
+                       if (type == EVENT_ITEM)
+                               field->arraylen = strtoul(token, NULL, 0);
+                       else
+                               field->arraylen = 0;
+
+                       while (strcmp(token, "]") != 0) {
+                               if (last_type == EVENT_ITEM &&
+                                   type == EVENT_ITEM)
+                                       len = 2;
+                               else
+                                       len = 1;
+                               last_type = type;
+
+                               brackets = realloc(brackets,
+                                                  strlen(brackets) +
+                                                  strlen(token) + len);
+                               if (len == 2)
+                                       strcat(brackets, " ");
+                               strcat(brackets, token);
+                               /* We only care about the last token */
+                               field->arraylen = strtoul(token, NULL, 0);
+                               free_token(token);
+                               type = read_token(&token);
+                               if (type == EVENT_NONE) {
+                                       die("failed to find token");
+                                       goto fail;
+                               }
+                       }
+
+                       free_token(token);
+
+                       brackets = realloc(brackets, strlen(brackets) + 2);
+                       strcat(brackets, "]");
+
+                       /* add brackets to type */
+
+                       type = read_token(&token);
+                       /*
+                        * If the next token is not an OP, then it is of
+                        * the format: type [] item;
+                        */
+                       if (type == EVENT_ITEM) {
+                               field->type = realloc(field->type,
+                                                     strlen(field->type) +
+                                                     strlen(field->name) +
+                                                     strlen(brackets) + 2);
+                               strcat(field->type, " ");
+                               strcat(field->type, field->name);
+                               free_token(field->name);
+                               strcat(field->type, brackets);
+                               field->name = token;
+                               type = read_token(&token);
+                       } else {
+                               field->type = realloc(field->type,
+                                                     strlen(field->type) +
+                                                     strlen(brackets) + 1);
+                               strcat(field->type, brackets);
+                       }
+                       free(brackets);
+               }
+
+               if (field_is_string(field))
+                       field->flags |= FIELD_IS_STRING;
+               if (field_is_dynamic(field))
+                       field->flags |= FIELD_IS_DYNAMIC;
+               if (field_is_long(field))
+                       field->flags |= FIELD_IS_LONG;
+
+               if (test_type_token(type, token,  EVENT_OP, ";"))
+                       goto fail;
+               free_token(token);
+
+               if (read_expected(EVENT_ITEM, "offset") < 0)
+                       goto fail_expect;
+
+               if (read_expected(EVENT_OP, ":") < 0)
+                       goto fail_expect;
+
+               if (read_expect_type(EVENT_ITEM, &token))
+                       goto fail;
+               field->offset = strtoul(token, NULL, 0);
+               free_token(token);
+
+               if (read_expected(EVENT_OP, ";") < 0)
+                       goto fail_expect;
+
+               if (read_expected(EVENT_ITEM, "size") < 0)
+                       goto fail_expect;
+
+               if (read_expected(EVENT_OP, ":") < 0)
+                       goto fail_expect;
+
+               if (read_expect_type(EVENT_ITEM, &token))
+                       goto fail;
+               field->size = strtoul(token, NULL, 0);
+               free_token(token);
+
+               if (read_expected(EVENT_OP, ";") < 0)
+                       goto fail_expect;
+
+               type = read_token(&token);
+               if (type != EVENT_NEWLINE) {
+                       /* newer versions of the kernel have a "signed" type */
+                       if (test_type_token(type, token, EVENT_ITEM, "signed"))
+                               goto fail;
+
+                       free_token(token);
+
+                       if (read_expected(EVENT_OP, ":") < 0)
+                               goto fail_expect;
+
+                       if (read_expect_type(EVENT_ITEM, &token))
+                               goto fail;
+
+                       /* add signed type */
+
+                       free_token(token);
+                       if (read_expected(EVENT_OP, ";") < 0)
+                               goto fail_expect;
+
+                       if (read_expect_type(EVENT_NEWLINE, &token))
+                               goto fail;
+               }
+
+               free_token(token);
+
+               if (field->flags & FIELD_IS_ARRAY) {
+                       if (field->arraylen)
+                               field->elementsize = field->size / field->arraylen;
+                       else if (field->flags & FIELD_IS_STRING)
+                               field->elementsize = 1;
+                       else
+                               field->elementsize = event->pevent->long_size;
+               } else
+                       field->elementsize = field->size;
+
+               *fields = field;
+               fields = &field->next;
+
+       } while (1);
+
+       return 0;
+
+fail:
+       free_token(token);
+fail_expect:
+       if (field)
+               free(field);
+       return -1;
+}
+
+static int event_read_format(struct event_format *event)
+{
+       char *token;
+       int ret;
+
+       if (read_expected_item(EVENT_ITEM, "format") < 0)
+               return -1;
+
+       if (read_expected(EVENT_OP, ":") < 0)
+               return -1;
+
+       if (read_expect_type(EVENT_NEWLINE, &token))
+               goto fail;
+       free_token(token);
+
+       ret = event_read_fields(event, &event->format.common_fields);
+       if (ret < 0)
+               return ret;
+       event->format.nr_common = ret;
+
+       ret = event_read_fields(event, &event->format.fields);
+       if (ret < 0)
+               return ret;
+       event->format.nr_fields = ret;
+
+       return 0;
+
+ fail:
+       free_token(token);
+       return -1;
+}
+
+static enum event_type
+process_arg_token(struct event_format *event, struct print_arg *arg,
+                 char **tok, enum event_type type);
+
+static enum event_type
+process_arg(struct event_format *event, struct print_arg *arg, char **tok)
+{
+       enum event_type type;
+       char *token;
+
+       type = read_token(&token);
+       *tok = token;
+
+       return process_arg_token(event, arg, tok, type);
+}
+
+static enum event_type
+process_op(struct event_format *event, struct print_arg *arg, char **tok);
+
+static enum event_type
+process_cond(struct event_format *event, struct print_arg *top, char **tok)
+{
+       struct print_arg *arg, *left, *right;
+       enum event_type type;
+       char *token = NULL;
+
+       arg = alloc_arg();
+       left = alloc_arg();
+       right = alloc_arg();
+
+       arg->type = PRINT_OP;
+       arg->op.left = left;
+       arg->op.right = right;
+
+       *tok = NULL;
+       type = process_arg(event, left, &token);
+
+ again:
+       /* Handle other operations in the arguments */
+       if (type == EVENT_OP && strcmp(token, ":") != 0) {
+               type = process_op(event, left, &token);
+               goto again;
+       }
+
+       if (test_type_token(type, token, EVENT_OP, ":"))
+               goto out_free;
+
+       arg->op.op = token;
+
+       type = process_arg(event, right, &token);
+
+       top->op.right = arg;
+
+       *tok = token;
+       return type;
+
+out_free:
+       /* Top may point to itself */
+       top->op.right = NULL;
+       free_token(token);
+       free_arg(arg);
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_array(struct event_format *event, struct print_arg *top, char **tok)
+{
+       struct print_arg *arg;
+       enum event_type type;
+       char *token = NULL;
+
+       arg = alloc_arg();
+
+       *tok = NULL;
+       type = process_arg(event, arg, &token);
+       if (test_type_token(type, token, EVENT_OP, "]"))
+               goto out_free;
+
+       top->op.right = arg;
+
+       free_token(token);
+       type = read_token_item(&token);
+       *tok = token;
+
+       return type;
+
+out_free:
+       free_token(*tok);
+       *tok = NULL;
+       free_arg(arg);
+       return EVENT_ERROR;
+}
+
+static int get_op_prio(char *op)
+{
+       if (!op[1]) {
+               switch (op[0]) {
+               case '~':
+               case '!':
+                       return 4;
+               case '*':
+               case '/':
+               case '%':
+                       return 6;
+               case '+':
+               case '-':
+                       return 7;
+                       /* '>>' and '<<' are 8 */
+               case '<':
+               case '>':
+                       return 9;
+                       /* '==' and '!=' are 10 */
+               case '&':
+                       return 11;
+               case '^':
+                       return 12;
+               case '|':
+                       return 13;
+               case '?':
+                       return 16;
+               default:
+                       do_warning("unknown op '%c'", op[0]);
+                       return -1;
+               }
+       } else {
+               if (strcmp(op, "++") == 0 ||
+                   strcmp(op, "--") == 0) {
+                       return 3;
+               } else if (strcmp(op, ">>") == 0 ||
+                          strcmp(op, "<<") == 0) {
+                       return 8;
+               } else if (strcmp(op, ">=") == 0 ||
+                          strcmp(op, "<=") == 0) {
+                       return 9;
+               } else if (strcmp(op, "==") == 0 ||
+                          strcmp(op, "!=") == 0) {
+                       return 10;
+               } else if (strcmp(op, "&&") == 0) {
+                       return 14;
+               } else if (strcmp(op, "||") == 0) {
+                       return 15;
+               } else {
+                       do_warning("unknown op '%s'", op);
+                       return -1;
+               }
+       }
+}
+
+static int set_op_prio(struct print_arg *arg)
+{
+
+       /* single ops are the greatest */
+       if (!arg->op.left || arg->op.left->type == PRINT_NULL)
+               arg->op.prio = 0;
+       else
+               arg->op.prio = get_op_prio(arg->op.op);
+
+       return arg->op.prio;
+}
+
+/* Note, *tok does not get freed, but will most likely be saved */
+static enum event_type
+process_op(struct event_format *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *left, *right = NULL;
+       enum event_type type;
+       char *token;
+
+       /* the op is passed in via tok */
+       token = *tok;
+
+       if (arg->type == PRINT_OP && !arg->op.left) {
+               /* handle single op */
+               if (token[1]) {
+                       die("bad op token %s", token);
+                       goto out_free;
+               }
+               switch (token[0]) {
+               case '~':
+               case '!':
+               case '+':
+               case '-':
+                       break;
+               default:
+                       do_warning("bad op token %s", token);
+                       goto out_free;
+
+               }
+
+               /* make an empty left */
+               left = alloc_arg();
+               left->type = PRINT_NULL;
+               arg->op.left = left;
+
+               right = alloc_arg();
+               arg->op.right = right;
+
+               /* do not free the token, it belongs to an op */
+               *tok = NULL;
+               type = process_arg(event, right, tok);
+
+       } else if (strcmp(token, "?") == 0) {
+
+               left = alloc_arg();
+               /* copy the top arg to the left */
+               *left = *arg;
+
+               arg->type = PRINT_OP;
+               arg->op.op = token;
+               arg->op.left = left;
+               arg->op.prio = 0;
+
+               type = process_cond(event, arg, tok);
+
+       } else if (strcmp(token, ">>") == 0 ||
+                  strcmp(token, "<<") == 0 ||
+                  strcmp(token, "&") == 0 ||
+                  strcmp(token, "|") == 0 ||
+                  strcmp(token, "&&") == 0 ||
+                  strcmp(token, "||") == 0 ||
+                  strcmp(token, "-") == 0 ||
+                  strcmp(token, "+") == 0 ||
+                  strcmp(token, "*") == 0 ||
+                  strcmp(token, "^") == 0 ||
+                  strcmp(token, "/") == 0 ||
+                  strcmp(token, "<") == 0 ||
+                  strcmp(token, ">") == 0 ||
+                  strcmp(token, "==") == 0 ||
+                  strcmp(token, "!=") == 0) {
+
+               left = alloc_arg();
+
+               /* copy the top arg to the left */
+               *left = *arg;
+
+               arg->type = PRINT_OP;
+               arg->op.op = token;
+               arg->op.left = left;
+
+               if (set_op_prio(arg) == -1) {
+                       event->flags |= EVENT_FL_FAILED;
+                       goto out_free;
+               }
+
+               type = read_token_item(&token);
+               *tok = token;
+
+               /* could just be a type pointer */
+               if ((strcmp(arg->op.op, "*") == 0) &&
+                   type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
+                       if (left->type != PRINT_ATOM)
+                               die("bad pointer type");
+                       left->atom.atom = realloc(left->atom.atom,
+                                           strlen(left->atom.atom) + 3);
+                       strcat(left->atom.atom, " *");
+                       free(arg->op.op);
+                       *arg = *left;
+                       free(left);
+
+                       return type;
+               }
+
+               right = alloc_arg();
+               type = process_arg_token(event, right, tok, type);
+               arg->op.right = right;
+
+       } else if (strcmp(token, "[") == 0) {
+
+               left = alloc_arg();
+               *left = *arg;
+
+               arg->type = PRINT_OP;
+               arg->op.op = token;
+               arg->op.left = left;
+
+               arg->op.prio = 0;
+
+               type = process_array(event, arg, tok);
+
+       } else {
+               do_warning("unknown op '%s'", token);
+               event->flags |= EVENT_FL_FAILED;
+               /* the arg is now the left side */
+               goto out_free;
+       }
+
+       if (type == EVENT_OP && strcmp(*tok, ":") != 0) {
+               int prio;
+
+               /* higher prios need to be closer to the root */
+               prio = get_op_prio(*tok);
+
+               if (prio > arg->op.prio)
+                       return process_op(event, arg, tok);
+
+               return process_op(event, right, tok);
+       }
+
+       return type;
+
+ out_free:
+       free_token(token);
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_entry(struct event_format *event __unused, struct print_arg *arg,
+             char **tok)
+{
+       enum event_type type;
+       char *field;
+       char *token;
+
+       if (read_expected(EVENT_OP, "->") < 0)
+               goto out_err;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto out_free;
+       field = token;
+
+       arg->type = PRINT_FIELD;
+       arg->field.name = field;
+
+       if (is_flag_field) {
+               arg->field.field = pevent_find_any_field(event, arg->field.name);
+               arg->field.field->flags |= FIELD_IS_FLAG;
+               is_flag_field = 0;
+       } else if (is_symbolic_field) {
+               arg->field.field = pevent_find_any_field(event, arg->field.name);
+               arg->field.field->flags |= FIELD_IS_SYMBOLIC;
+               is_symbolic_field = 0;
+       }
+
+       type = read_token(&token);
+       *tok = token;
+
+       return type;
+
+ out_free:
+       free_token(token);
+ out_err:
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
+static char *arg_eval (struct print_arg *arg);
+
+static unsigned long long
+eval_type_str(unsigned long long val, const char *type, int pointer)
+{
+       int sign = 0;
+       char *ref;
+       int len;
+
+       len = strlen(type);
+
+       if (pointer) {
+
+               if (type[len-1] != '*') {
+                       do_warning("pointer expected with non pointer type");
+                       return val;
+               }
+
+               ref = malloc_or_die(len);
+               memcpy(ref, type, len);
+
+               /* chop off the " *" */
+               ref[len - 2] = 0;
+
+               val = eval_type_str(val, ref, 0);
+               free(ref);
+               return val;
+       }
+
+       /* check if this is a pointer */
+       if (type[len - 1] == '*')
+               return val;
+
+       /* Try to figure out the arg size*/
+       if (strncmp(type, "struct", 6) == 0)
+               /* all bets off */
+               return val;
+
+       if (strcmp(type, "u8") == 0)
+               return val & 0xff;
+
+       if (strcmp(type, "u16") == 0)
+               return val & 0xffff;
+
+       if (strcmp(type, "u32") == 0)
+               return val & 0xffffffff;
+
+       if (strcmp(type, "u64") == 0 ||
+           strcmp(type, "s64"))
+               return val;
+
+       if (strcmp(type, "s8") == 0)
+               return (unsigned long long)(char)val & 0xff;
+
+       if (strcmp(type, "s16") == 0)
+               return (unsigned long long)(short)val & 0xffff;
+
+       if (strcmp(type, "s32") == 0)
+               return (unsigned long long)(int)val & 0xffffffff;
+
+       if (strncmp(type, "unsigned ", 9) == 0) {
+               sign = 0;
+               type += 9;
+       }
+
+       if (strcmp(type, "char") == 0) {
+               if (sign)
+                       return (unsigned long long)(char)val & 0xff;
+               else
+                       return val & 0xff;
+       }
+
+       if (strcmp(type, "short") == 0) {
+               if (sign)
+                       return (unsigned long long)(short)val & 0xffff;
+               else
+                       return val & 0xffff;
+       }
+
+       if (strcmp(type, "int") == 0) {
+               if (sign)
+                       return (unsigned long long)(int)val & 0xffffffff;
+               else
+                       return val & 0xffffffff;
+       }
+
+       return val;
+}
+
+/*
+ * Try to figure out the type.
+ */
+static unsigned long long
+eval_type(unsigned long long val, struct print_arg *arg, int pointer)
+{
+       if (arg->type != PRINT_TYPE)
+               die("expected type argument");
+
+       return eval_type_str(val, arg->typecast.type, pointer);
+}
+
+static int arg_num_eval(struct print_arg *arg, long long *val)
+{
+       long long left, right;
+       int ret = 1;
+
+       switch (arg->type) {
+       case PRINT_ATOM:
+               *val = strtoll(arg->atom.atom, NULL, 0);
+               break;
+       case PRINT_TYPE:
+               ret = arg_num_eval(arg->typecast.item, val);
+               if (!ret)
+                       break;
+               *val = eval_type(*val, arg, 0);
+               break;
+       case PRINT_OP:
+               switch (arg->op.op[0]) {
+               case '|':
+                       ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+                       if (arg->op.op[1])
+                               *val = left || right;
+                       else
+                               *val = left | right;
+                       break;
+               case '&':
+                       ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+                       if (arg->op.op[1])
+                               *val = left && right;
+                       else
+                               *val = left & right;
+                       break;
+               case '<':
+                       ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               *val = left < right;
+                               break;
+                       case '<':
+                               *val = left << right;
+                               break;
+                       case '=':
+                               *val = left <= right;
+                               break;
+                       default:
+                               do_warning("unknown op '%s'", arg->op.op);
+                               ret = 0;
+                       }
+                       break;
+               case '>':
+                       ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               *val = left > right;
+                               break;
+                       case '>':
+                               *val = left >> right;
+                               break;
+                       case '=':
+                               *val = left >= right;
+                               break;
+                       default:
+                               do_warning("unknown op '%s'", arg->op.op);
+                               ret = 0;
+                       }
+                       break;
+               case '=':
+                       ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+
+                       if (arg->op.op[1] != '=') {
+                               do_warning("unknown op '%s'", arg->op.op);
+                               ret = 0;
+                       } else
+                               *val = left == right;
+                       break;
+               case '!':
+                       ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+
+                       switch (arg->op.op[1]) {
+                       case '=':
+                               *val = left != right;
+                               break;
+                       default:
+                               do_warning("unknown op '%s'", arg->op.op);
+                               ret = 0;
+                       }
+                       break;
+               case '-':
+                       /* check for negative */
+                       if (arg->op.left->type == PRINT_NULL)
+                               left = 0;
+                       else
+                               ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+                       *val = left - right;
+                       break;
+               case '+':
+                       if (arg->op.left->type == PRINT_NULL)
+                               left = 0;
+                       else
+                               ret = arg_num_eval(arg->op.left, &left);
+                       if (!ret)
+                               break;
+                       ret = arg_num_eval(arg->op.right, &right);
+                       if (!ret)
+                               break;
+                       *val = left + right;
+                       break;
+               default:
+                       do_warning("unknown op '%s'", arg->op.op);
+                       ret = 0;
+               }
+               break;
+
+       case PRINT_NULL:
+       case PRINT_FIELD ... PRINT_SYMBOL:
+       case PRINT_STRING:
+       case PRINT_BSTRING:
+       default:
+               do_warning("invalid eval type %d", arg->type);
+               ret = 0;
+
+       }
+       return ret;
+}
+
+static char *arg_eval (struct print_arg *arg)
+{
+       long long val;
+       static char buf[20];
+
+       switch (arg->type) {
+       case PRINT_ATOM:
+               return arg->atom.atom;
+       case PRINT_TYPE:
+               return arg_eval(arg->typecast.item);
+       case PRINT_OP:
+               if (!arg_num_eval(arg, &val))
+                       break;
+               sprintf(buf, "%lld", val);
+               return buf;
+
+       case PRINT_NULL:
+       case PRINT_FIELD ... PRINT_SYMBOL:
+       case PRINT_STRING:
+       case PRINT_BSTRING:
+       default:
+               die("invalid eval type %d", arg->type);
+               break;
+       }
+
+       return NULL;
+}
+
+static enum event_type
+process_fields(struct event_format *event, struct print_flag_sym **list, char **tok)
+{
+       enum event_type type;
+       struct print_arg *arg = NULL;
+       struct print_flag_sym *field;
+       char *token = *tok;
+       char *value;
+
+       do {
+               free_token(token);
+               type = read_token_item(&token);
+               if (test_type_token(type, token, EVENT_OP, "{"))
+                       break;
+
+               arg = alloc_arg();
+
+               free_token(token);
+               type = process_arg(event, arg, &token);
+               if (test_type_token(type, token, EVENT_DELIM, ","))
+                       goto out_free;
+
+               field = malloc_or_die(sizeof(*field));
+               memset(field, 0, sizeof(*field));
+
+               value = arg_eval(arg);
+               if (value == NULL)
+                       goto out_free;
+               field->value = strdup(value);
+
+               free_arg(arg);
+               arg = alloc_arg();
+
+               free_token(token);
+               type = process_arg(event, arg, &token);
+               if (test_type_token(type, token, EVENT_OP, "}"))
+                       goto out_free;
+
+               value = arg_eval(arg);
+               if (value == NULL)
+                       goto out_free;
+               field->str = strdup(value);
+               free_arg(arg);
+               arg = NULL;
+
+               *list = field;
+               list = &field->next;
+
+               free_token(token);
+               type = read_token_item(&token);
+       } while (type == EVENT_DELIM && strcmp(token, ",") == 0);
+
+       *tok = token;
+       return type;
+
+out_free:
+       free_arg(arg);
+       free_token(token);
+       *tok = NULL;
+
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_flags(struct event_format *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *field;
+       enum event_type type;
+       char *token;
+
+       memset(arg, 0, sizeof(*arg));
+       arg->type = PRINT_FLAGS;
+
+       field = alloc_arg();
+
+       type = process_arg(event, field, &token);
+
+       /* Handle operations in the first argument */
+       while (type == EVENT_OP)
+               type = process_op(event, field, &token);
+
+       if (test_type_token(type, token, EVENT_DELIM, ","))
+               goto out_free;
+       free_token(token);
+
+       arg->flags.field = field;
+
+       type = read_token_item(&token);
+       if (event_item_type(type)) {
+               arg->flags.delim = token;
+               type = read_token_item(&token);
+       }
+
+       if (test_type_token(type, token, EVENT_DELIM, ","))
+               goto out_free;
+
+       type = process_fields(event, &arg->flags.flags, &token);
+       if (test_type_token(type, token, EVENT_DELIM, ")"))
+               goto out_free;
+
+       free_token(token);
+       type = read_token_item(tok);
+       return type;
+
+ out_free:
+       free_token(token);
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *field;
+       enum event_type type;
+       char *token;
+
+       memset(arg, 0, sizeof(*arg));
+       arg->type = PRINT_SYMBOL;
+
+       field = alloc_arg();
+
+       type = process_arg(event, field, &token);
+       if (test_type_token(type, token, EVENT_DELIM, ","))
+               goto out_free;
+
+       arg->symbol.field = field;
+
+       type = process_fields(event, &arg->symbol.symbols, &token);
+       if (test_type_token(type, token, EVENT_DELIM, ")"))
+               goto out_free;
+
+       free_token(token);
+       type = read_token_item(tok);
+       return type;
+
+ out_free:
+       free_token(token);
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok)
+{
+       struct format_field *field;
+       enum event_type type;
+       char *token;
+
+       memset(arg, 0, sizeof(*arg));
+       arg->type = PRINT_DYNAMIC_ARRAY;
+
+       /*
+        * The item within the parenthesis is another field that holds
+        * the index into where the array starts.
+        */
+       type = read_token(&token);
+       *tok = token;
+       if (type != EVENT_ITEM)
+               goto out_free;
+
+       /* Find the field */
+
+       field = pevent_find_field(event, token);
+       if (!field)
+               goto out_free;
+
+       arg->dynarray.field = field;
+       arg->dynarray.index = 0;
+
+       if (read_expected(EVENT_DELIM, ")") < 0)
+               goto out_free;
+
+       free_token(token);
+       type = read_token_item(&token);
+       *tok = token;
+       if (type != EVENT_OP || strcmp(token, "[") != 0)
+               return type;
+
+       free_token(token);
+       arg = alloc_arg();
+       type = process_arg(event, arg, &token);
+       if (type == EVENT_ERROR)
+               goto out_free;
+
+       if (!test_type_token(type, token, EVENT_OP, "]"))
+               goto out_free;
+
+       free_token(token);
+       type = read_token_item(tok);
+       return type;
+
+ out_free:
+       free(arg);
+       free_token(token);
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_paren(struct event_format *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *item_arg;
+       enum event_type type;
+       char *token;
+
+       type = process_arg(event, arg, &token);
+
+       if (type == EVENT_ERROR)
+               goto out_free;
+
+       if (type == EVENT_OP)
+               type = process_op(event, arg, &token);
+
+       if (type == EVENT_ERROR)
+               goto out_free;
+
+       if (test_type_token(type, token, EVENT_DELIM, ")"))
+               goto out_free;
+
+       free_token(token);
+       type = read_token_item(&token);
+
+       /*
+        * If the next token is an item or another open paren, then
+        * this was a typecast.
+        */
+       if (event_item_type(type) ||
+           (type == EVENT_DELIM && strcmp(token, "(") == 0)) {
+
+               /* make this a typecast and contine */
+
+               /* prevous must be an atom */
+               if (arg->type != PRINT_ATOM)
+                       die("previous needed to be PRINT_ATOM");
+
+               item_arg = alloc_arg();
+
+               arg->type = PRINT_TYPE;
+               arg->typecast.type = arg->atom.atom;
+               arg->typecast.item = item_arg;
+               type = process_arg_token(event, item_arg, &token, type);
+
+       }
+
+       *tok = token;
+       return type;
+
+ out_free:
+       free_token(token);
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
+
+static enum event_type
+process_str(struct event_format *event __unused, struct print_arg *arg, char **tok)
+{
+       enum event_type type;
+       char *token;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto out_free;
+
+       arg->type = PRINT_STRING;
+       arg->string.string = token;
+       arg->string.offset = -1;
+
+       if (read_expected(EVENT_DELIM, ")") < 0)
+               goto out_err;
+
+       type = read_token(&token);
+       *tok = token;
+
+       return type;
+
+ out_free:
+       free_token(token);
+ out_err:
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
+static struct pevent_function_handler *
+find_func_handler(struct pevent *pevent, char *func_name)
+{
+       struct pevent_function_handler *func;
+
+       for (func = pevent->func_handlers; func; func = func->next) {
+               if (strcmp(func->name, func_name) == 0)
+                       break;
+       }
+
+       return func;
+}
+
+static void remove_func_handler(struct pevent *pevent, char *func_name)
+{
+       struct pevent_function_handler *func;
+       struct pevent_function_handler **next;
+
+       next = &pevent->func_handlers;
+       while ((func = *next)) {
+               if (strcmp(func->name, func_name) == 0) {
+                       *next = func->next;
+                       free_func_handle(func);
+                       break;
+               }
+               next = &func->next;
+       }
+}
+
+static enum event_type
+process_func_handler(struct event_format *event, struct pevent_function_handler *func,
+                    struct print_arg *arg, char **tok)
+{
+       struct print_arg **next_arg;
+       struct print_arg *farg;
+       enum event_type type;
+       char *token;
+       char *test;
+       int i;
+
+       arg->type = PRINT_FUNC;
+       arg->func.func = func;
+
+       *tok = NULL;
+
+       next_arg = &(arg->func.args);
+       for (i = 0; i < func->nr_args; i++) {
+               farg = alloc_arg();
+               type = process_arg(event, farg, &token);
+               if (i < (func->nr_args - 1))
+                       test = ",";
+               else
+                       test = ")";
+
+               if (test_type_token(type, token, EVENT_DELIM, test)) {
+                       free_arg(farg);
+                       free_token(token);
+                       return EVENT_ERROR;
+               }
+
+               *next_arg = farg;
+               next_arg = &(farg->next);
+               free_token(token);
+       }
+
+       type = read_token(&token);
+       *tok = token;
+
+       return type;
+}
+
+static enum event_type
+process_function(struct event_format *event, struct print_arg *arg,
+                char *token, char **tok)
+{
+       struct pevent_function_handler *func;
+
+       if (strcmp(token, "__print_flags") == 0) {
+               free_token(token);
+               is_flag_field = 1;
+               return process_flags(event, arg, tok);
+       }
+       if (strcmp(token, "__print_symbolic") == 0) {
+               free_token(token);
+               is_symbolic_field = 1;
+               return process_symbols(event, arg, tok);
+       }
+       if (strcmp(token, "__get_str") == 0) {
+               free_token(token);
+               return process_str(event, arg, tok);
+       }
+       if (strcmp(token, "__get_dynamic_array") == 0) {
+               free_token(token);
+               return process_dynamic_array(event, arg, tok);
+       }
+
+       func = find_func_handler(event->pevent, token);
+       if (func) {
+               free_token(token);
+               return process_func_handler(event, func, arg, tok);
+       }
+
+       do_warning("function %s not defined", token);
+       free_token(token);
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_arg_token(struct event_format *event, struct print_arg *arg,
+                 char **tok, enum event_type type)
+{
+       char *token;
+       char *atom;
+
+       token = *tok;
+
+       switch (type) {
+       case EVENT_ITEM:
+               if (strcmp(token, "REC") == 0) {
+                       free_token(token);
+                       type = process_entry(event, arg, &token);
+                       break;
+               }
+               atom = token;
+               /* test the next token */
+               type = read_token_item(&token);
+
+               /*
+                * If the next token is a parenthesis, then this
+                * is a function.
+                */
+               if (type == EVENT_DELIM && strcmp(token, "(") == 0) {
+                       free_token(token);
+                       token = NULL;
+                       /* this will free atom. */
+                       type = process_function(event, arg, atom, &token);
+                       break;
+               }
+               /* atoms can be more than one token long */
+               while (type == EVENT_ITEM) {
+                       atom = realloc(atom, strlen(atom) + strlen(token) + 2);
+                       strcat(atom, " ");
+                       strcat(atom, token);
+                       free_token(token);
+                       type = read_token_item(&token);
+               }
+
+               arg->type = PRINT_ATOM;
+               arg->atom.atom = atom;
+               break;
+
+       case EVENT_DQUOTE:
+       case EVENT_SQUOTE:
+               arg->type = PRINT_ATOM;
+               arg->atom.atom = token;
+               type = read_token_item(&token);
+               break;
+       case EVENT_DELIM:
+               if (strcmp(token, "(") == 0) {
+                       free_token(token);
+                       type = process_paren(event, arg, &token);
+                       break;
+               }
+       case EVENT_OP:
+               /* handle single ops */
+               arg->type = PRINT_OP;
+               arg->op.op = token;
+               arg->op.left = NULL;
+               type = process_op(event, arg, &token);
+
+               /* On error, the op is freed */
+               if (type == EVENT_ERROR)
+                       arg->op.op = NULL;
+
+               /* return error type if errored */
+               break;
+
+       case EVENT_ERROR ... EVENT_NEWLINE:
+       default:
+               die("unexpected type %d", type);
+       }
+       *tok = token;
+
+       return type;
+}
+
+static int event_read_print_args(struct event_format *event, struct print_arg **list)
+{
+       enum event_type type = EVENT_ERROR;
+       struct print_arg *arg;
+       char *token;
+       int args = 0;
+
+       do {
+               if (type == EVENT_NEWLINE) {
+                       type = read_token_item(&token);
+                       continue;
+               }
+
+               arg = alloc_arg();
+
+               type = process_arg(event, arg, &token);
+
+               if (type == EVENT_ERROR) {
+                       free_token(token);
+                       free_arg(arg);
+                       return -1;
+               }
+
+               *list = arg;
+               args++;
+
+               if (type == EVENT_OP) {
+                       type = process_op(event, arg, &token);
+                       free_token(token);
+                       if (type == EVENT_ERROR) {
+                               *list = NULL;
+                               free_arg(arg);
+                               return -1;
+                       }
+                       list = &arg->next;
+                       continue;
+               }
+
+               if (type == EVENT_DELIM && strcmp(token, ",") == 0) {
+                       free_token(token);
+                       *list = arg;
+                       list = &arg->next;
+                       continue;
+               }
+               break;
+       } while (type != EVENT_NONE);
+
+       if (type != EVENT_NONE && type != EVENT_ERROR)
+               free_token(token);
+
+       return args;
+}
+
+static int event_read_print(struct event_format *event)
+{
+       enum event_type type;
+       char *token;
+       int ret;
+
+       if (read_expected_item(EVENT_ITEM, "print") < 0)
+               return -1;
+
+       if (read_expected(EVENT_ITEM, "fmt") < 0)
+               return -1;
+
+       if (read_expected(EVENT_OP, ":") < 0)
+               return -1;
+
+       if (read_expect_type(EVENT_DQUOTE, &token) < 0)
+               goto fail;
+
+ concat:
+       event->print_fmt.format = token;
+       event->print_fmt.args = NULL;
+
+       /* ok to have no arg */
+       type = read_token_item(&token);
+
+       if (type == EVENT_NONE)
+               return 0;
+
+       /* Handle concatenation of print lines */
+       if (type == EVENT_DQUOTE) {
+               char *cat;
+
+               cat = malloc_or_die(strlen(event->print_fmt.format) +
+                                   strlen(token) + 1);
+               strcpy(cat, event->print_fmt.format);
+               strcat(cat, token);
+               free_token(token);
+               free_token(event->print_fmt.format);
+               event->print_fmt.format = NULL;
+               token = cat;
+               goto concat;
+       }
+                            
+       if (test_type_token(type, token, EVENT_DELIM, ","))
+               goto fail;
+
+       free_token(token);
+
+       ret = event_read_print_args(event, &event->print_fmt.args);
+       if (ret < 0)
+               return -1;
+
+       return ret;
+
+ fail:
+       free_token(token);
+       return -1;
+}
+
+/**
+ * pevent_find_common_field - return a common field by event
+ * @event: handle for the event
+ * @name: the name of the common field to return
+ *
+ * Returns a common field from the event by the given @name.
+ * This only searchs the common fields and not all field.
+ */
+struct format_field *
+pevent_find_common_field(struct event_format *event, const char *name)
+{
+       struct format_field *format;
+
+       for (format = event->format.common_fields;
+            format; format = format->next) {
+               if (strcmp(format->name, name) == 0)
+                       break;
+       }
+
+       return format;
+}
+
+/**
+ * pevent_find_field - find a non-common field
+ * @event: handle for the event
+ * @name: the name of the non-common field
+ *
+ * Returns a non-common field by the given @name.
+ * This does not search common fields.
+ */
+struct format_field *
+pevent_find_field(struct event_format *event, const char *name)
+{
+       struct format_field *format;
+
+       for (format = event->format.fields;
+            format; format = format->next) {
+               if (strcmp(format->name, name) == 0)
+                       break;
+       }
+
+       return format;
+}
+
+/**
+ * pevent_find_any_field - find any field by name
+ * @event: handle for the event
+ * @name: the name of the field
+ *
+ * Returns a field by the given @name.
+ * This searchs the common field names first, then
+ * the non-common ones if a common one was not found.
+ */
+struct format_field *
+pevent_find_any_field(struct event_format *event, const char *name)
+{
+       struct format_field *format;
+
+       format = pevent_find_common_field(event, name);
+       if (format)
+               return format;
+       return pevent_find_field(event, name);
+}
+
+/**
+ * pevent_read_number - read a number from data
+ * @pevent: handle for the pevent
+ * @ptr: the raw data
+ * @size: the size of the data that holds the number
+ *
+ * Returns the number (converted to host) from the
+ * raw data.
+ */
+unsigned long long pevent_read_number(struct pevent *pevent,
+                                     const void *ptr, int size)
+{
+       switch (size) {
+       case 1:
+               return *(unsigned char *)ptr;
+       case 2:
+               return data2host2(pevent, ptr);
+       case 4:
+               return data2host4(pevent, ptr);
+       case 8:
+               return data2host8(pevent, ptr);
+       default:
+               /* BUG! */
+               return 0;
+       }
+}
+
+/**
+ * pevent_read_number_field - read a number from data
+ * @field: a handle to the field
+ * @data: the raw data to read
+ * @value: the value to place the number in
+ *
+ * Reads raw data according to a field offset and size,
+ * and translates it into @value.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int pevent_read_number_field(struct format_field *field, const void *data,
+                            unsigned long long *value)
+{
+       if (!field)
+               return -1;
+       switch (field->size) {
+       case 1:
+       case 2:
+       case 4:
+       case 8:
+               *value = pevent_read_number(field->event->pevent,
+                                           data + field->offset, field->size);
+               return 0;
+       default:
+               return -1;
+       }
+}
+
+static int get_common_info(struct pevent *pevent,
+                          const char *type, int *offset, int *size)
+{
+       struct event_format *event;
+       struct format_field *field;
+
+       /*
+        * All events should have the same common elements.
+        * Pick any event to find where the type is;
+        */
+       if (!pevent->events)
+               die("no event_list!");
+
+       event = pevent->events[0];
+       field = pevent_find_common_field(event, type);
+       if (!field)
+               die("field '%s' not found", type);
+
+       *offset = field->offset;
+       *size = field->size;
+
+       return 0;
+}
+
+static int __parse_common(struct pevent *pevent, void *data,
+                         int *size, int *offset, const char *name)
+{
+       int ret;
+
+       if (!*size) {
+               ret = get_common_info(pevent, name, offset, size);
+               if (ret < 0)
+                       return ret;
+       }
+       return pevent_read_number(pevent, data + *offset, *size);
+}
+
+static int trace_parse_common_type(struct pevent *pevent, void *data)
+{
+       return __parse_common(pevent, data,
+                             &pevent->type_size, &pevent->type_offset,
+                             "common_type");
+}
+
+static int parse_common_pid(struct pevent *pevent, void *data)
+{
+       return __parse_common(pevent, data,
+                             &pevent->pid_size, &pevent->pid_offset,
+                             "common_pid");
+}
+
+static int parse_common_pc(struct pevent *pevent, void *data)
+{
+       return __parse_common(pevent, data,
+                             &pevent->pc_size, &pevent->pc_offset,
+                             "common_preempt_count");
+}
+
+static int parse_common_flags(struct pevent *pevent, void *data)
+{
+       return __parse_common(pevent, data,
+                             &pevent->flags_size, &pevent->flags_offset,
+                             "common_flags");
+}
+
+static int parse_common_lock_depth(struct pevent *pevent, void *data)
+{
+       int ret;
+
+       ret = __parse_common(pevent, data,
+                            &pevent->ld_size, &pevent->ld_offset,
+                            "common_lock_depth");
+       if (ret < 0)
+               return -1;
+
+       return ret;
+}
+
+static int events_id_cmp(const void *a, const void *b);
+
+/**
+ * pevent_find_event - find an event by given id
+ * @pevent: a handle to the pevent
+ * @id: the id of the event
+ *
+ * Returns an event that has a given @id.
+ */
+struct event_format *pevent_find_event(struct pevent *pevent, int id)
+{
+       struct event_format **eventptr;
+       struct event_format key;
+       struct event_format *pkey = &key;
+
+       /* Check cache first */
+       if (pevent->last_event && pevent->last_event->id == id)
+               return pevent->last_event;
+
+       key.id = id;
+
+       eventptr = bsearch(&pkey, pevent->events, pevent->nr_events,
+                          sizeof(*pevent->events), events_id_cmp);
+
+       if (eventptr) {
+               pevent->last_event = *eventptr;
+               return *eventptr;
+       }
+
+       return NULL;
+}
+
+/**
+ * pevent_find_event_by_name - find an event by given name
+ * @pevent: a handle to the pevent
+ * @sys: the system name to search for
+ * @name: the name of the event to search for
+ *
+ * This returns an event with a given @name and under the system
+ * @sys. If @sys is NULL the first event with @name is returned.
+ */
+struct event_format *
+pevent_find_event_by_name(struct pevent *pevent,
+                         const char *sys, const char *name)
+{
+       struct event_format *event;
+       int i;
+
+       if (pevent->last_event &&
+           strcmp(pevent->last_event->name, name) == 0 &&
+           (!sys || strcmp(pevent->last_event->system, sys) == 0))
+               return pevent->last_event;
+
+       for (i = 0; i < pevent->nr_events; i++) {
+               event = pevent->events[i];
+               if (strcmp(event->name, name) == 0) {
+                       if (!sys)
+                               break;
+                       if (strcmp(event->system, sys) == 0)
+                               break;
+               }
+       }
+       if (i == pevent->nr_events)
+               event = NULL;
+
+       pevent->last_event = event;
+       return event;
+}
+
+static unsigned long long
+eval_num_arg(void *data, int size, struct event_format *event, struct print_arg *arg)
+{
+       struct pevent *pevent = event->pevent;
+       unsigned long long val = 0;
+       unsigned long long left, right;
+       struct print_arg *typearg = NULL;
+       struct print_arg *larg;
+       unsigned long offset;
+       unsigned int field_size;
+
+       switch (arg->type) {
+       case PRINT_NULL:
+               /* ?? */
+               return 0;
+       case PRINT_ATOM:
+               return strtoull(arg->atom.atom, NULL, 0);
+       case PRINT_FIELD:
+               if (!arg->field.field) {
+                       arg->field.field = pevent_find_any_field(event, arg->field.name);
+                       if (!arg->field.field)
+                               die("field %s not found", arg->field.name);
+               }
+               /* must be a number */
+               val = pevent_read_number(pevent, data + arg->field.field->offset,
+                               arg->field.field->size);
+               break;
+       case PRINT_FLAGS:
+       case PRINT_SYMBOL:
+               break;
+       case PRINT_TYPE:
+               val = eval_num_arg(data, size, event, arg->typecast.item);
+               return eval_type(val, arg, 0);
+       case PRINT_STRING:
+       case PRINT_BSTRING:
+               return 0;
+       case PRINT_FUNC: {
+               struct trace_seq s;
+               trace_seq_init(&s);
+               val = process_defined_func(&s, data, size, event, arg);
+               trace_seq_destroy(&s);
+               return val;
+       }
+       case PRINT_OP:
+               if (strcmp(arg->op.op, "[") == 0) {
+                       /*
+                        * Arrays are special, since we don't want
+                        * to read the arg as is.
+                        */
+                       right = eval_num_arg(data, size, event, arg->op.right);
+
+                       /* handle typecasts */
+                       larg = arg->op.left;
+                       while (larg->type == PRINT_TYPE) {
+                               if (!typearg)
+                                       typearg = larg;
+                               larg = larg->typecast.item;
+                       }
+
+                       /* Default to long size */
+                       field_size = pevent->long_size;
+
+                       switch (larg->type) {
+                       case PRINT_DYNAMIC_ARRAY:
+                               offset = pevent_read_number(pevent,
+                                                  data + larg->dynarray.field->offset,
+                                                  larg->dynarray.field->size);
+                               if (larg->dynarray.field->elementsize)
+                                       field_size = larg->dynarray.field->elementsize;
+                               /*
+                                * The actual length of the dynamic array is stored
+                                * in the top half of the field, and the offset
+                                * is in the bottom half of the 32 bit field.
+                                */
+                               offset &= 0xffff;
+                               offset += right;
+                               break;
+                       case PRINT_FIELD:
+                               if (!larg->field.field) {
+                                       larg->field.field =
+                                               pevent_find_any_field(event, larg->field.name);
+                                       if (!larg->field.field)
+                                               die("field %s not found", larg->field.name);
+                               }
+                               field_size = larg->field.field->elementsize;
+                               offset = larg->field.field->offset +
+                                       right * larg->field.field->elementsize;
+                               break;
+                       default:
+                               goto default_op; /* oops, all bets off */
+                       }
+                       val = pevent_read_number(pevent,
+                                                data + offset, field_size);
+                       if (typearg)
+                               val = eval_type(val, typearg, 1);
+                       break;
+               } else if (strcmp(arg->op.op, "?") == 0) {
+                       left = eval_num_arg(data, size, event, arg->op.left);
+                       arg = arg->op.right;
+                       if (left)
+                               val = eval_num_arg(data, size, event, arg->op.left);
+                       else
+                               val = eval_num_arg(data, size, event, arg->op.right);
+                       break;
+               }
+ default_op:
+               left = eval_num_arg(data, size, event, arg->op.left);
+               right = eval_num_arg(data, size, event, arg->op.right);
+               switch (arg->op.op[0]) {
+               case '!':
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               val = !right;
+                               break;
+                       case '=':
+                               val = left != right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               case '~':
+                       val = ~right;
+                       break;
+               case '|':
+                       if (arg->op.op[1])
+                               val = left || right;
+                       else
+                               val = left | right;
+                       break;
+               case '&':
+                       if (arg->op.op[1])
+                               val = left && right;
+                       else
+                               val = left & right;
+                       break;
+               case '<':
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               val = left < right;
+                               break;
+                       case '<':
+                               val = left << right;
+                               break;
+                       case '=':
+                               val = left <= right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               case '>':
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               val = left > right;
+                               break;
+                       case '>':
+                               val = left >> right;
+                               break;
+                       case '=':
+                               val = left >= right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               case '=':
+                       if (arg->op.op[1] != '=')
+                               die("unknown op '%s'", arg->op.op);
+                       val = left == right;
+                       break;
+               case '-':
+                       val = left - right;
+                       break;
+               case '+':
+                       val = left + right;
+                       break;
+               case '/':
+                       val = left / right;
+                       break;
+               case '*':
+                       val = left * right;
+                       break;
+               default:
+                       die("unknown op '%s'", arg->op.op);
+               }
+               break;
+       default: /* not sure what to do there */
+               return 0;
+       }
+       return val;
+}
+
+struct flag {
+       const char *name;
+       unsigned long long value;
+};
+
+static const struct flag flags[] = {
+       { "HI_SOFTIRQ", 0 },
+       { "TIMER_SOFTIRQ", 1 },
+       { "NET_TX_SOFTIRQ", 2 },
+       { "NET_RX_SOFTIRQ", 3 },
+       { "BLOCK_SOFTIRQ", 4 },
+       { "BLOCK_IOPOLL_SOFTIRQ", 5 },
+       { "TASKLET_SOFTIRQ", 6 },
+       { "SCHED_SOFTIRQ", 7 },
+       { "HRTIMER_SOFTIRQ", 8 },
+       { "RCU_SOFTIRQ", 9 },
+
+       { "HRTIMER_NORESTART", 0 },
+       { "HRTIMER_RESTART", 1 },
+};
+
+static unsigned long long eval_flag(const char *flag)
+{
+       int i;
+
+       /*
+        * Some flags in the format files do not get converted.
+        * If the flag is not numeric, see if it is something that
+        * we already know about.
+        */
+       if (isdigit(flag[0]))
+               return strtoull(flag, NULL, 0);
+
+       for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++)
+               if (strcmp(flags[i].name, flag) == 0)
+                       return flags[i].value;
+
+       return 0;
+}
+
+static void print_str_to_seq(struct trace_seq *s, const char *format,
+                            int len_arg, const char *str)
+{
+       if (len_arg >= 0)
+               trace_seq_printf(s, format, len_arg, str);
+       else
+               trace_seq_printf(s, format, str);
+}
+
+static void print_str_arg(struct trace_seq *s, void *data, int size,
+                         struct event_format *event, const char *format,
+                         int len_arg, struct print_arg *arg)
+{
+       struct pevent *pevent = event->pevent;
+       struct print_flag_sym *flag;
+       unsigned long long val, fval;
+       unsigned long addr;
+       char *str;
+       int print;
+       int len;
+
+       switch (arg->type) {
+       case PRINT_NULL:
+               /* ?? */
+               return;
+       case PRINT_ATOM:
+               print_str_to_seq(s, format, len_arg, arg->atom.atom);
+               return;
+       case PRINT_FIELD:
+               if (!arg->field.field) {
+                       arg->field.field = pevent_find_any_field(event, arg->field.name);
+                       if (!arg->field.field)
+                               die("field %s not found", arg->field.name);
+               }
+               /* Zero sized fields, mean the rest of the data */
+               len = arg->field.field->size ? : size - arg->field.field->offset;
+
+               /*
+                * Some events pass in pointers. If this is not an array
+                * and the size is the same as long_size, assume that it
+                * is a pointer.
+                */
+               if (!(arg->field.field->flags & FIELD_IS_ARRAY) &&
+                   arg->field.field->size == pevent->long_size) {
+                       addr = *(unsigned long *)(data + arg->field.field->offset);
+                       trace_seq_printf(s, "%lx", addr);
+                       break;
+               }
+               str = malloc_or_die(len + 1);
+               memcpy(str, data + arg->field.field->offset, len);
+               str[len] = 0;
+               print_str_to_seq(s, format, len_arg, str);
+               free(str);
+               break;
+       case PRINT_FLAGS:
+               val = eval_num_arg(data, size, event, arg->flags.field);
+               print = 0;
+               for (flag = arg->flags.flags; flag; flag = flag->next) {
+                       fval = eval_flag(flag->value);
+                       if (!val && !fval) {
+                               print_str_to_seq(s, format, len_arg, flag->str);
+                               break;
+                       }
+                       if (fval && (val & fval) == fval) {
+                               if (print && arg->flags.delim)
+                                       trace_seq_puts(s, arg->flags.delim);
+                               print_str_to_seq(s, format, len_arg, flag->str);
+                               print = 1;
+                               val &= ~fval;
+                       }
+               }
+               break;
+       case PRINT_SYMBOL:
+               val = eval_num_arg(data, size, event, arg->symbol.field);
+               for (flag = arg->symbol.symbols; flag; flag = flag->next) {
+                       fval = eval_flag(flag->value);
+                       if (val == fval) {
+                               print_str_to_seq(s, format, len_arg, flag->str);
+                               break;
+                       }
+               }
+               break;
+
+       case PRINT_TYPE:
+               break;
+       case PRINT_STRING: {
+               int str_offset;
+
+               if (arg->string.offset == -1) {
+                       struct format_field *f;
+
+                       f = pevent_find_any_field(event, arg->string.string);
+                       arg->string.offset = f->offset;
+               }
+               str_offset = data2host4(pevent, data + arg->string.offset);
+               str_offset &= 0xffff;
+               print_str_to_seq(s, format, len_arg, ((char *)data) + str_offset);
+               break;
+       }
+       case PRINT_BSTRING:
+               trace_seq_printf(s, format, arg->string.string);
+               break;
+       case PRINT_OP:
+               /*
+                * The only op for string should be ? :
+                */
+               if (arg->op.op[0] != '?')
+                       return;
+               val = eval_num_arg(data, size, event, arg->op.left);
+               if (val)
+                       print_str_arg(s, data, size, event,
+                                     format, len_arg, arg->op.right->op.left);
+               else
+                       print_str_arg(s, data, size, event,
+                                     format, len_arg, arg->op.right->op.right);
+               break;
+       case PRINT_FUNC:
+               process_defined_func(s, data, size, event, arg);
+               break;
+       default:
+               /* well... */
+               break;
+       }
+}
+
+static unsigned long long
+process_defined_func(struct trace_seq *s, void *data, int size,
+                    struct event_format *event, struct print_arg *arg)
+{
+       struct pevent_function_handler *func_handle = arg->func.func;
+       struct pevent_func_params *param;
+       unsigned long long *args;
+       unsigned long long ret;
+       struct print_arg *farg;
+       struct trace_seq str;
+       struct save_str {
+               struct save_str *next;
+               char *str;
+       } *strings = NULL, *string;
+       int i;
+
+       if (!func_handle->nr_args) {
+               ret = (*func_handle->func)(s, NULL);
+               goto out;
+       }
+
+       farg = arg->func.args;
+       param = func_handle->params;
+
+       args = malloc_or_die(sizeof(*args) * func_handle->nr_args);
+       for (i = 0; i < func_handle->nr_args; i++) {
+               switch (param->type) {
+               case PEVENT_FUNC_ARG_INT:
+               case PEVENT_FUNC_ARG_LONG:
+               case PEVENT_FUNC_ARG_PTR:
+                       args[i] = eval_num_arg(data, size, event, farg);
+                       break;
+               case PEVENT_FUNC_ARG_STRING:
+                       trace_seq_init(&str);
+                       print_str_arg(&str, data, size, event, "%s", -1, farg);
+                       trace_seq_terminate(&str);
+                       string = malloc_or_die(sizeof(*string));
+                       string->next = strings;
+                       string->str = strdup(str.buffer);
+                       strings = string;
+                       trace_seq_destroy(&str);
+                       break;
+               default:
+                       /*
+                        * Something went totally wrong, this is not
+                        * an input error, something in this code broke.
+                        */
+                       die("Unexpected end of arguments\n");
+                       break;
+               }
+               farg = farg->next;
+       }
+
+       ret = (*func_handle->func)(s, args);
+       free(args);
+       while (strings) {
+               string = strings;
+               strings = string->next;
+               free(string->str);
+               free(string);
+       }
+
+ out:
+       /* TBD : handle return type here */
+       return ret;
+}
+
+static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event)
+{
+       struct pevent *pevent = event->pevent;
+       struct format_field *field, *ip_field;
+       struct print_arg *args, *arg, **next;
+       unsigned long long ip, val;
+       char *ptr;
+       void *bptr;
+
+       field = pevent->bprint_buf_field;
+       ip_field = pevent->bprint_ip_field;
+
+       if (!field) {
+               field = pevent_find_field(event, "buf");
+               if (!field)
+                       die("can't find buffer field for binary printk");
+               ip_field = pevent_find_field(event, "ip");
+               if (!ip_field)
+                       die("can't find ip field for binary printk");
+               pevent->bprint_buf_field = field;
+               pevent->bprint_ip_field = ip_field;
+       }
+
+       ip = pevent_read_number(pevent, data + ip_field->offset, ip_field->size);
+
+       /*
+        * The first arg is the IP pointer.
+        */
+       args = alloc_arg();
+       arg = args;
+       arg->next = NULL;
+       next = &arg->next;
+
+       arg->type = PRINT_ATOM;
+       arg->atom.atom = malloc_or_die(32);
+       sprintf(arg->atom.atom, "%lld", ip);
+
+       /* skip the first "%pf : " */
+       for (ptr = fmt + 6, bptr = data + field->offset;
+            bptr < data + size && *ptr; ptr++) {
+               int ls = 0;
+
+               if (*ptr == '%') {
+ process_again:
+                       ptr++;
+                       switch (*ptr) {
+                       case '%':
+                               break;
+                       case 'l':
+                               ls++;
+                               goto process_again;
+                       case 'L':
+                               ls = 2;
+                               goto process_again;
+                       case '0' ... '9':
+                               goto process_again;
+                       case 'p':
+                               ls = 1;
+                               /* fall through */
+                       case 'd':
+                       case 'u':
+                       case 'x':
+                       case 'i':
+                               /* the pointers are always 4 bytes aligned */
+                               bptr = (void *)(((unsigned long)bptr + 3) &
+                                               ~3);
+                               switch (ls) {
+                               case 0:
+                                       ls = 4;
+                                       break;
+                               case 1:
+                                       ls = pevent->long_size;
+                                       break;
+                               case 2:
+                                       ls = 8;
+                               default:
+                                       break;
+                               }
+                               val = pevent_read_number(pevent, bptr, ls);
+                               bptr += ls;
+                               arg = alloc_arg();
+                               arg->next = NULL;
+                               arg->type = PRINT_ATOM;
+                               arg->atom.atom = malloc_or_die(32);
+                               sprintf(arg->atom.atom, "%lld", val);
+                               *next = arg;
+                               next = &arg->next;
+                               break;
+                       case 's':
+                               arg = alloc_arg();
+                               arg->next = NULL;
+                               arg->type = PRINT_BSTRING;
+                               arg->string.string = strdup(bptr);
+                               bptr += strlen(bptr) + 1;
+                               *next = arg;
+                               next = &arg->next;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       return args;
+}
+
+static void free_args(struct print_arg *args)
+{
+       struct print_arg *next;
+
+       while (args) {
+               next = args->next;
+
+               free_arg(args);
+               args = next;
+       }
+}
+
+static char *
+get_bprint_format(void *data, int size __unused, struct event_format *event)
+{
+       struct pevent *pevent = event->pevent;
+       unsigned long long addr;
+       struct format_field *field;
+       struct printk_map *printk;
+       char *format;
+       char *p;
+
+       field = pevent->bprint_fmt_field;
+
+       if (!field) {
+               field = pevent_find_field(event, "fmt");
+               if (!field)
+                       die("can't find format field for binary printk");
+               pevent->bprint_fmt_field = field;
+       }
+
+       addr = pevent_read_number(pevent, data + field->offset, field->size);
+
+       printk = find_printk(pevent, addr);
+       if (!printk) {
+               format = malloc_or_die(45);
+               sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n",
+                       addr);
+               return format;
+       }
+
+       p = printk->printk;
+       /* Remove any quotes. */
+       if (*p == '"')
+               p++;
+       format = malloc_or_die(strlen(p) + 10);
+       sprintf(format, "%s : %s", "%pf", p);
+       /* remove ending quotes and new line since we will add one too */
+       p = format + strlen(format) - 1;
+       if (*p == '"')
+               *p = 0;
+
+       p -= 2;
+       if (strcmp(p, "\\n") == 0)
+               *p = 0;
+
+       return format;
+}
+
+static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
+                         struct event_format *event, struct print_arg *arg)
+{
+       unsigned char *buf;
+       char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x";
+
+       if (arg->type == PRINT_FUNC) {
+               process_defined_func(s, data, size, event, arg);
+               return;
+       }
+
+       if (arg->type != PRINT_FIELD) {
+               trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d",
+                                arg->type);
+               return;
+       }
+
+       if (mac == 'm')
+               fmt = "%.2x%.2x%.2x%.2x%.2x%.2x";
+       if (!arg->field.field) {
+               arg->field.field =
+                       pevent_find_any_field(event, arg->field.name);
+               if (!arg->field.field)
+                       die("field %s not found", arg->field.name);
+       }
+       if (arg->field.field->size != 6) {
+               trace_seq_printf(s, "INVALIDMAC");
+               return;
+       }
+       buf = data + arg->field.field->offset;
+       trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+}
+
+static void print_event_fields(struct trace_seq *s, void *data, int size,
+                              struct event_format *event)
+{
+       struct format_field *field;
+       unsigned long long val;
+       unsigned int offset, len, i;
+
+       field = event->format.fields;
+       while (field) {
+               trace_seq_printf(s, " %s=", field->name);
+               if (field->flags & FIELD_IS_ARRAY) {
+                       offset = field->offset;
+                       len = field->size;
+                       if (field->flags & FIELD_IS_DYNAMIC) {
+                               val = pevent_read_number(event->pevent, data + offset, len);
+                               offset = val;
+                               len = offset >> 16;
+                               offset &= 0xffff;
+                       }
+                       if (field->flags & FIELD_IS_STRING) {
+                               trace_seq_printf(s, "%s", (char *)data + offset);
+                       } else {
+                               trace_seq_puts(s, "ARRAY[");
+                               for (i = 0; i < len; i++) {
+                                       if (i)
+                                               trace_seq_puts(s, ", ");
+                                       trace_seq_printf(s, "%02x",
+                                                        *((unsigned char *)data + offset + i));
+                               }
+                               trace_seq_putc(s, ']');
+                       }
+               } else {
+                       val = pevent_read_number(event->pevent, data + field->offset,
+                                                field->size);
+                       if (field->flags & FIELD_IS_POINTER) {
+                               trace_seq_printf(s, "0x%llx", val);
+                       } else if (field->flags & FIELD_IS_SIGNED) {
+                               switch (field->size) {
+                               case 4:
+                                       /*
+                                        * If field is long then print it in hex.
+                                        * A long usually stores pointers.
+                                        */
+                                       if (field->flags & FIELD_IS_LONG)
+                                               trace_seq_printf(s, "0x%x", (int)val);
+                                       else
+                                               trace_seq_printf(s, "%d", (int)val);
+                                       break;
+                               case 2:
+                                       trace_seq_printf(s, "%2d", (short)val);
+                                       break;
+                               case 1:
+                                       trace_seq_printf(s, "%1d", (char)val);
+                                       break;
+                               default:
+                                       trace_seq_printf(s, "%lld", val);
+                               }
+                       } else {
+                               if (field->flags & FIELD_IS_LONG)
+                                       trace_seq_printf(s, "0x%llx", val);
+                               else
+                                       trace_seq_printf(s, "%llu", val);
+                       }
+               }
+               field = field->next;
+       }
+}
+
+static void pretty_print(struct trace_seq *s, void *data, int size, struct event_format *event)
+{
+       struct pevent *pevent = event->pevent;
+       struct print_fmt *print_fmt = &event->print_fmt;
+       struct print_arg *arg = print_fmt->args;
+       struct print_arg *args = NULL;
+       const char *ptr = print_fmt->format;
+       unsigned long long val;
+       struct func_map *func;
+       const char *saveptr;
+       char *bprint_fmt = NULL;
+       char format[32];
+       int show_func;
+       int len_as_arg;
+       int len_arg;
+       int len;
+       int ls;
+
+       if (event->flags & EVENT_FL_FAILED) {
+               trace_seq_printf(s, "[FAILED TO PARSE]");
+               print_event_fields(s, data, size, event);
+               return;
+       }
+
+       if (event->flags & EVENT_FL_ISBPRINT) {
+               bprint_fmt = get_bprint_format(data, size, event);
+               args = make_bprint_args(bprint_fmt, data, size, event);
+               arg = args;
+               ptr = bprint_fmt;
+       }
+
+       for (; *ptr; ptr++) {
+               ls = 0;
+               if (*ptr == '\\') {
+                       ptr++;
+                       switch (*ptr) {
+                       case 'n':
+                               trace_seq_putc(s, '\n');
+                               break;
+                       case 't':
+                               trace_seq_putc(s, '\t');
+                               break;
+                       case 'r':
+                               trace_seq_putc(s, '\r');
+                               break;
+                       case '\\':
+                               trace_seq_putc(s, '\\');
+                               break;
+                       default:
+                               trace_seq_putc(s, *ptr);
+                               break;
+                       }
+
+               } else if (*ptr == '%') {
+                       saveptr = ptr;
+                       show_func = 0;
+                       len_as_arg = 0;
+ cont_process:
+                       ptr++;
+                       switch (*ptr) {
+                       case '%':
+                               trace_seq_putc(s, '%');
+                               break;
+                       case '#':
+                               /* FIXME: need to handle properly */
+                               goto cont_process;
+                       case 'h':
+                               ls--;
+                               goto cont_process;
+                       case 'l':
+                               ls++;
+                               goto cont_process;
+                       case 'L':
+                               ls = 2;
+                               goto cont_process;
+                       case '*':
+                               /* The argument is the length. */
+                               if (!arg)
+                                       die("no argument match");
+                               len_arg = eval_num_arg(data, size, event, arg);
+                               len_as_arg = 1;
+                               arg = arg->next;
+                               goto cont_process;
+                       case '.':
+                       case 'z':
+                       case 'Z':
+                       case '0' ... '9':
+                               goto cont_process;
+                       case 'p':
+                               if (pevent->long_size == 4)
+                                       ls = 1;
+                               else
+                                       ls = 2;
+
+                               if (*(ptr+1) == 'F' ||
+                                   *(ptr+1) == 'f') {
+                                       ptr++;
+                                       show_func = *ptr;
+                               } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') {
+                                       print_mac_arg(s, *(ptr+1), data, size, event, arg);
+                                       ptr++;
+                                       break;
+                               }
+
+                               /* fall through */
+                       case 'd':
+                       case 'i':
+                       case 'x':
+                       case 'X':
+                       case 'u':
+                               if (!arg)
+                                       die("no argument match");
+
+                               len = ((unsigned long)ptr + 1) -
+                                       (unsigned long)saveptr;
+
+                               /* should never happen */
+                               if (len > 31)
+                                       die("bad format!");
+
+                               memcpy(format, saveptr, len);
+                               format[len] = 0;
+
+                               val = eval_num_arg(data, size, event, arg);
+                               arg = arg->next;
+
+                               if (show_func) {
+                                       func = find_func(pevent, val);
+                                       if (func) {
+                                               trace_seq_puts(s, func->func);
+                                               if (show_func == 'F')
+                                                       trace_seq_printf(s,
+                                                              "+0x%llx",
+                                                              val - func->addr);
+                                               break;
+                                       }
+                               }
+                               if (pevent->long_size == 8 && ls) {
+                                       char *p;
+
+                                       ls = 2;
+                                       /* make %l into %ll */
+                                       p = strchr(format, 'l');
+                                       if (p)
+                                               memmove(p, p+1, strlen(p)+1);
+                                       else if (strcmp(format, "%p") == 0)
+                                               strcpy(format, "0x%llx");
+                               }
+                               switch (ls) {
+                               case -2:
+                                       if (len_as_arg)
+                                               trace_seq_printf(s, format, len_arg, (char)val);
+                                       else
+                                               trace_seq_printf(s, format, (char)val);
+                                       break;
+                               case -1:
+                                       if (len_as_arg)
+                                               trace_seq_printf(s, format, len_arg, (short)val);
+                                       else
+                                               trace_seq_printf(s, format, (short)val);
+                                       break;
+                               case 0:
+                                       if (len_as_arg)
+                                               trace_seq_printf(s, format, len_arg, (int)val);
+                                       else
+                                               trace_seq_printf(s, format, (int)val);
+                                       break;
+                               case 1:
+                                       if (len_as_arg)
+                                               trace_seq_printf(s, format, len_arg, (long)val);
+                                       else
+                                               trace_seq_printf(s, format, (long)val);
+                                       break;
+                               case 2:
+                                       if (len_as_arg)
+                                               trace_seq_printf(s, format, len_arg,
+                                                                (long long)val);
+                                       else
+                                               trace_seq_printf(s, format, (long long)val);
+                                       break;
+                               default:
+                                       die("bad count (%d)", ls);
+                               }
+                               break;
+                       case 's':
+                               if (!arg)
+                                       die("no matching argument");
+
+                               len = ((unsigned long)ptr + 1) -
+                                       (unsigned long)saveptr;
+
+                               /* should never happen */
+                               if (len > 31)
+                                       die("bad format!");
+
+                               memcpy(format, saveptr, len);
+                               format[len] = 0;
+                               if (!len_as_arg)
+                                       len_arg = -1;
+                               print_str_arg(s, data, size, event,
+                                             format, len_arg, arg);
+                               arg = arg->next;
+                               break;
+                       default:
+                               trace_seq_printf(s, ">%c<", *ptr);
+
+                       }
+               } else
+                       trace_seq_putc(s, *ptr);
+       }
+
+       if (args) {
+               free_args(args);
+               free(bprint_fmt);
+       }
+}
+
+/**
+ * pevent_data_lat_fmt - parse the data for the latency format
+ * @pevent: a handle to the pevent
+ * @s: the trace_seq to write to
+ * @data: the raw data to read from
+ * @size: currently unused.
+ *
+ * This parses out the Latency format (interrupts disabled,
+ * need rescheduling, in hard/soft interrupt, preempt count
+ * and lock depth) and places it into the trace_seq.
+ */
+void pevent_data_lat_fmt(struct pevent *pevent,
+                        struct trace_seq *s, struct pevent_record *record)
+{
+       static int check_lock_depth = 1;
+       static int lock_depth_exists;
+       unsigned int lat_flags;
+       unsigned int pc;
+       int lock_depth;
+       int hardirq;
+       int softirq;
+       void *data = record->data;
+
+       lat_flags = parse_common_flags(pevent, data);
+       pc = parse_common_pc(pevent, data);
+       /* lock_depth may not always exist */
+       if (check_lock_depth) {
+               struct format_field *field;
+               struct event_format *event;
+
+               check_lock_depth = 0;
+               event = pevent->events[0];
+               field = pevent_find_common_field(event, "common_lock_depth");
+               if (field)
+                       lock_depth_exists = 1;
+       }
+       if (lock_depth_exists)
+               lock_depth = parse_common_lock_depth(pevent, data);
+
+       hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
+       softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
+
+       trace_seq_printf(s, "%c%c%c",
+              (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' :
+              (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ?
+              'X' : '.',
+              (lat_flags & TRACE_FLAG_NEED_RESCHED) ?
+              'N' : '.',
+              (hardirq && softirq) ? 'H' :
+              hardirq ? 'h' : softirq ? 's' : '.');
+
+       if (pc)
+               trace_seq_printf(s, "%x", pc);
+       else
+               trace_seq_putc(s, '.');
+
+       if (lock_depth_exists) {
+               if (lock_depth < 0)
+                       trace_seq_putc(s, '.');
+               else
+                       trace_seq_printf(s, "%d", lock_depth);
+       }
+
+       trace_seq_terminate(s);
+}
+
+/**
+ * pevent_data_type - parse out the given event type
+ * @pevent: a handle to the pevent
+ * @rec: the record to read from
+ *
+ * This returns the event id from the @rec.
+ */
+int pevent_data_type(struct pevent *pevent, struct pevent_record *rec)
+{
+       return trace_parse_common_type(pevent, rec->data);
+}
+
+/**
+ * pevent_data_event_from_type - find the event by a given type
+ * @pevent: a handle to the pevent
+ * @type: the type of the event.
+ *
+ * This returns the event form a given @type;
+ */
+struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type)
+{
+       return pevent_find_event(pevent, type);
+}
+
+/**
+ * pevent_data_pid - parse the PID from raw data
+ * @pevent: a handle to the pevent
+ * @rec: the record to parse
+ *
+ * This returns the PID from a raw data.
+ */
+int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec)
+{
+       return parse_common_pid(pevent, rec->data);
+}
+
+/**
+ * pevent_data_comm_from_pid - return the command line from PID
+ * @pevent: a handle to the pevent
+ * @pid: the PID of the task to search for
+ *
+ * This returns a pointer to the command line that has the given
+ * @pid.
+ */
+const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid)
+{
+       const char *comm;
+
+       comm = find_cmdline(pevent, pid);
+       return comm;
+}
+
+/**
+ * pevent_data_comm_from_pid - parse the data into the print format
+ * @s: the trace_seq to write to
+ * @event: the handle to the event
+ * @cpu: the cpu the event was recorded on
+ * @data: the raw data
+ * @size: the size of the raw data
+ * @nsecs: the timestamp of the event
+ *
+ * This parses the raw @data using the given @event information and
+ * writes the print format into the trace_seq.
+ */
+void pevent_event_info(struct trace_seq *s, struct event_format *event,
+                      struct pevent_record *record)
+{
+       int print_pretty = 1;
+
+       if (event->pevent->print_raw)
+               print_event_fields(s, record->data, record->size, event);
+       else {
+
+               if (event->handler)
+                       print_pretty = event->handler(s, record, event,
+                                                     event->context);
+
+               if (print_pretty)
+                       pretty_print(s, record->data, record->size, event);
+       }
+
+       trace_seq_terminate(s);
+}
+
+void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
+                       struct pevent_record *record)
+{
+       static char *spaces = "                    "; /* 20 spaces */
+       struct event_format *event;
+       unsigned long secs;
+       unsigned long usecs;
+       unsigned long nsecs;
+       const char *comm;
+       void *data = record->data;
+       int type;
+       int pid;
+       int len;
+       int p;
+
+       secs = record->ts / NSECS_PER_SEC;
+       nsecs = record->ts - secs * NSECS_PER_SEC;
+
+       if (record->size < 0) {
+               do_warning("ug! negative record size %d", record->size);
+               return;
+       }
+
+       type = trace_parse_common_type(pevent, data);
+
+       event = pevent_find_event(pevent, type);
+       if (!event) {
+               do_warning("ug! no event found for type %d", type);
+               return;
+       }
+
+       pid = parse_common_pid(pevent, data);
+       comm = find_cmdline(pevent, pid);
+
+       if (pevent->latency_format) {
+               trace_seq_printf(s, "%8.8s-%-5d %3d",
+                      comm, pid, record->cpu);
+               pevent_data_lat_fmt(pevent, s, record);
+       } else
+               trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu);
+
+       if (pevent->flags & PEVENT_NSEC_OUTPUT) {
+               usecs = nsecs;
+               p = 9;
+       } else {
+               usecs = (nsecs + 500) / NSECS_PER_USEC;
+               p = 6;
+       }
+
+       trace_seq_printf(s, " %5lu.%0*lu: %s: ", secs, p, usecs, event->name);
+
+       /* Space out the event names evenly. */
+       len = strlen(event->name);
+       if (len < 20)
+               trace_seq_printf(s, "%.*s", 20 - len, spaces);
+
+       pevent_event_info(s, event, record);
+}
+
+static int events_id_cmp(const void *a, const void *b)
+{
+       struct event_format * const * ea = a;
+       struct event_format * const * eb = b;
+
+       if ((*ea)->id < (*eb)->id)
+               return -1;
+
+       if ((*ea)->id > (*eb)->id)
+               return 1;
+
+       return 0;
+}
+
+static int events_name_cmp(const void *a, const void *b)
+{
+       struct event_format * const * ea = a;
+       struct event_format * const * eb = b;
+       int res;
+
+       res = strcmp((*ea)->name, (*eb)->name);
+       if (res)
+               return res;
+
+       res = strcmp((*ea)->system, (*eb)->system);
+       if (res)
+               return res;
+
+       return events_id_cmp(a, b);
+}
+
+static int events_system_cmp(const void *a, const void *b)
+{
+       struct event_format * const * ea = a;
+       struct event_format * const * eb = b;
+       int res;
+
+       res = strcmp((*ea)->system, (*eb)->system);
+       if (res)
+               return res;
+
+       res = strcmp((*ea)->name, (*eb)->name);
+       if (res)
+               return res;
+
+       return events_id_cmp(a, b);
+}
+
+struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type sort_type)
+{
+       struct event_format **events;
+       int (*sort)(const void *a, const void *b);
+
+       events = pevent->sort_events;
+
+       if (events && pevent->last_type == sort_type)
+               return events;
+
+       if (!events) {
+               events = malloc(sizeof(*events) * (pevent->nr_events + 1));
+               if (!events)
+                       return NULL;
+
+               memcpy(events, pevent->events, sizeof(*events) * pevent->nr_events);
+               events[pevent->nr_events] = NULL;
+
+               pevent->sort_events = events;
+
+               /* the internal events are sorted by id */
+               if (sort_type == EVENT_SORT_ID) {
+                       pevent->last_type = sort_type;
+                       return events;
+               }
+       }
+
+       switch (sort_type) {
+       case EVENT_SORT_ID:
+               sort = events_id_cmp;
+               break;
+       case EVENT_SORT_NAME:
+               sort = events_name_cmp;
+               break;
+       case EVENT_SORT_SYSTEM:
+               sort = events_system_cmp;
+               break;
+       default:
+               return events;
+       }
+
+       qsort(events, pevent->nr_events, sizeof(*events), sort);
+       pevent->last_type = sort_type;
+
+       return events;
+}
+
+static struct format_field **
+get_event_fields(const char *type, const char *name,
+                int count, struct format_field *list)
+{
+       struct format_field **fields;
+       struct format_field *field;
+       int i = 0;
+
+       fields = malloc_or_die(sizeof(*fields) * (count + 1));
+       for (field = list; field; field = field->next) {
+               fields[i++] = field;
+               if (i == count + 1) {
+                       do_warning("event %s has more %s fields than specified",
+                               name, type);
+                       i--;
+                       break;
+               }
+       }
+
+       if (i != count)
+               do_warning("event %s has less %s fields than specified",
+                       name, type);
+
+       fields[i] = NULL;
+
+       return fields;
+}
+
+/**
+ * pevent_event_common_fields - return a list of common fields for an event
+ * @event: the event to return the common fields of.
+ *
+ * Returns an allocated array of fields. The last item in the array is NULL.
+ * The array must be freed with free().
+ */
+struct format_field **pevent_event_common_fields(struct event_format *event)
+{
+       return get_event_fields("common", event->name,
+                               event->format.nr_common,
+                               event->format.common_fields);
+}
+
+/**
+ * pevent_event_fields - return a list of event specific fields for an event
+ * @event: the event to return the fields of.
+ *
+ * Returns an allocated array of fields. The last item in the array is NULL.
+ * The array must be freed with free().
+ */
+struct format_field **pevent_event_fields(struct event_format *event)
+{
+       return get_event_fields("event", event->name,
+                               event->format.nr_fields,
+                               event->format.fields);
+}
+
+static void print_fields(struct trace_seq *s, struct print_flag_sym *field)
+{
+       trace_seq_printf(s, "{ %s, %s }", field->value, field->str);
+       if (field->next) {
+               trace_seq_puts(s, ", ");
+               print_fields(s, field->next);
+       }
+}
+
+/* for debugging */
+static void print_args(struct print_arg *args)
+{
+       int print_paren = 1;
+       struct trace_seq s;
+
+       switch (args->type) {
+       case PRINT_NULL:
+               printf("null");
+               break;
+       case PRINT_ATOM:
+               printf("%s", args->atom.atom);
+               break;
+       case PRINT_FIELD:
+               printf("REC->%s", args->field.name);
+               break;
+       case PRINT_FLAGS:
+               printf("__print_flags(");
+               print_args(args->flags.field);
+               printf(", %s, ", args->flags.delim);
+               trace_seq_init(&s);
+               print_fields(&s, args->flags.flags);
+               trace_seq_do_printf(&s);
+               trace_seq_destroy(&s);
+               printf(")");
+               break;
+       case PRINT_SYMBOL:
+               printf("__print_symbolic(");
+               print_args(args->symbol.field);
+               printf(", ");
+               trace_seq_init(&s);
+               print_fields(&s, args->symbol.symbols);
+               trace_seq_do_printf(&s);
+               trace_seq_destroy(&s);
+               printf(")");
+               break;
+       case PRINT_STRING:
+       case PRINT_BSTRING:
+               printf("__get_str(%s)", args->string.string);
+               break;
+       case PRINT_TYPE:
+               printf("(%s)", args->typecast.type);
+               print_args(args->typecast.item);
+               break;
+       case PRINT_OP:
+               if (strcmp(args->op.op, ":") == 0)
+                       print_paren = 0;
+               if (print_paren)
+                       printf("(");
+               print_args(args->op.left);
+               printf(" %s ", args->op.op);
+               print_args(args->op.right);
+               if (print_paren)
+                       printf(")");
+               break;
+       default:
+               /* we should warn... */
+               return;
+       }
+       if (args->next) {
+               printf("\n");
+               print_args(args->next);
+       }
+}
+
+static void parse_header_field(const char *field,
+                              int *offset, int *size, int mandatory)
+{
+       unsigned long long save_input_buf_ptr;
+       unsigned long long save_input_buf_siz;
+       char *token;
+       int type;
+
+       save_input_buf_ptr = input_buf_ptr;
+       save_input_buf_siz = input_buf_siz;
+
+       if (read_expected(EVENT_ITEM, "field") < 0)
+               return;
+       if (read_expected(EVENT_OP, ":") < 0)
+               return;
+
+       /* type */
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+       free_token(token);
+
+       /*
+        * If this is not a mandatory field, then test it first.
+        */
+       if (mandatory) {
+               if (read_expected(EVENT_ITEM, field) < 0)
+                       return;
+       } else {
+               if (read_expect_type(EVENT_ITEM, &token) < 0)
+                       goto fail;
+               if (strcmp(token, field) != 0)
+                       goto discard;
+               free_token(token);
+       }
+
+       if (read_expected(EVENT_OP, ";") < 0)
+               return;
+       if (read_expected(EVENT_ITEM, "offset") < 0)
+               return;
+       if (read_expected(EVENT_OP, ":") < 0)
+               return;
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+       *offset = atoi(token);
+       free_token(token);
+       if (read_expected(EVENT_OP, ";") < 0)
+               return;
+       if (read_expected(EVENT_ITEM, "size") < 0)
+               return;
+       if (read_expected(EVENT_OP, ":") < 0)
+               return;
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+       *size = atoi(token);
+       free_token(token);
+       if (read_expected(EVENT_OP, ";") < 0)
+               return;
+       type = read_token(&token);
+       if (type != EVENT_NEWLINE) {
+               /* newer versions of the kernel have a "signed" type */
+               if (type != EVENT_ITEM)
+                       goto fail;
+
+               if (strcmp(token, "signed") != 0)
+                       goto fail;
+
+               free_token(token);
+
+               if (read_expected(EVENT_OP, ":") < 0)
+                       return;
+
+               if (read_expect_type(EVENT_ITEM, &token))
+                       goto fail;
+
+               free_token(token);
+               if (read_expected(EVENT_OP, ";") < 0)
+                       return;
+
+               if (read_expect_type(EVENT_NEWLINE, &token))
+                       goto fail;
+       }
+ fail:
+       free_token(token);
+       return;
+
+ discard:
+       input_buf_ptr = save_input_buf_ptr;
+       input_buf_siz = save_input_buf_siz;
+       *offset = 0;
+       *size = 0;
+       free_token(token);
+}
+
+/**
+ * pevent_parse_header_page - parse the data stored in the header page
+ * @pevent: the handle to the pevent
+ * @buf: the buffer storing the header page format string
+ * @size: the size of @buf
+ * @long_size: the long size to use if there is no header
+ *
+ * This parses the header page format for information on the
+ * ring buffer used. The @buf should be copied from
+ *
+ * /sys/kernel/debug/tracing/events/header_page
+ */
+int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size,
+                            int long_size)
+{
+       int ignore;
+
+       if (!size) {
+               /*
+                * Old kernels did not have header page info.
+                * Sorry but we just use what we find here in user space.
+                */
+               pevent->header_page_ts_size = sizeof(long long);
+               pevent->header_page_size_size = long_size;
+               pevent->header_page_data_offset = sizeof(long long) + long_size;
+               pevent->old_format = 1;
+               return -1;
+       }
+       init_input_buf(buf, size);
+
+       parse_header_field("timestamp", &pevent->header_page_ts_offset,
+                          &pevent->header_page_ts_size, 1);
+       parse_header_field("commit", &pevent->header_page_size_offset,
+                          &pevent->header_page_size_size, 1);
+       parse_header_field("overwrite", &pevent->header_page_overwrite,
+                          &ignore, 0);
+       parse_header_field("data", &pevent->header_page_data_offset,
+                          &pevent->header_page_data_size, 1);
+
+       return 0;
+}
+
+static int event_matches(struct event_format *event,
+                        int id, const char *sys_name,
+                        const char *event_name)
+{
+       if (id >= 0 && id != event->id)
+               return 0;
+
+       if (event_name && (strcmp(event_name, event->name) != 0))
+               return 0;
+
+       if (sys_name && (strcmp(sys_name, event->system) != 0))
+               return 0;
+
+       return 1;
+}
+
+static void free_handler(struct event_handler *handle)
+{
+       free((void *)handle->sys_name);
+       free((void *)handle->event_name);
+       free(handle);
+}
+
+static int find_event_handle(struct pevent *pevent, struct event_format *event)
+{
+       struct event_handler *handle, **next;
+
+       for (next = &pevent->handlers; *next;
+            next = &(*next)->next) {
+               handle = *next;
+               if (event_matches(event, handle->id,
+                                 handle->sys_name,
+                                 handle->event_name))
+                       break;
+       }
+
+       if (!(*next))
+               return 0;
+
+       pr_stat("overriding event (%d) %s:%s with new print handler",
+               event->id, event->system, event->name);
+
+       event->handler = handle->func;
+       event->context = handle->context;
+
+       *next = handle->next;
+       free_handler(handle);
+
+       return 1;
+}
+
+/**
+ * pevent_parse_event - parse the event format
+ * @pevent: the handle to the pevent
+ * @buf: the buffer storing the event format string
+ * @size: the size of @buf
+ * @sys: the system the event belongs to
+ *
+ * This parses the event format and creates an event structure
+ * to quickly parse raw data for a given event.
+ *
+ * These files currently come from:
+ *
+ * /sys/kernel/debug/tracing/events/.../.../format
+ */
+int pevent_parse_event(struct pevent *pevent,
+                      const char *buf, unsigned long size,
+                      const char *sys)
+{
+       struct event_format *event;
+       int ret;
+
+       init_input_buf(buf, size);
+
+       event = alloc_event();
+       if (!event)
+               return -ENOMEM;
+
+       event->name = event_read_name();
+       if (!event->name) {
+               /* Bad event? */
+               free(event);
+               return -1;
+       }
+
+       if (strcmp(sys, "ftrace") == 0) {
+
+               event->flags |= EVENT_FL_ISFTRACE;
+
+               if (strcmp(event->name, "bprint") == 0)
+                       event->flags |= EVENT_FL_ISBPRINT;
+       }
+               
+       event->id = event_read_id();
+       if (event->id < 0)
+               die("failed to read event id");
+
+       event->system = strdup(sys);
+
+       /* Add pevent to event so that it can be referenced */
+       event->pevent = pevent;
+
+       ret = event_read_format(event);
+       if (ret < 0) {
+               do_warning("failed to read event format for %s", event->name);
+               goto event_failed;
+       }
+
+       /*
+        * If the event has an override, don't print warnings if the event
+        * print format fails to parse.
+        */
+       if (find_event_handle(pevent, event))
+               show_warning = 0;
+
+       ret = event_read_print(event);
+       if (ret < 0) {
+               do_warning("failed to read event print fmt for %s",
+                          event->name);
+               show_warning = 1;
+               goto event_failed;
+       }
+       show_warning = 1;
+
+       add_event(pevent, event);
+
+       if (!ret && (event->flags & EVENT_FL_ISFTRACE)) {
+               struct format_field *field;
+               struct print_arg *arg, **list;
+
+               /* old ftrace had no args */
+
+               list = &event->print_fmt.args;
+               for (field = event->format.fields; field; field = field->next) {
+                       arg = alloc_arg();
+                       *list = arg;
+                       list = &arg->next;
+                       arg->type = PRINT_FIELD;
+                       arg->field.name = strdup(field->name);
+                       arg->field.field = field;
+               }
+               return 0;
+       }
+
+#define PRINT_ARGS 0
+       if (PRINT_ARGS && event->print_fmt.args)
+               print_args(event->print_fmt.args);
+
+       return 0;
+
+ event_failed:
+       event->flags |= EVENT_FL_FAILED;
+       /* still add it even if it failed */
+       add_event(pevent, event);
+       return -1;
+}
+
+int get_field_val(struct trace_seq *s, struct format_field *field,
+                 const char *name, struct pevent_record *record,
+                 unsigned long long *val, int err)
+{
+       if (!field) {
+               if (err)
+                       trace_seq_printf(s, "<CANT FIND FIELD %s>", name);
+               return -1;
+       }
+
+       if (pevent_read_number_field(field, record->data, val)) {
+               if (err)
+                       trace_seq_printf(s, " %s=INVALID", name);
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * pevent_get_field_raw - return the raw pointer into the data field
+ * @s: The seq to print to on error
+ * @event: the event that the field is for
+ * @name: The name of the field
+ * @record: The record with the field name.
+ * @len: place to store the field length.
+ * @err: print default error if failed.
+ *
+ * Returns a pointer into record->data of the field and places
+ * the length of the field in @len.
+ *
+ * On failure, it returns NULL.
+ */
+void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
+                          const char *name, struct pevent_record *record,
+                          int *len, int err)
+{
+       struct format_field *field;
+       void *data = record->data;
+       unsigned offset;
+       int dummy;
+
+       if (!event)
+               return NULL;
+
+       field = pevent_find_field(event, name);
+
+       if (!field) {
+               if (err)
+                       trace_seq_printf(s, "<CANT FIND FIELD %s>", name);
+               return NULL;
+       }
+
+       /* Allow @len to be NULL */
+       if (!len)
+               len = &dummy;
+
+       offset = field->offset;
+       if (field->flags & FIELD_IS_DYNAMIC) {
+               offset = pevent_read_number(event->pevent,
+                                           data + offset, field->size);
+               *len = offset >> 16;
+               offset &= 0xffff;
+       } else
+               *len = field->size;
+
+       return data + offset;
+}
+
+/**
+ * pevent_get_field_val - find a field and return its value
+ * @s: The seq to print to on error
+ * @event: the event that the field is for
+ * @name: The name of the field
+ * @record: The record with the field name.
+ * @val: place to store the value of the field.
+ * @err: print default error if failed.
+ *
+ * Returns 0 on success -1 on field not found.
+ */
+int pevent_get_field_val(struct trace_seq *s, struct event_format *event,
+                        const char *name, struct pevent_record *record,
+                        unsigned long long *val, int err)
+{
+       struct format_field *field;
+
+       if (!event)
+               return -1;
+
+       field = pevent_find_field(event, name);
+
+       return get_field_val(s, field, name, record, val, err);
+}
+
+/**
+ * pevent_get_common_field_val - find a common field and return its value
+ * @s: The seq to print to on error
+ * @event: the event that the field is for
+ * @name: The name of the field
+ * @record: The record with the field name.
+ * @val: place to store the value of the field.
+ * @err: print default error if failed.
+ *
+ * Returns 0 on success -1 on field not found.
+ */
+int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event,
+                               const char *name, struct pevent_record *record,
+                               unsigned long long *val, int err)
+{
+       struct format_field *field;
+
+       if (!event)
+               return -1;
+
+       field = pevent_find_common_field(event, name);
+
+       return get_field_val(s, field, name, record, val, err);
+}
+
+/**
+ * pevent_get_any_field_val - find a any field and return its value
+ * @s: The seq to print to on error
+ * @event: the event that the field is for
+ * @name: The name of the field
+ * @record: The record with the field name.
+ * @val: place to store the value of the field.
+ * @err: print default error if failed.
+ *
+ * Returns 0 on success -1 on field not found.
+ */
+int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event,
+                            const char *name, struct pevent_record *record,
+                            unsigned long long *val, int err)
+{
+       struct format_field *field;
+
+       if (!event)
+               return -1;
+
+       field = pevent_find_any_field(event, name);
+
+       return get_field_val(s, field, name, record, val, err);
+}
+
+/**
+ * pevent_print_num_field - print a field and a format
+ * @s: The seq to print to
+ * @fmt: The printf format to print the field with.
+ * @event: the event that the field is for
+ * @name: The name of the field
+ * @record: The record with the field name.
+ * @err: print default error if failed.
+ *
+ * Returns: 0 on success, -1 field not fould, or 1 if buffer is full.
+ */
+int pevent_print_num_field(struct trace_seq *s, const char *fmt,
+                          struct event_format *event, const char *name,
+                          struct pevent_record *record, int err)
+{
+       struct format_field *field = pevent_find_field(event, name);
+       unsigned long long val;
+
+       if (!field)
+               goto failed;
+
+       if (pevent_read_number_field(field, record->data, &val))
+               goto failed;
+
+       return trace_seq_printf(s, fmt, val);
+
+ failed:
+       if (err)
+               trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name);
+       return -1;
+}
+
+static void free_func_handle(struct pevent_function_handler *func)
+{
+       struct pevent_func_params *params;
+
+       free(func->name);
+
+       while (func->params) {
+               params = func->params;
+               func->params = params->next;
+               free(params);
+       }
+
+       free(func);
+}
+
+/**
+ * pevent_register_print_function - register a helper function
+ * @pevent: the handle to the pevent
+ * @func: the function to process the helper function
+ * @name: the name of the helper function
+ * @parameters: A list of enum pevent_func_arg_type
+ *
+ * Some events may have helper functions in the print format arguments.
+ * This allows a plugin to dynmically create a way to process one
+ * of these functions.
+ *
+ * The @parameters is a variable list of pevent_func_arg_type enums that
+ * must end with PEVENT_FUNC_ARG_VOID.
+ */
+int pevent_register_print_function(struct pevent *pevent,
+                                  pevent_func_handler func,
+                                  enum pevent_func_arg_type ret_type,
+                                  char *name, ...)
+{
+       struct pevent_function_handler *func_handle;
+       struct pevent_func_params **next_param;
+       struct pevent_func_params *param;
+       enum pevent_func_arg_type type;
+       va_list ap;
+
+       func_handle = find_func_handler(pevent, name);
+       if (func_handle) {
+               /*
+                * This is most like caused by the users own
+                * plugins updating the function. This overrides the
+                * system defaults.
+                */
+               pr_stat("override of function helper '%s'", name);
+               remove_func_handler(pevent, name);
+       }
+
+       func_handle = malloc_or_die(sizeof(*func_handle));
+       memset(func_handle, 0, sizeof(*func_handle));
+
+       func_handle->ret_type = ret_type;
+       func_handle->name = strdup(name);
+       func_handle->func = func;
+       if (!func_handle->name)
+               die("Failed to allocate function name");
+
+       next_param = &(func_handle->params);
+       va_start(ap, name);
+       for (;;) {
+               type = va_arg(ap, enum pevent_func_arg_type);
+               if (type == PEVENT_FUNC_ARG_VOID)
+                       break;
+
+               if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) {
+                       warning("Invalid argument type %d", type);
+                       goto out_free;
+               }
+
+               param = malloc_or_die(sizeof(*param));
+               param->type = type;
+               param->next = NULL;
+
+               *next_param = param;
+               next_param = &(param->next);
+
+               func_handle->nr_args++;
+       }
+       va_end(ap);
+
+       func_handle->next = pevent->func_handlers;
+       pevent->func_handlers = func_handle;
+
+       return 0;
+ out_free:
+       va_end(ap);
+       free_func_handle(func_handle);
+       return -1;
+}
+
+/**
+ * pevent_register_event_handle - register a way to parse an event
+ * @pevent: the handle to the pevent
+ * @id: the id of the event to register
+ * @sys_name: the system name the event belongs to
+ * @event_name: the name of the event
+ * @func: the function to call to parse the event information
+ *
+ * This function allows a developer to override the parsing of
+ * a given event. If for some reason the default print format
+ * is not sufficient, this function will register a function
+ * for an event to be used to parse the data instead.
+ *
+ * If @id is >= 0, then it is used to find the event.
+ * else @sys_name and @event_name are used.
+ */
+int pevent_register_event_handler(struct pevent *pevent,
+                                 int id, char *sys_name, char *event_name,
+                                 pevent_event_handler_func func,
+                                 void *context)
+{
+       struct event_format *event;
+       struct event_handler *handle;
+
+       if (id >= 0) {
+               /* search by id */
+               event = pevent_find_event(pevent, id);
+               if (!event)
+                       goto not_found;
+               if (event_name && (strcmp(event_name, event->name) != 0))
+                       goto not_found;
+               if (sys_name && (strcmp(sys_name, event->system) != 0))
+                       goto not_found;
+       } else {
+               event = pevent_find_event_by_name(pevent, sys_name, event_name);
+               if (!event)
+                       goto not_found;
+       }
+
+       pr_stat("overriding event (%d) %s:%s with new print handler",
+               event->id, event->system, event->name);
+
+       event->handler = func;
+       event->context = context;
+       return 0;
+
+ not_found:
+       /* Save for later use. */
+       handle = malloc_or_die(sizeof(*handle));
+       memset(handle, 0, sizeof(*handle));
+       handle->id = id;
+       if (event_name)
+               handle->event_name = strdup(event_name);
+       if (sys_name)
+               handle->sys_name = strdup(sys_name);
+
+       handle->func = func;
+       handle->next = pevent->handlers;
+       pevent->handlers = handle;
+       handle->context = context;
+
+       return -1;
+}
+
+/**
+ * pevent_alloc - create a pevent handle
+ */
+struct pevent *pevent_alloc(void)
+{
+       struct pevent *pevent;
+
+       pevent = malloc(sizeof(*pevent));
+       if (!pevent)
+               return NULL;
+       memset(pevent, 0, sizeof(*pevent));
+       pevent->ref_count = 1;
+
+       return pevent;
+}
+
+void pevent_ref(struct pevent *pevent)
+{
+       pevent->ref_count++;
+}
+
+static void free_format_fields(struct format_field *field)
+{
+       struct format_field *next;
+
+       while (field) {
+               next = field->next;
+               free(field->type);
+               free(field->name);
+               free(field);
+               field = next;
+       }
+}
+
+static void free_formats(struct format *format)
+{
+       free_format_fields(format->common_fields);
+       free_format_fields(format->fields);
+}
+
+static void free_event(struct event_format *event)
+{
+       free(event->name);
+       free(event->system);
+
+       free_formats(&event->format);
+
+       free(event->print_fmt.format);
+       free_args(event->print_fmt.args);
+
+       free(event);
+}
+
+/**
+ * pevent_free - free a pevent handle
+ * @pevent: the pevent handle to free
+ */
+void pevent_free(struct pevent *pevent)
+{
+       struct cmdline_list *cmdlist, *cmdnext;
+       struct func_list *funclist, *funcnext;
+       struct printk_list *printklist, *printknext;
+       struct pevent_function_handler *func_handler;
+       struct event_handler *handle;
+       int i;
+
+       if (!pevent)
+               return;
+
+       cmdlist = pevent->cmdlist;
+       funclist = pevent->funclist;
+       printklist = pevent->printklist;
+
+       pevent->ref_count--;
+       if (pevent->ref_count)
+               return;
+
+       if (pevent->cmdlines) {
+               for (i = 0; i < pevent->cmdline_count; i++)
+                       free(pevent->cmdlines[i].comm);
+               free(pevent->cmdlines);
+       }
+
+       while (cmdlist) {
+               cmdnext = cmdlist->next;
+               free(cmdlist->comm);
+               free(cmdlist);
+               cmdlist = cmdnext;
+       }
+
+       if (pevent->func_map) {
+               for (i = 0; i < pevent->func_count; i++) {
+                       free(pevent->func_map[i].func);
+                       free(pevent->func_map[i].mod);
+               }
+               free(pevent->func_map);
+       }
+
+       while (funclist) {
+               funcnext = funclist->next;
+               free(funclist->func);
+               free(funclist->mod);
+               free(funclist);
+               funclist = funcnext;
+       }
+
+       while (pevent->func_handlers) {
+               func_handler = pevent->func_handlers;
+               pevent->func_handlers = func_handler->next;
+               free_func_handle(func_handler);
+       }
+
+       if (pevent->printk_map) {
+               for (i = 0; i < pevent->printk_count; i++)
+                       free(pevent->printk_map[i].printk);
+               free(pevent->printk_map);
+       }
+
+       while (printklist) {
+               printknext = printklist->next;
+               free(printklist->printk);
+               free(printklist);
+               printklist = printknext;
+       }
+
+       for (i = 0; i < pevent->nr_events; i++)
+               free_event(pevent->events[i]);
+
+       while (pevent->handlers) {
+               handle = pevent->handlers;
+               pevent->handlers = handle->next;
+               free_handler(handle);
+       }
+
+       free(pevent->events);
+       free(pevent->sort_events);
+
+       free(pevent);
+}
+
+void pevent_unref(struct pevent *pevent)
+{
+       pevent_free(pevent);
+}
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h
new file mode 100644 (file)
index 0000000..ac997bc
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef _PARSE_EVENTS_H
+#define _PARSE_EVENTS_H
+
+#include <stdarg.h>
+#include <regex.h>
+
+#ifndef __unused
+#define __unused __attribute__ ((unused))
+#endif
+
+/* ----------------------- trace_seq ----------------------- */
+
+
+#ifndef TRACE_SEQ_BUF_SIZE
+#define TRACE_SEQ_BUF_SIZE 4096
+#endif
+
+#ifndef DEBUG_RECORD
+#define DEBUG_RECORD 0
+#endif
+
+struct pevent_record {
+       unsigned long long      ts;
+       unsigned long long      offset;
+       long long               missed_events;  /* buffer dropped events before */
+       int                     record_size;    /* size of binary record */
+       int                     size;           /* size of data */
+       void                    *data;
+       int                     cpu;
+       int                     ref_count;
+       int                     locked;         /* Do not free, even if ref_count is zero */
+       void                    *private;
+#if DEBUG_RECORD
+       struct pevent_record    *prev;
+       struct pevent_record    *next;
+       long                    alloc_addr;
+#endif
+};
+
+/*
+ * Trace sequences are used to allow a function to call several other functions
+ * to create a string of data to use (up to a max of PAGE_SIZE).
+ */
+
+struct trace_seq {
+       char                    *buffer;
+       unsigned int            buffer_size;
+       unsigned int            len;
+       unsigned int            readpos;
+};
+
+void trace_seq_init(struct trace_seq *s);
+void trace_seq_destroy(struct trace_seq *s);
+
+extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
+       __attribute__ ((format (printf, 2, 3)));
+extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
+       __attribute__ ((format (printf, 2, 0)));
+
+extern int trace_seq_puts(struct trace_seq *s, const char *str);
+extern int trace_seq_putc(struct trace_seq *s, unsigned char c);
+
+extern void trace_seq_terminate(struct trace_seq *s);
+
+extern int trace_seq_do_printf(struct trace_seq *s);
+
+
+/* ----------------------- pevent ----------------------- */
+
+struct pevent;
+struct event_format;
+
+typedef int (*pevent_event_handler_func)(struct trace_seq *s,
+                                        struct pevent_record *record,
+                                        struct event_format *event,
+                                        void *context);
+
+typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
+typedef int (*pevent_plugin_unload_func)(void);
+
+struct plugin_option {
+       struct plugin_option            *next;
+       void                            *handle;
+       char                            *file;
+       char                            *name;
+       char                            *plugin_alias;
+       char                            *description;
+       char                            *value;
+       void                            *private;
+       int                             set;
+};
+
+/*
+ * Plugin hooks that can be called:
+ *
+ * PEVENT_PLUGIN_LOADER:  (required)
+ *   The function name to initialized the plugin.
+ *
+ *   int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
+ *
+ * PEVENT_PLUGIN_UNLOADER:  (optional)
+ *   The function called just before unloading
+ *
+ *   int PEVENT_PLUGIN_UNLOADER(void)
+ *
+ * PEVENT_PLUGIN_OPTIONS:  (optional)
+ *   Plugin options that can be set before loading
+ *
+ *   struct plugin_option PEVENT_PLUGIN_OPTIONS[] = {
+ *     {
+ *             .name = "option-name",
+ *             .plugin_alias = "overide-file-name", (optional)
+ *             .description = "description of option to show users",
+ *     },
+ *     {
+ *             .name = NULL,
+ *     },
+ *   };
+ *
+ *   Array must end with .name = NULL;
+ *
+ *
+ *   .plugin_alias is used to give a shorter name to access
+ *   the vairable. Useful if a plugin handles more than one event.
+ *
+ * PEVENT_PLUGIN_ALIAS: (optional)
+ *   The name to use for finding options (uses filename if not defined)
+ */
+#define PEVENT_PLUGIN_LOADER pevent_plugin_loader
+#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader
+#define PEVENT_PLUGIN_OPTIONS pevent_plugin_options
+#define PEVENT_PLUGIN_ALIAS pevent_plugin_alias
+#define _MAKE_STR(x)   #x
+#define MAKE_STR(x)    _MAKE_STR(x)
+#define PEVENT_PLUGIN_LOADER_NAME MAKE_STR(PEVENT_PLUGIN_LOADER)
+#define PEVENT_PLUGIN_UNLOADER_NAME MAKE_STR(PEVENT_PLUGIN_UNLOADER)
+#define PEVENT_PLUGIN_OPTIONS_NAME MAKE_STR(PEVENT_PLUGIN_OPTIONS)
+#define PEVENT_PLUGIN_ALIAS_NAME MAKE_STR(PEVENT_PLUGIN_ALIAS)
+
+#define NSECS_PER_SEC          1000000000ULL
+#define NSECS_PER_USEC         1000ULL
+
+enum format_flags {
+       FIELD_IS_ARRAY          = 1,
+       FIELD_IS_POINTER        = 2,
+       FIELD_IS_SIGNED         = 4,
+       FIELD_IS_STRING         = 8,
+       FIELD_IS_DYNAMIC        = 16,
+       FIELD_IS_LONG           = 32,
+       FIELD_IS_FLAG           = 64,
+       FIELD_IS_SYMBOLIC       = 128,
+};
+
+struct format_field {
+       struct format_field     *next;
+       struct event_format     *event;
+       char                    *type;
+       char                    *name;
+       int                     offset;
+       int                     size;
+       unsigned int            arraylen;
+       unsigned int            elementsize;
+       unsigned long           flags;
+};
+
+struct format {
+       int                     nr_common;
+       int                     nr_fields;
+       struct format_field     *common_fields;
+       struct format_field     *fields;
+};
+
+struct print_arg_atom {
+       char                    *atom;
+};
+
+struct print_arg_string {
+       char                    *string;
+       int                     offset;
+};
+
+struct print_arg_field {
+       char                    *name;
+       struct format_field     *field;
+};
+
+struct print_flag_sym {
+       struct print_flag_sym   *next;
+       char                    *value;
+       char                    *str;
+};
+
+struct print_arg_typecast {
+       char                    *type;
+       struct print_arg        *item;
+};
+
+struct print_arg_flags {
+       struct print_arg        *field;
+       char                    *delim;
+       struct print_flag_sym   *flags;
+};
+
+struct print_arg_symbol {
+       struct print_arg        *field;
+       struct print_flag_sym   *symbols;
+};
+
+struct print_arg_dynarray {
+       struct format_field     *field;
+       struct print_arg        *index;
+};
+
+struct print_arg;
+
+struct print_arg_op {
+       char                    *op;
+       int                     prio;
+       struct print_arg        *left;
+       struct print_arg        *right;
+};
+
+struct pevent_function_handler;
+
+struct print_arg_func {
+       struct pevent_function_handler  *func;
+       struct print_arg                *args;
+};
+
+enum print_arg_type {
+       PRINT_NULL,
+       PRINT_ATOM,
+       PRINT_FIELD,
+       PRINT_FLAGS,
+       PRINT_SYMBOL,
+       PRINT_TYPE,
+       PRINT_STRING,
+       PRINT_BSTRING,
+       PRINT_DYNAMIC_ARRAY,
+       PRINT_OP,
+       PRINT_FUNC,
+};
+
+struct print_arg {
+       struct print_arg                *next;
+       enum print_arg_type             type;
+       union {
+               struct print_arg_atom           atom;
+               struct print_arg_field          field;
+               struct print_arg_typecast       typecast;
+               struct print_arg_flags          flags;
+               struct print_arg_symbol         symbol;
+               struct print_arg_func           func;
+               struct print_arg_string         string;
+               struct print_arg_op             op;
+               struct print_arg_dynarray       dynarray;
+       };
+};
+
+struct print_fmt {
+       char                    *format;
+       struct print_arg        *args;
+};
+
+struct event_format {
+       struct pevent           *pevent;
+       char                    *name;
+       int                     id;
+       int                     flags;
+       struct format           format;
+       struct print_fmt        print_fmt;
+       char                    *system;
+       pevent_event_handler_func handler;
+       void                    *context;
+};
+
+enum {
+       EVENT_FL_ISFTRACE       = 0x01,
+       EVENT_FL_ISPRINT        = 0x02,
+       EVENT_FL_ISBPRINT       = 0x04,
+       EVENT_FL_ISFUNCENT      = 0x10,
+       EVENT_FL_ISFUNCRET      = 0x20,
+
+       EVENT_FL_FAILED         = 0x80000000
+};
+
+enum event_sort_type {
+       EVENT_SORT_ID,
+       EVENT_SORT_NAME,
+       EVENT_SORT_SYSTEM,
+};
+
+enum event_type {
+       EVENT_ERROR,
+       EVENT_NONE,
+       EVENT_SPACE,
+       EVENT_NEWLINE,
+       EVENT_OP,
+       EVENT_DELIM,
+       EVENT_ITEM,
+       EVENT_DQUOTE,
+       EVENT_SQUOTE,
+};
+
+typedef unsigned long long (*pevent_func_handler)(struct trace_seq *s,
+                                            unsigned long long *args);
+
+enum pevent_func_arg_type {
+       PEVENT_FUNC_ARG_VOID,
+       PEVENT_FUNC_ARG_INT,
+       PEVENT_FUNC_ARG_LONG,
+       PEVENT_FUNC_ARG_STRING,
+       PEVENT_FUNC_ARG_PTR,
+       PEVENT_FUNC_ARG_MAX_TYPES
+};
+
+enum pevent_flag {
+       PEVENT_NSEC_OUTPUT              = 1,    /* output in NSECS */
+};
+
+struct cmdline;
+struct cmdline_list;
+struct func_map;
+struct func_list;
+struct event_handler;
+
+struct pevent {
+       int ref_count;
+
+       int header_page_ts_offset;
+       int header_page_ts_size;
+       int header_page_size_offset;
+       int header_page_size_size;
+       int header_page_data_offset;
+       int header_page_data_size;
+       int header_page_overwrite;
+
+       int file_bigendian;
+       int host_bigendian;
+
+       int latency_format;
+
+       int old_format;
+
+       int cpus;
+       int long_size;
+
+       struct cmdline *cmdlines;
+       struct cmdline_list *cmdlist;
+       int cmdline_count;
+
+       struct func_map *func_map;
+       struct func_list *funclist;
+       unsigned int func_count;
+
+       struct printk_map *printk_map;
+       struct printk_list *printklist;
+       unsigned int printk_count;
+
+
+       struct event_format **events;
+       int nr_events;
+       struct event_format **sort_events;
+       enum event_sort_type last_type;
+
+       int type_offset;
+       int type_size;
+
+       int pid_offset;
+       int pid_size;
+
+       int pc_offset;
+       int pc_size;
+
+       int flags_offset;
+       int flags_size;
+
+       int ld_offset;
+       int ld_size;
+
+       int print_raw;
+
+       int test_filters;
+
+       int flags;
+
+       struct format_field *bprint_ip_field;
+       struct format_field *bprint_fmt_field;
+       struct format_field *bprint_buf_field;
+
+       struct event_handler *handlers;
+       struct pevent_function_handler *func_handlers;
+
+       /* cache */
+       struct event_format *last_event;
+};
+
+static inline void pevent_set_flag(struct pevent *pevent, int flag)
+{
+       pevent->flags |= flag;
+}
+
+static inline unsigned short
+__data2host2(struct pevent *pevent, unsigned short data)
+{
+       unsigned short swap;
+
+       if (pevent->host_bigendian == pevent->file_bigendian)
+               return data;
+
+       swap = ((data & 0xffULL) << 8) |
+               ((data & (0xffULL << 8)) >> 8);
+
+       return swap;
+}
+
+static inline unsigned int
+__data2host4(struct pevent *pevent, unsigned int data)
+{
+       unsigned int swap;
+
+       if (pevent->host_bigendian == pevent->file_bigendian)
+               return data;
+
+       swap = ((data & 0xffULL) << 24) |
+               ((data & (0xffULL << 8)) << 8) |
+               ((data & (0xffULL << 16)) >> 8) |
+               ((data & (0xffULL << 24)) >> 24);
+
+       return swap;
+}
+
+static inline unsigned long long
+__data2host8(struct pevent *pevent, unsigned long long data)
+{
+       unsigned long long swap;
+
+       if (pevent->host_bigendian == pevent->file_bigendian)
+               return data;
+
+       swap = ((data & 0xffULL) << 56) |
+               ((data & (0xffULL << 8)) << 40) |
+               ((data & (0xffULL << 16)) << 24) |
+               ((data & (0xffULL << 24)) << 8) |
+               ((data & (0xffULL << 32)) >> 8) |
+               ((data & (0xffULL << 40)) >> 24) |
+               ((data & (0xffULL << 48)) >> 40) |
+               ((data & (0xffULL << 56)) >> 56);
+
+       return swap;
+}
+
+#define data2host2(pevent, ptr)                __data2host2(pevent, *(unsigned short *)(ptr))
+#define data2host4(pevent, ptr)                __data2host4(pevent, *(unsigned int *)(ptr))
+#define data2host8(pevent, ptr)                                        \
+({                                                             \
+       unsigned long long __val;                               \
+                                                               \
+       memcpy(&__val, (ptr), sizeof(unsigned long long));      \
+       __data2host8(pevent, __val);                            \
+})
+
+/* taken from kernel/trace/trace.h */
+enum trace_flag_type {
+       TRACE_FLAG_IRQS_OFF             = 0x01,
+       TRACE_FLAG_IRQS_NOSUPPORT       = 0x02,
+       TRACE_FLAG_NEED_RESCHED         = 0x04,
+       TRACE_FLAG_HARDIRQ              = 0x08,
+       TRACE_FLAG_SOFTIRQ              = 0x10,
+};
+
+int pevent_register_comm(struct pevent *pevent, const char *comm, int pid);
+int pevent_register_function(struct pevent *pevent, char *name,
+                            unsigned long long addr, char *mod);
+int pevent_register_print_string(struct pevent *pevent, char *fmt,
+                                unsigned long long addr);
+int pevent_pid_is_registered(struct pevent *pevent, int pid);
+
+void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
+                       struct pevent_record *record);
+
+int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size,
+                            int long_size);
+
+int pevent_parse_event(struct pevent *pevent, const char *buf,
+                      unsigned long size, const char *sys);
+
+void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
+                          const char *name, struct pevent_record *record,
+                          int *len, int err);
+
+int pevent_get_field_val(struct trace_seq *s, struct event_format *event,
+                        const char *name, struct pevent_record *record,
+                        unsigned long long *val, int err);
+int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event,
+                               const char *name, struct pevent_record *record,
+                               unsigned long long *val, int err);
+int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event,
+                            const char *name, struct pevent_record *record,
+                            unsigned long long *val, int err);
+
+int pevent_print_num_field(struct trace_seq *s, const char *fmt,
+                          struct event_format *event, const char *name,
+                          struct pevent_record *record, int err);
+
+int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name,
+                                 pevent_event_handler_func func, void *context);
+int pevent_register_print_function(struct pevent *pevent,
+                                  pevent_func_handler func,
+                                  enum pevent_func_arg_type ret_type,
+                                  char *name, ...);
+
+struct format_field *pevent_find_common_field(struct event_format *event, const char *name);
+struct format_field *pevent_find_field(struct event_format *event, const char *name);
+struct format_field *pevent_find_any_field(struct event_format *event, const char *name);
+
+const char *pevent_find_function(struct pevent *pevent, unsigned long long addr);
+unsigned long long
+pevent_find_function_address(struct pevent *pevent, unsigned long long addr);
+unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size);
+int pevent_read_number_field(struct format_field *field, const void *data,
+                            unsigned long long *value);
+
+struct event_format *pevent_find_event(struct pevent *pevent, int id);
+
+struct event_format *
+pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name);
+
+void pevent_data_lat_fmt(struct pevent *pevent,
+                        struct trace_seq *s, struct pevent_record *record);
+int pevent_data_type(struct pevent *pevent, struct pevent_record *rec);
+struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
+int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
+const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid);
+void pevent_event_info(struct trace_seq *s, struct event_format *event,
+                      struct pevent_record *record);
+
+struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type);
+struct format_field **pevent_event_common_fields(struct event_format *event);
+struct format_field **pevent_event_fields(struct event_format *event);
+
+static inline int pevent_get_cpus(struct pevent *pevent)
+{
+       return pevent->cpus;
+}
+
+static inline void pevent_set_cpus(struct pevent *pevent, int cpus)
+{
+       pevent->cpus = cpus;
+}
+
+static inline int pevent_get_long_size(struct pevent *pevent)
+{
+       return pevent->long_size;
+}
+
+static inline void pevent_set_long_size(struct pevent *pevent, int long_size)
+{
+       pevent->long_size = long_size;
+}
+
+static inline int pevent_is_file_bigendian(struct pevent *pevent)
+{
+       return pevent->file_bigendian;
+}
+
+static inline void pevent_set_file_bigendian(struct pevent *pevent, int endian)
+{
+       pevent->file_bigendian = endian;
+}
+
+static inline int pevent_is_host_bigendian(struct pevent *pevent)
+{
+       return pevent->host_bigendian;
+}
+
+static inline void pevent_set_host_bigendian(struct pevent *pevent, int endian)
+{
+       pevent->host_bigendian = endian;
+}
+
+static inline int pevent_is_latency_format(struct pevent *pevent)
+{
+       return pevent->latency_format;
+}
+
+static inline void pevent_set_latency_format(struct pevent *pevent, int lat)
+{
+       pevent->latency_format = lat;
+}
+
+struct pevent *pevent_alloc(void);
+void pevent_free(struct pevent *pevent);
+void pevent_ref(struct pevent *pevent);
+void pevent_unref(struct pevent *pevent);
+
+/* access to the internal parser */
+void pevent_buffer_init(const char *buf, unsigned long long size);
+enum event_type pevent_read_token(char **tok);
+void pevent_free_token(char *token);
+int pevent_peek_char(void);
+const char *pevent_get_input_buf(void);
+unsigned long long pevent_get_input_buf_ptr(void);
+
+/* for debugging */
+void pevent_print_funcs(struct pevent *pevent);
+void pevent_print_printk(struct pevent *pevent);
+
+/* ----------------------- filtering ----------------------- */
+
+enum filter_boolean_type {
+       FILTER_FALSE,
+       FILTER_TRUE,
+};
+
+enum filter_op_type {
+       FILTER_OP_AND = 1,
+       FILTER_OP_OR,
+       FILTER_OP_NOT,
+};
+
+enum filter_cmp_type {
+       FILTER_CMP_NONE,
+       FILTER_CMP_EQ,
+       FILTER_CMP_NE,
+       FILTER_CMP_GT,
+       FILTER_CMP_LT,
+       FILTER_CMP_GE,
+       FILTER_CMP_LE,
+       FILTER_CMP_MATCH,
+       FILTER_CMP_NOT_MATCH,
+       FILTER_CMP_REGEX,
+       FILTER_CMP_NOT_REGEX,
+};
+
+enum filter_exp_type {
+       FILTER_EXP_NONE,
+       FILTER_EXP_ADD,
+       FILTER_EXP_SUB,
+       FILTER_EXP_MUL,
+       FILTER_EXP_DIV,
+       FILTER_EXP_MOD,
+       FILTER_EXP_RSHIFT,
+       FILTER_EXP_LSHIFT,
+       FILTER_EXP_AND,
+       FILTER_EXP_OR,
+       FILTER_EXP_XOR,
+       FILTER_EXP_NOT,
+};
+
+enum filter_arg_type {
+       FILTER_ARG_NONE,
+       FILTER_ARG_BOOLEAN,
+       FILTER_ARG_VALUE,
+       FILTER_ARG_FIELD,
+       FILTER_ARG_EXP,
+       FILTER_ARG_OP,
+       FILTER_ARG_NUM,
+       FILTER_ARG_STR,
+};
+
+enum filter_value_type {
+       FILTER_NUMBER,
+       FILTER_STRING,
+       FILTER_CHAR
+};
+
+struct fliter_arg;
+
+struct filter_arg_boolean {
+       enum filter_boolean_type        value;
+};
+
+struct filter_arg_field {
+       struct format_field     *field;
+};
+
+struct filter_arg_value {
+       enum filter_value_type  type;
+       union {
+               char                    *str;
+               unsigned long long      val;
+       };
+};
+
+struct filter_arg_op {
+       enum filter_op_type     type;
+       struct filter_arg       *left;
+       struct filter_arg       *right;
+};
+
+struct filter_arg_exp {
+       enum filter_exp_type    type;
+       struct filter_arg       *left;
+       struct filter_arg       *right;
+};
+
+struct filter_arg_num {
+       enum filter_cmp_type    type;
+       struct filter_arg       *left;
+       struct filter_arg       *right;
+};
+
+struct filter_arg_str {
+       enum filter_cmp_type    type;
+       struct format_field     *field;
+       char                    *val;
+       char                    *buffer;
+       regex_t                 reg;
+};
+
+struct filter_arg {
+       enum filter_arg_type    type;
+       union {
+               struct filter_arg_boolean       boolean;
+               struct filter_arg_field         field;
+               struct filter_arg_value         value;
+               struct filter_arg_op            op;
+               struct filter_arg_exp           exp;
+               struct filter_arg_num           num;
+               struct filter_arg_str           str;
+       };
+};
+
+struct filter_type {
+       int                     event_id;
+       struct event_format     *event;
+       struct filter_arg       *filter;
+};
+
+struct event_filter {
+       struct pevent           *pevent;
+       int                     filters;
+       struct filter_type      *event_filters;
+};
+
+struct event_filter *pevent_filter_alloc(struct pevent *pevent);
+
+#define FILTER_NONE            -2
+#define FILTER_NOEXIST         -1
+#define FILTER_MISS            0
+#define FILTER_MATCH           1
+
+enum filter_trivial_type {
+       FILTER_TRIVIAL_FALSE,
+       FILTER_TRIVIAL_TRUE,
+       FILTER_TRIVIAL_BOTH,
+};
+
+int pevent_filter_add_filter_str(struct event_filter *filter,
+                                const char *filter_str,
+                                char **error_str);
+
+
+int pevent_filter_match(struct event_filter *filter,
+                       struct pevent_record *record);
+
+int pevent_event_filtered(struct event_filter *filter,
+                         int event_id);
+
+void pevent_filter_reset(struct event_filter *filter);
+
+void pevent_filter_clear_trivial(struct event_filter *filter,
+                                enum filter_trivial_type type);
+
+void pevent_filter_free(struct event_filter *filter);
+
+char *pevent_filter_make_string(struct event_filter *filter, int event_id);
+
+int pevent_filter_remove_event(struct event_filter *filter,
+                              int event_id);
+
+int pevent_filter_event_has_trivial(struct event_filter *filter,
+                                   int event_id,
+                                   enum filter_trivial_type type);
+
+int pevent_filter_copy(struct event_filter *dest, struct event_filter *source);
+
+int pevent_update_trivial(struct event_filter *dest, struct event_filter *source,
+                         enum filter_trivial_type type);
+
+int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2);
+
+#endif /* _PARSE_EVENTS_H */
diff --git a/tools/lib/traceevent/event-utils.h b/tools/lib/traceevent/event-utils.h
new file mode 100644 (file)
index 0000000..0829638
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef __UTIL_H
+#define __UTIL_H
+
+#include <ctype.h>
+
+/* Can be overridden */
+void die(const char *fmt, ...);
+void *malloc_or_die(unsigned int size);
+void warning(const char *fmt, ...);
+void pr_stat(const char *fmt, ...);
+void vpr_stat(const char *fmt, va_list ap);
+
+/* Always available */
+void __die(const char *fmt, ...);
+void __warning(const char *fmt, ...);
+void __pr_stat(const char *fmt, ...);
+
+void __vdie(const char *fmt, ...);
+void __vwarning(const char *fmt, ...);
+void __vpr_stat(const char *fmt, ...);
+
+static inline char *strim(char *string)
+{
+       char *ret;
+
+       if (!string)
+               return NULL;
+       while (*string) {
+               if (!isspace(*string))
+                       break;
+               string++;
+       }
+       ret = string;
+
+       string = ret + strlen(ret) - 1;
+       while (string > ret) {
+               if (!isspace(*string))
+                       break;
+               string--;
+       }
+       string[1] = 0;
+
+       return ret;
+}
+
+static inline int has_text(const char *text)
+{
+       if (!text)
+               return 0;
+
+       while (*text) {
+               if (!isspace(*text))
+                       return 1;
+               text++;
+       }
+
+       return 0;
+}
+
+#endif
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c
new file mode 100644 (file)
index 0000000..2d40c5e
--- /dev/null
@@ -0,0 +1,2262 @@
+/*
+ * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include "event-parse.h"
+#include "event-utils.h"
+
+#define COMM "COMM"
+
+static struct format_field comm = {
+       .name = "COMM",
+};
+
+struct event_list {
+       struct event_list       *next;
+       struct event_format     *event;
+};
+
+#define MAX_ERR_STR_SIZE 256
+
+static void show_error(char **error_str, const char *fmt, ...)
+{
+       unsigned long long index;
+       const char *input;
+       char *error;
+       va_list ap;
+       int len;
+       int i;
+
+       if (!error_str)
+               return;
+
+       input = pevent_get_input_buf();
+       index = pevent_get_input_buf_ptr();
+       len = input ? strlen(input) : 0;
+
+       error = malloc_or_die(MAX_ERR_STR_SIZE + (len*2) + 3);
+
+       if (len) {
+               strcpy(error, input);
+               error[len] = '\n';
+               for (i = 1; i < len && i < index; i++)
+                       error[len+i] = ' ';
+               error[len + i] = '^';
+               error[len + i + 1] = '\n';
+               len += i+2;
+       }
+
+       va_start(ap, fmt);
+       vsnprintf(error + len, MAX_ERR_STR_SIZE, fmt, ap);
+       va_end(ap);
+
+       *error_str = error;
+}
+
+static void free_token(char *token)
+{
+       pevent_free_token(token);
+}
+
+static enum event_type read_token(char **tok)
+{
+       enum event_type type;
+       char *token = NULL;
+
+       do {
+               free_token(token);
+               type = pevent_read_token(&token);
+       } while (type == EVENT_NEWLINE || type == EVENT_SPACE);
+
+       /* If token is = or ! check to see if the next char is ~ */
+       if (token &&
+           (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) &&
+           pevent_peek_char() == '~') {
+               /* append it */
+               *tok = malloc(3);
+               sprintf(*tok, "%c%c", *token, '~');
+               free_token(token);
+               /* Now remove the '~' from the buffer */
+               pevent_read_token(&token);
+               free_token(token);
+       } else
+               *tok = token;
+
+       return type;
+}
+
+static int filter_cmp(const void *a, const void *b)
+{
+       const struct filter_type *ea = a;
+       const struct filter_type *eb = b;
+
+       if (ea->event_id < eb->event_id)
+               return -1;
+
+       if (ea->event_id > eb->event_id)
+               return 1;
+
+       return 0;
+}
+
+static struct filter_type *
+find_filter_type(struct event_filter *filter, int id)
+{
+       struct filter_type *filter_type;
+       struct filter_type key;
+
+       key.event_id = id;
+
+       filter_type = bsearch(&key, filter->event_filters,
+                             filter->filters,
+                             sizeof(*filter->event_filters),
+                             filter_cmp);
+
+       return filter_type;
+}
+
+static struct filter_type *
+add_filter_type(struct event_filter *filter, int id)
+{
+       struct filter_type *filter_type;
+       int i;
+
+       filter_type = find_filter_type(filter, id);
+       if (filter_type)
+               return filter_type;
+
+       if (!filter->filters)
+               filter->event_filters =
+                       malloc_or_die(sizeof(*filter->event_filters));
+       else {
+               filter->event_filters =
+                       realloc(filter->event_filters,
+                               sizeof(*filter->event_filters) *
+                               (filter->filters + 1));
+               if (!filter->event_filters)
+                       die("Could not allocate filter");
+       }
+
+       for (i = 0; i < filter->filters; i++) {
+               if (filter->event_filters[i].event_id > id)
+                       break;
+       }
+
+       if (i < filter->filters)
+               memmove(&filter->event_filters[i+1],
+                       &filter->event_filters[i],
+                       sizeof(*filter->event_filters) *
+                       (filter->filters - i));
+
+       filter_type = &filter->event_filters[i];
+       filter_type->event_id = id;
+       filter_type->event = pevent_find_event(filter->pevent, id);
+       filter_type->filter = NULL;
+
+       filter->filters++;
+
+       return filter_type;
+}
+
+/**
+ * pevent_filter_alloc - create a new event filter
+ * @pevent: The pevent that this filter is associated with
+ */
+struct event_filter *pevent_filter_alloc(struct pevent *pevent)
+{
+       struct event_filter *filter;
+
+       filter = malloc_or_die(sizeof(*filter));
+       memset(filter, 0, sizeof(*filter));
+       filter->pevent = pevent;
+       pevent_ref(pevent);
+
+       return filter;
+}
+
+static struct filter_arg *allocate_arg(void)
+{
+       struct filter_arg *arg;
+
+       arg = malloc_or_die(sizeof(*arg));
+       memset(arg, 0, sizeof(*arg));
+
+       return arg;
+}
+
+static void free_arg(struct filter_arg *arg)
+{
+       if (!arg)
+               return;
+
+       switch (arg->type) {
+       case FILTER_ARG_NONE:
+       case FILTER_ARG_BOOLEAN:
+       case FILTER_ARG_NUM:
+               break;
+
+       case FILTER_ARG_STR:
+               free(arg->str.val);
+               regfree(&arg->str.reg);
+               free(arg->str.buffer);
+               break;
+
+       case FILTER_ARG_OP:
+               free_arg(arg->op.left);
+               free_arg(arg->op.right);
+       default:
+               break;
+       }
+
+       free(arg);
+}
+
+static void add_event(struct event_list **events,
+                     struct event_format *event)
+{
+       struct event_list *list;
+
+       list = malloc_or_die(sizeof(*list));
+       list->next = *events;
+       *events = list;
+       list->event = event;
+}
+
+static int event_match(struct event_format *event,
+                      regex_t *sreg, regex_t *ereg)
+{
+       if (sreg) {
+               return !regexec(sreg, event->system, 0, NULL, 0) &&
+                       !regexec(ereg, event->name, 0, NULL, 0);
+       }
+
+       return !regexec(ereg, event->system, 0, NULL, 0) ||
+               !regexec(ereg, event->name, 0, NULL, 0);
+}
+
+static int
+find_event(struct pevent *pevent, struct event_list **events,
+          char *sys_name, char *event_name)
+{
+       struct event_format *event;
+       regex_t ereg;
+       regex_t sreg;
+       int match = 0;
+       char *reg;
+       int ret;
+       int i;
+
+       if (!event_name) {
+               /* if no name is given, then swap sys and name */
+               event_name = sys_name;
+               sys_name = NULL;
+       }
+
+       reg = malloc_or_die(strlen(event_name) + 3);
+       sprintf(reg, "^%s$", event_name);
+
+       ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB);
+       free(reg);
+
+       if (ret)
+               return -1;
+
+       if (sys_name) {
+               reg = malloc_or_die(strlen(sys_name) + 3);
+               sprintf(reg, "^%s$", sys_name);
+               ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB);
+               free(reg);
+               if (ret) {
+                       regfree(&ereg);
+                       return -1;
+               }
+       }
+
+       for (i = 0; i < pevent->nr_events; i++) {
+               event = pevent->events[i];
+               if (event_match(event, sys_name ? &sreg : NULL, &ereg)) {
+                       match = 1;
+                       add_event(events, event);
+               }
+       }
+
+       regfree(&ereg);
+       if (sys_name)
+               regfree(&sreg);
+
+       if (!match)
+               return -1;
+
+       return 0;
+}
+
+static void free_events(struct event_list *events)
+{
+       struct event_list *event;
+
+       while (events) {
+               event = events;
+               events = events->next;
+               free(event);
+       }
+}
+
+static struct filter_arg *
+create_arg_item(struct event_format *event,
+               const char *token, enum filter_arg_type type,
+               char **error_str)
+{
+       struct format_field *field;
+       struct filter_arg *arg;
+
+       arg = allocate_arg();
+
+       switch (type) {
+
+       case EVENT_SQUOTE:
+       case EVENT_DQUOTE:
+               arg->type = FILTER_ARG_VALUE;
+               arg->value.type =
+                       type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR;
+               arg->value.str = strdup(token);
+               if (!arg->value.str)
+                       die("malloc string");
+               break;
+       case EVENT_ITEM:
+               /* if it is a number, then convert it */
+               if (isdigit(token[0])) {
+                       arg->type = FILTER_ARG_VALUE;
+                       arg->value.type = FILTER_NUMBER;
+                       arg->value.val = strtoull(token, NULL, 0);
+                       break;
+               }
+               /* Consider this a field */
+               field = pevent_find_any_field(event, token);
+               if (!field) {
+                       if (strcmp(token, COMM) != 0) {
+                               /* not a field, Make it false */
+                               arg->type = FILTER_ARG_BOOLEAN;
+                               arg->boolean.value = FILTER_FALSE;
+                               break;
+                       }
+                       /* If token is 'COMM' then it is special */
+                       field = &comm;
+               }
+               arg->type = FILTER_ARG_FIELD;
+               arg->field.field = field;
+               break;
+       default:
+               free_arg(arg);
+               show_error(error_str, "expected a value but found %s",
+                          token);
+               return NULL;
+       }
+       return arg;
+}
+
+static struct filter_arg *
+create_arg_op(enum filter_op_type btype)
+{
+       struct filter_arg *arg;
+
+       arg = allocate_arg();
+       arg->type = FILTER_ARG_OP;
+       arg->op.type = btype;
+
+       return arg;
+}
+
+static struct filter_arg *
+create_arg_exp(enum filter_exp_type etype)
+{
+       struct filter_arg *arg;
+
+       arg = allocate_arg();
+       arg->type = FILTER_ARG_EXP;
+       arg->op.type = etype;
+
+       return arg;
+}
+
+static struct filter_arg *
+create_arg_cmp(enum filter_exp_type etype)
+{
+       struct filter_arg *arg;
+
+       arg = allocate_arg();
+       /* Use NUM and change if necessary */
+       arg->type = FILTER_ARG_NUM;
+       arg->op.type = etype;
+
+       return arg;
+}
+
+static int add_right(struct filter_arg *op, struct filter_arg *arg,
+                    char **error_str)
+{
+       struct filter_arg *left;
+       char *str;
+       int op_type;
+       int ret;
+
+       switch (op->type) {
+       case FILTER_ARG_EXP:
+               if (op->exp.right)
+                       goto out_fail;
+               op->exp.right = arg;
+               break;
+
+       case FILTER_ARG_OP:
+               if (op->op.right)
+                       goto out_fail;
+               op->op.right = arg;
+               break;
+
+       case FILTER_ARG_NUM:
+               if (op->op.right)
+                       goto out_fail;
+               /*
+                * The arg must be num, str, or field
+                */
+               switch (arg->type) {
+               case FILTER_ARG_VALUE:
+               case FILTER_ARG_FIELD:
+                       break;
+               default:
+                       show_error(error_str,
+                                  "Illegal rvalue");
+                       return -1;
+               }
+
+               /*
+                * Depending on the type, we may need to
+                * convert this to a string or regex.
+                */
+               switch (arg->value.type) {
+               case FILTER_CHAR:
+                       /*
+                        * A char should be converted to number if
+                        * the string is 1 byte, and the compare
+                        * is not a REGEX.
+                        */
+                       if (strlen(arg->value.str) == 1 &&
+                           op->num.type != FILTER_CMP_REGEX &&
+                           op->num.type != FILTER_CMP_NOT_REGEX) {
+                               arg->value.type = FILTER_NUMBER;
+                               goto do_int;
+                       }
+                       /* fall through */
+               case FILTER_STRING:
+
+                       /* convert op to a string arg */
+                       op_type = op->num.type;
+                       left = op->num.left;
+                       str = arg->value.str;
+
+                       /* reset the op for the new field */
+                       memset(op, 0, sizeof(*op));
+
+                       /*
+                        * If left arg was a field not found then
+                        * NULL the entire op.
+                        */
+                       if (left->type == FILTER_ARG_BOOLEAN) {
+                               free_arg(left);
+                               free_arg(arg);
+                               op->type = FILTER_ARG_BOOLEAN;
+                               op->boolean.value = FILTER_FALSE;
+                               break;
+                       }
+
+                       /* Left arg must be a field */
+                       if (left->type != FILTER_ARG_FIELD) {
+                               show_error(error_str,
+                                          "Illegal lvalue for string comparison");
+                               return -1;
+                       }
+
+                       /* Make sure this is a valid string compare */
+                       switch (op_type) {
+                       case FILTER_CMP_EQ:
+                               op_type = FILTER_CMP_MATCH;
+                               break;
+                       case FILTER_CMP_NE:
+                               op_type = FILTER_CMP_NOT_MATCH;
+                               break;
+
+                       case FILTER_CMP_REGEX:
+                       case FILTER_CMP_NOT_REGEX:
+                               ret = regcomp(&op->str.reg, str, REG_ICASE|REG_NOSUB);
+                               if (ret) {
+                                       show_error(error_str,
+                                                  "RegEx '%s' did not compute",
+                                                  str);
+                                       return -1;
+                               }
+                               break;
+                       default:
+                               show_error(error_str,
+                                          "Illegal comparison for string");
+                               return -1;
+                       }
+
+                       op->type = FILTER_ARG_STR;
+                       op->str.type = op_type;
+                       op->str.field = left->field.field;
+                       op->str.val = strdup(str);
+                       if (!op->str.val)
+                               die("malloc string");
+                       /*
+                        * Need a buffer to copy data for tests
+                        */
+                       op->str.buffer = malloc_or_die(op->str.field->size + 1);
+                       /* Null terminate this buffer */
+                       op->str.buffer[op->str.field->size] = 0;
+
+                       /* We no longer have left or right args */
+                       free_arg(arg);
+                       free_arg(left);
+
+                       break;
+
+               case FILTER_NUMBER:
+
+ do_int:
+                       switch (op->num.type) {
+                       case FILTER_CMP_REGEX:
+                       case FILTER_CMP_NOT_REGEX:
+                               show_error(error_str,
+                                          "Op not allowed with integers");
+                               return -1;
+
+                       default:
+                               break;
+                       }
+
+                       /* numeric compare */
+                       op->num.right = arg;
+                       break;
+               default:
+                       goto out_fail;
+               }
+               break;
+       default:
+               goto out_fail;
+       }
+
+       return 0;
+
+ out_fail:
+       show_error(error_str,
+                  "Syntax error");
+       return -1;
+}
+
+static struct filter_arg *
+rotate_op_right(struct filter_arg *a, struct filter_arg *b)
+{
+       struct filter_arg *arg;
+
+       arg = a->op.right;
+       a->op.right = b;
+       return arg;
+}
+
+static int add_left(struct filter_arg *op, struct filter_arg *arg)
+{
+       switch (op->type) {
+       case FILTER_ARG_EXP:
+               if (arg->type == FILTER_ARG_OP)
+                       arg = rotate_op_right(arg, op);
+               op->exp.left = arg;
+               break;
+
+       case FILTER_ARG_OP:
+               op->op.left = arg;
+               break;
+       case FILTER_ARG_NUM:
+               if (arg->type == FILTER_ARG_OP)
+                       arg = rotate_op_right(arg, op);
+
+               /* left arg of compares must be a field */
+               if (arg->type != FILTER_ARG_FIELD &&
+                   arg->type != FILTER_ARG_BOOLEAN)
+                       return -1;
+               op->num.left = arg;
+               break;
+       default:
+               return -1;
+       }
+       return 0;
+}
+
+enum op_type {
+       OP_NONE,
+       OP_BOOL,
+       OP_NOT,
+       OP_EXP,
+       OP_CMP,
+};
+
+static enum op_type process_op(const char *token,
+                              enum filter_op_type *btype,
+                              enum filter_cmp_type *ctype,
+                              enum filter_exp_type *etype)
+{
+       *btype = FILTER_OP_NOT;
+       *etype = FILTER_EXP_NONE;
+       *ctype = FILTER_CMP_NONE;
+
+       if (strcmp(token, "&&") == 0)
+               *btype = FILTER_OP_AND;
+       else if (strcmp(token, "||") == 0)
+               *btype = FILTER_OP_OR;
+       else if (strcmp(token, "!") == 0)
+               return OP_NOT;
+
+       if (*btype != FILTER_OP_NOT)
+               return OP_BOOL;
+
+       /* Check for value expressions */
+       if (strcmp(token, "+") == 0) {
+               *etype = FILTER_EXP_ADD;
+       } else if (strcmp(token, "-") == 0) {
+               *etype = FILTER_EXP_SUB;
+       } else if (strcmp(token, "*") == 0) {
+               *etype = FILTER_EXP_MUL;
+       } else if (strcmp(token, "/") == 0) {
+               *etype = FILTER_EXP_DIV;
+       } else if (strcmp(token, "%") == 0) {
+               *etype = FILTER_EXP_MOD;
+       } else if (strcmp(token, ">>") == 0) {
+               *etype = FILTER_EXP_RSHIFT;
+       } else if (strcmp(token, "<<") == 0) {
+               *etype = FILTER_EXP_LSHIFT;
+       } else if (strcmp(token, "&") == 0) {
+               *etype = FILTER_EXP_AND;
+       } else if (strcmp(token, "|") == 0) {
+               *etype = FILTER_EXP_OR;
+       } else if (strcmp(token, "^") == 0) {
+               *etype = FILTER_EXP_XOR;
+       } else if (strcmp(token, "~") == 0)
+               *etype = FILTER_EXP_NOT;
+
+       if (*etype != FILTER_EXP_NONE)
+               return OP_EXP;
+
+       /* Check for compares */
+       if (strcmp(token, "==") == 0)
+               *ctype = FILTER_CMP_EQ;
+       else if (strcmp(token, "!=") == 0)
+               *ctype = FILTER_CMP_NE;
+       else if (strcmp(token, "<") == 0)
+               *ctype = FILTER_CMP_LT;
+       else if (strcmp(token, ">") == 0)
+               *ctype = FILTER_CMP_GT;
+       else if (strcmp(token, "<=") == 0)
+               *ctype = FILTER_CMP_LE;
+       else if (strcmp(token, ">=") == 0)
+               *ctype = FILTER_CMP_GE;
+       else if (strcmp(token, "=~") == 0)
+               *ctype = FILTER_CMP_REGEX;
+       else if (strcmp(token, "!~") == 0)
+               *ctype = FILTER_CMP_NOT_REGEX;
+       else
+               return OP_NONE;
+
+       return OP_CMP;
+}
+
+static int check_op_done(struct filter_arg *arg)
+{
+       switch (arg->type) {
+       case FILTER_ARG_EXP:
+               return arg->exp.right != NULL;
+
+       case FILTER_ARG_OP:
+               return arg->op.right != NULL;
+
+       case FILTER_ARG_NUM:
+               return arg->num.right != NULL;
+
+       case FILTER_ARG_STR:
+               /* A string conversion is always done */
+               return 1;
+
+       case FILTER_ARG_BOOLEAN:
+               /* field not found, is ok */
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+enum filter_vals {
+       FILTER_VAL_NORM,
+       FILTER_VAL_FALSE,
+       FILTER_VAL_TRUE,
+};
+
+void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child,
+                 struct filter_arg *arg)
+{
+       struct filter_arg *other_child;
+       struct filter_arg **ptr;
+
+       if (parent->type != FILTER_ARG_OP &&
+           arg->type != FILTER_ARG_OP)
+               die("can not reparent other than OP");
+
+       /* Get the sibling */
+       if (old_child->op.right == arg) {
+               ptr = &old_child->op.right;
+               other_child = old_child->op.left;
+       } else if (old_child->op.left == arg) {
+               ptr = &old_child->op.left;
+               other_child = old_child->op.right;
+       } else
+               die("Error in reparent op, find other child");
+
+       /* Detach arg from old_child */
+       *ptr = NULL;
+
+       /* Check for root */
+       if (parent == old_child) {
+               free_arg(other_child);
+               *parent = *arg;
+               /* Free arg without recussion */
+               free(arg);
+               return;
+       }
+
+       if (parent->op.right == old_child)
+               ptr = &parent->op.right;
+       else if (parent->op.left == old_child)
+               ptr = &parent->op.left;
+       else
+               die("Error in reparent op");
+       *ptr = arg;
+
+       free_arg(old_child);
+}
+
+enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg)
+{
+       enum filter_vals lval, rval;
+
+       switch (arg->type) {
+
+               /* bad case */
+       case FILTER_ARG_BOOLEAN:
+               return FILTER_VAL_FALSE + arg->boolean.value;
+
+               /* good cases: */
+       case FILTER_ARG_STR:
+       case FILTER_ARG_VALUE:
+       case FILTER_ARG_FIELD:
+               return FILTER_VAL_NORM;
+
+       case FILTER_ARG_EXP:
+               lval = test_arg(arg, arg->exp.left);
+               if (lval != FILTER_VAL_NORM)
+                       return lval;
+               rval = test_arg(arg, arg->exp.right);
+               if (rval != FILTER_VAL_NORM)
+                       return rval;
+               return FILTER_VAL_NORM;
+
+       case FILTER_ARG_NUM:
+               lval = test_arg(arg, arg->num.left);
+               if (lval != FILTER_VAL_NORM)
+                       return lval;
+               rval = test_arg(arg, arg->num.right);
+               if (rval != FILTER_VAL_NORM)
+                       return rval;
+               return FILTER_VAL_NORM;
+
+       case FILTER_ARG_OP:
+               if (arg->op.type != FILTER_OP_NOT) {
+                       lval = test_arg(arg, arg->op.left);
+                       switch (lval) {
+                       case FILTER_VAL_NORM:
+                               break;
+                       case FILTER_VAL_TRUE:
+                               if (arg->op.type == FILTER_OP_OR)
+                                       return FILTER_VAL_TRUE;
+                               rval = test_arg(arg, arg->op.right);
+                               if (rval != FILTER_VAL_NORM)
+                                       return rval;
+
+                               reparent_op_arg(parent, arg, arg->op.right);
+                               return FILTER_VAL_NORM;
+
+                       case FILTER_VAL_FALSE:
+                               if (arg->op.type == FILTER_OP_AND)
+                                       return FILTER_VAL_FALSE;
+                               rval = test_arg(arg, arg->op.right);
+                               if (rval != FILTER_VAL_NORM)
+                                       return rval;
+
+                               reparent_op_arg(parent, arg, arg->op.right);
+                               return FILTER_VAL_NORM;
+                       }
+               }
+
+               rval = test_arg(arg, arg->op.right);
+               switch (rval) {
+               case FILTER_VAL_NORM:
+                       break;
+               case FILTER_VAL_TRUE:
+                       if (arg->op.type == FILTER_OP_OR)
+                               return FILTER_VAL_TRUE;
+                       if (arg->op.type == FILTER_OP_NOT)
+                               return FILTER_VAL_FALSE;
+
+                       reparent_op_arg(parent, arg, arg->op.left);
+                       return FILTER_VAL_NORM;
+
+               case FILTER_VAL_FALSE:
+                       if (arg->op.type == FILTER_OP_AND)
+                               return FILTER_VAL_FALSE;
+                       if (arg->op.type == FILTER_OP_NOT)
+                               return FILTER_VAL_TRUE;
+
+                       reparent_op_arg(parent, arg, arg->op.left);
+                       return FILTER_VAL_NORM;
+               }
+
+               return FILTER_VAL_NORM;
+       default:
+               die("bad arg in filter tree");
+       }
+       return FILTER_VAL_NORM;
+}
+
+/* Remove any unknown event fields */
+static struct filter_arg *collapse_tree(struct filter_arg *arg)
+{
+       enum filter_vals ret;
+
+       ret = test_arg(arg, arg);
+       switch (ret) {
+       case FILTER_VAL_NORM:
+               return arg;
+
+       case FILTER_VAL_TRUE:
+       case FILTER_VAL_FALSE:
+               free_arg(arg);
+               arg = allocate_arg();
+               arg->type = FILTER_ARG_BOOLEAN;
+               arg->boolean.value = ret == FILTER_VAL_TRUE;
+       }
+
+       return arg;
+}
+
+static int
+process_filter(struct event_format *event, struct filter_arg **parg,
+              char **error_str, int not)
+{
+       enum event_type type;
+       char *token = NULL;
+       struct filter_arg *current_op = NULL;
+       struct filter_arg *current_exp = NULL;
+       struct filter_arg *left_item = NULL;
+       struct filter_arg *arg = NULL;
+       enum op_type op_type;
+       enum filter_op_type btype;
+       enum filter_exp_type etype;
+       enum filter_cmp_type ctype;
+       int ret;
+
+       *parg = NULL;
+
+       do {
+               free(token);
+               type = read_token(&token);
+               switch (type) {
+               case EVENT_SQUOTE:
+               case EVENT_DQUOTE:
+               case EVENT_ITEM:
+                       arg = create_arg_item(event, token, type, error_str);
+                       if (!arg)
+                               goto fail;
+                       if (!left_item)
+                               left_item = arg;
+                       else if (current_exp) {
+                               ret = add_right(current_exp, arg, error_str);
+                               if (ret < 0)
+                                       goto fail;
+                               left_item = NULL;
+                               /* Not's only one one expression */
+                               if (not) {
+                                       arg = NULL;
+                                       if (current_op)
+                                               goto fail_print;
+                                       free(token);
+                                       *parg = current_exp;
+                                       return 0;
+                               }
+                       } else
+                               goto fail_print;
+                       arg = NULL;
+                       break;
+
+               case EVENT_DELIM:
+                       if (*token == ',') {
+                               show_error(error_str,
+                                          "Illegal token ','");
+                               goto fail;
+                       }
+
+                       if (*token == '(') {
+                               if (left_item) {
+                                       show_error(error_str,
+                                                  "Open paren can not come after item");
+                                       goto fail;
+                               }
+                               if (current_exp) {
+                                       show_error(error_str,
+                                                  "Open paren can not come after expression");
+                                       goto fail;
+                               }
+
+                               ret = process_filter(event, &arg, error_str, 0);
+                               if (ret != 1) {
+                                       if (ret == 0)
+                                               show_error(error_str,
+                                                          "Unbalanced number of '('");
+                                       goto fail;
+                               }
+                               ret = 0;
+
+                               /* A not wants just one expression */
+                               if (not) {
+                                       if (current_op)
+                                               goto fail_print;
+                                       *parg = arg;
+                                       return 0;
+                               }
+
+                               if (current_op)
+                                       ret = add_right(current_op, arg, error_str);
+                               else
+                                       current_exp = arg;
+
+                               if (ret < 0)
+                                       goto fail;
+
+                       } else { /* ')' */
+                               if (!current_op && !current_exp)
+                                       goto fail_print;
+
+                               /* Make sure everything is finished at this level */
+                               if (current_exp && !check_op_done(current_exp))
+                                       goto fail_print;
+                               if (current_op && !check_op_done(current_op))
+                                       goto fail_print;
+
+                               if (current_op)
+                                       *parg = current_op;
+                               else
+                                       *parg = current_exp;
+                               return 1;
+                       }
+                       break;
+
+               case EVENT_OP:
+                       op_type = process_op(token, &btype, &ctype, &etype);
+
+                       /* All expect a left arg except for NOT */
+                       switch (op_type) {
+                       case OP_BOOL:
+                               /* Logic ops need a left expression */
+                               if (!current_exp && !current_op)
+                                       goto fail_print;
+                               /* fall through */
+                       case OP_NOT:
+                               /* logic only processes ops and exp */
+                               if (left_item)
+                                       goto fail_print;
+                               break;
+                       case OP_EXP:
+                       case OP_CMP:
+                               if (!left_item)
+                                       goto fail_print;
+                               break;
+                       case OP_NONE:
+                               show_error(error_str,
+                                          "Unknown op token %s", token);
+                               goto fail;
+                       }
+
+                       ret = 0;
+                       switch (op_type) {
+                       case OP_BOOL:
+                               arg = create_arg_op(btype);
+                               if (current_op)
+                                       ret = add_left(arg, current_op);
+                               else
+                                       ret = add_left(arg, current_exp);
+                               current_op = arg;
+                               current_exp = NULL;
+                               break;
+
+                       case OP_NOT:
+                               arg = create_arg_op(btype);
+                               if (current_op)
+                                       ret = add_right(current_op, arg, error_str);
+                               if (ret < 0)
+                                       goto fail;
+                               current_exp = arg;
+                               ret = process_filter(event, &arg, error_str, 1);
+                               if (ret < 0)
+                                       goto fail;
+                               ret = add_right(current_exp, arg, error_str);
+                               if (ret < 0)
+                                       goto fail;
+                               break;
+
+                       case OP_EXP:
+                       case OP_CMP:
+                               if (op_type == OP_EXP)
+                                       arg = create_arg_exp(etype);
+                               else
+                                       arg = create_arg_cmp(ctype);
+
+                               if (current_op)
+                                       ret = add_right(current_op, arg, error_str);
+                               if (ret < 0)
+                                       goto fail;
+                               ret = add_left(arg, left_item);
+                               if (ret < 0) {
+                                       arg = NULL;
+                                       goto fail_print;
+                               }
+                               current_exp = arg;
+                               break;
+                       default:
+                               break;
+                       }
+                       arg = NULL;
+                       if (ret < 0)
+                               goto fail_print;
+                       break;
+               case EVENT_NONE:
+                       break;
+               default:
+                       goto fail_print;
+               }
+       } while (type != EVENT_NONE);
+
+       if (!current_op && !current_exp)
+               goto fail_print;
+
+       if (!current_op)
+               current_op = current_exp;
+
+       current_op = collapse_tree(current_op);
+
+       *parg = current_op;
+
+       return 0;
+
+ fail_print:
+       show_error(error_str, "Syntax error");
+ fail:
+       free_arg(current_op);
+       free_arg(current_exp);
+       free_arg(arg);
+       free(token);
+       return -1;
+}
+
+static int
+process_event(struct event_format *event, const char *filter_str,
+             struct filter_arg **parg, char **error_str)
+{
+       int ret;
+
+       pevent_buffer_init(filter_str, strlen(filter_str));
+
+       ret = process_filter(event, parg, error_str, 0);
+       if (ret == 1) {
+               show_error(error_str,
+                          "Unbalanced number of ')'");
+               return -1;
+       }
+       if (ret < 0)
+               return ret;
+
+       /* If parg is NULL, then make it into FALSE */
+       if (!*parg) {
+               *parg = allocate_arg();
+               (*parg)->type = FILTER_ARG_BOOLEAN;
+               (*parg)->boolean.value = FILTER_FALSE;
+       }
+
+       return 0;
+}
+
+static int filter_event(struct event_filter *filter,
+                       struct event_format *event,
+                       const char *filter_str, char **error_str)
+{
+       struct filter_type *filter_type;
+       struct filter_arg *arg;
+       int ret;
+
+       if (filter_str) {
+               ret = process_event(event, filter_str, &arg, error_str);
+               if (ret < 0)
+                       return ret;
+
+       } else {
+               /* just add a TRUE arg */
+               arg = allocate_arg();
+               arg->type = FILTER_ARG_BOOLEAN;
+               arg->boolean.value = FILTER_TRUE;
+       }
+
+       filter_type = add_filter_type(filter, event->id);
+       if (filter_type->filter)
+               free_arg(filter_type->filter);
+       filter_type->filter = arg;
+
+       return 0;
+}
+
+/**
+ * pevent_filter_add_filter_str - add a new filter
+ * @filter: the event filter to add to
+ * @filter_str: the filter string that contains the filter
+ * @error_str: string containing reason for failed filter
+ *
+ * Returns 0 if the filter was successfully added
+ *   -1 if there was an error.
+ *
+ * On error, if @error_str points to a string pointer,
+ * it is set to the reason that the filter failed.
+ * This string must be freed with "free".
+ */
+int pevent_filter_add_filter_str(struct event_filter *filter,
+                                const char *filter_str,
+                                char **error_str)
+{
+       struct pevent *pevent = filter->pevent;
+       struct event_list *event;
+       struct event_list *events = NULL;
+       const char *filter_start;
+       const char *next_event;
+       char *this_event;
+       char *event_name = NULL;
+       char *sys_name = NULL;
+       char *sp;
+       int rtn = 0;
+       int len;
+       int ret;
+
+       /* clear buffer to reset show error */
+       pevent_buffer_init("", 0);
+
+       if (error_str)
+               *error_str = NULL;
+
+       filter_start = strchr(filter_str, ':');
+       if (filter_start)
+               len = filter_start - filter_str;
+       else
+               len = strlen(filter_str);
+
+
+       do {
+               next_event = strchr(filter_str, ',');
+               if (next_event &&
+                   (!filter_start || next_event < filter_start))
+                       len = next_event - filter_str;
+               else if (filter_start)
+                       len = filter_start - filter_str;
+               else
+                       len = strlen(filter_str);
+
+               this_event = malloc_or_die(len + 1);
+               memcpy(this_event, filter_str, len);
+               this_event[len] = 0;
+
+               if (next_event)
+                       next_event++;
+
+               filter_str = next_event;
+
+               sys_name = strtok_r(this_event, "/", &sp);
+               event_name = strtok_r(NULL, "/", &sp);
+
+               if (!sys_name) {
+                       show_error(error_str, "No filter found");
+                       /* This can only happen when events is NULL, but still */
+                       free_events(events);
+                       free(this_event);
+                       return -1;
+               }
+
+               /* Find this event */
+               ret = find_event(pevent, &events, strim(sys_name), strim(event_name));
+               if (ret < 0) {
+                       if (event_name)
+                               show_error(error_str,
+                                          "No event found under '%s.%s'",
+                                          sys_name, event_name);
+                       else
+                               show_error(error_str,
+                                          "No event found under '%s'",
+                                          sys_name);
+                       free_events(events);
+                       free(this_event);
+                       return -1;
+               }
+               free(this_event);
+       } while (filter_str);
+
+       /* Skip the ':' */
+       if (filter_start)
+               filter_start++;
+
+       /* filter starts here */
+       for (event = events; event; event = event->next) {
+               ret = filter_event(filter, event->event, filter_start,
+                                  error_str);
+               /* Failures are returned if a parse error happened */
+               if (ret < 0)
+                       rtn = ret;
+
+               if (ret >= 0 && pevent->test_filters) {
+                       char *test;
+                       test = pevent_filter_make_string(filter, event->event->id);
+                       printf(" '%s: %s'\n", event->event->name, test);
+                       free(test);
+               }
+       }
+
+       free_events(events);
+
+       if (rtn >= 0 && pevent->test_filters)
+               exit(0);
+
+       return rtn;
+}
+
+static void free_filter_type(struct filter_type *filter_type)
+{
+       free_arg(filter_type->filter);
+}
+
+/**
+ * pevent_filter_remove_event - remove a filter for an event
+ * @filter: the event filter to remove from
+ * @event_id: the event to remove a filter for
+ *
+ * Removes the filter saved for an event defined by @event_id
+ * from the @filter.
+ *
+ * Returns 1: if an event was removed
+ *   0: if the event was not found
+ */
+int pevent_filter_remove_event(struct event_filter *filter,
+                              int event_id)
+{
+       struct filter_type *filter_type;
+       unsigned long len;
+
+       if (!filter->filters)
+               return 0;
+
+       filter_type = find_filter_type(filter, event_id);
+
+       if (!filter_type)
+               return 0;
+
+       free_filter_type(filter_type);
+
+       /* The filter_type points into the event_filters array */
+       len = (unsigned long)(filter->event_filters + filter->filters) -
+               (unsigned long)(filter_type + 1);
+
+       memmove(filter_type, filter_type + 1, len);
+       filter->filters--;
+
+       memset(&filter->event_filters[filter->filters], 0,
+              sizeof(*filter_type));
+
+       return 1;
+}
+
+/**
+ * pevent_filter_reset - clear all filters in a filter
+ * @filter: the event filter to reset
+ *
+ * Removes all filters from a filter and resets it.
+ */
+void pevent_filter_reset(struct event_filter *filter)
+{
+       int i;
+
+       for (i = 0; i < filter->filters; i++)
+               free_filter_type(&filter->event_filters[i]);
+
+       free(filter->event_filters);
+       filter->filters = 0;
+       filter->event_filters = NULL;
+}
+
+void pevent_filter_free(struct event_filter *filter)
+{
+       pevent_unref(filter->pevent);
+
+       pevent_filter_reset(filter);
+
+       free(filter);
+}
+
+static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg);
+
+static int copy_filter_type(struct event_filter *filter,
+                            struct event_filter *source,
+                            struct filter_type *filter_type)
+{
+       struct filter_arg *arg;
+       struct event_format *event;
+       const char *sys;
+       const char *name;
+       char *str;
+
+       /* Can't assume that the pevent's are the same */
+       sys = filter_type->event->system;
+       name = filter_type->event->name;
+       event = pevent_find_event_by_name(filter->pevent, sys, name);
+       if (!event)
+               return -1;
+
+       str = arg_to_str(source, filter_type->filter);
+       if (!str)
+               return -1;
+
+       if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) {
+               /* Add trivial event */
+               arg = allocate_arg();
+               arg->type = FILTER_ARG_BOOLEAN;
+               if (strcmp(str, "TRUE") == 0)
+                       arg->boolean.value = 1;
+               else
+                       arg->boolean.value = 0;
+
+               filter_type = add_filter_type(filter, event->id);
+               filter_type->filter = arg;
+
+               free(str);
+               return 0;
+       }
+
+       filter_event(filter, event, str, NULL);
+       free(str);
+
+       return 0;
+}
+
+/**
+ * pevent_filter_copy - copy a filter using another filter
+ * @dest - the filter to copy to
+ * @source - the filter to copy from
+ *
+ * Returns 0 on success and -1 if not all filters were copied
+ */
+int pevent_filter_copy(struct event_filter *dest, struct event_filter *source)
+{
+       int ret = 0;
+       int i;
+
+       pevent_filter_reset(dest);
+
+       for (i = 0; i < source->filters; i++) {
+               if (copy_filter_type(dest, source, &source->event_filters[i]))
+                       ret = -1;
+       }
+       return ret;
+}
+
+
+/**
+ * pevent_update_trivial - update the trivial filters with the given filter
+ * @dest - the filter to update
+ * @source - the filter as the source of the update
+ * @type - the type of trivial filter to update.
+ *
+ * Scan dest for trivial events matching @type to replace with the source.
+ *
+ * Returns 0 on success and -1 if there was a problem updating, but
+ *   events may have still been updated on error.
+ */
+int pevent_update_trivial(struct event_filter *dest, struct event_filter *source,
+                         enum filter_trivial_type type)
+{
+       struct pevent *src_pevent;
+       struct pevent *dest_pevent;
+       struct event_format *event;
+       struct filter_type *filter_type;
+       struct filter_arg *arg;
+       char *str;
+       int i;
+
+       src_pevent = source->pevent;
+       dest_pevent = dest->pevent;
+
+       /* Do nothing if either of the filters has nothing to filter */
+       if (!dest->filters || !source->filters)
+               return 0;
+
+       for (i = 0; i < dest->filters; i++) {
+               filter_type = &dest->event_filters[i];
+               arg = filter_type->filter;
+               if (arg->type != FILTER_ARG_BOOLEAN)
+                       continue;
+               if ((arg->boolean.value && type == FILTER_TRIVIAL_FALSE) ||
+                   (!arg->boolean.value && type == FILTER_TRIVIAL_TRUE))
+                       continue;
+
+               event = filter_type->event;
+
+               if (src_pevent != dest_pevent) {
+                       /* do a look up */
+                       event = pevent_find_event_by_name(src_pevent,
+                                                         event->system,
+                                                         event->name);
+                       if (!event)
+                               return -1;
+               }
+
+               str = pevent_filter_make_string(source, event->id);
+               if (!str)
+                       continue;
+
+               /* Don't bother if the filter is trivial too */
+               if (strcmp(str, "TRUE") != 0 && strcmp(str, "FALSE") != 0)
+                       filter_event(dest, event, str, NULL);
+               free(str);
+       }
+       return 0;
+}
+
+/**
+ * pevent_filter_clear_trivial - clear TRUE and FALSE filters
+ * @filter: the filter to remove trivial filters from
+ * @type: remove only true, false, or both
+ *
+ * Removes filters that only contain a TRUE or FALES boolean arg.
+ */
+void pevent_filter_clear_trivial(struct event_filter *filter,
+                                enum filter_trivial_type type)
+{
+       struct filter_type *filter_type;
+       int count = 0;
+       int *ids;
+       int i;
+
+       if (!filter->filters)
+               return;
+
+       /*
+        * Two steps, first get all ids with trivial filters.
+        *  then remove those ids.
+        */
+       for (i = 0; i < filter->filters; i++) {
+               filter_type = &filter->event_filters[i];
+               if (filter_type->filter->type != FILTER_ARG_BOOLEAN)
+                       continue;
+               switch (type) {
+               case FILTER_TRIVIAL_FALSE:
+                       if (filter_type->filter->boolean.value)
+                               continue;
+               case FILTER_TRIVIAL_TRUE:
+                       if (!filter_type->filter->boolean.value)
+                               continue;
+               default:
+                       break;
+               }
+               if (count)
+                       ids = realloc(ids, sizeof(*ids) * (count + 1));
+               else
+                       ids = malloc(sizeof(*ids));
+               if (!ids)
+                       die("Can't allocate ids");
+               ids[count++] = filter_type->event_id;
+       }
+
+       if (!count)
+               return;
+
+       for (i = 0; i < count; i++)
+               pevent_filter_remove_event(filter, ids[i]);
+
+       free(ids);
+}
+
+/**
+ * pevent_filter_event_has_trivial - return true event contains trivial filter
+ * @filter: the filter with the information
+ * @event_id: the id of the event to test
+ * @type: trivial type to test for (TRUE, FALSE, EITHER)
+ *
+ * Returns 1 if the event contains a matching trivial type
+ *  otherwise 0.
+ */
+int pevent_filter_event_has_trivial(struct event_filter *filter,
+                                   int event_id,
+                                   enum filter_trivial_type type)
+{
+       struct filter_type *filter_type;
+
+       if (!filter->filters)
+               return 0;
+
+       filter_type = find_filter_type(filter, event_id);
+
+       if (!filter_type)
+               return 0;
+
+       if (filter_type->filter->type != FILTER_ARG_BOOLEAN)
+               return 0;
+
+       switch (type) {
+       case FILTER_TRIVIAL_FALSE:
+               return !filter_type->filter->boolean.value;
+
+       case FILTER_TRIVIAL_TRUE:
+               return filter_type->filter->boolean.value;
+       default:
+               return 1;
+       }
+}
+
+static int test_filter(struct event_format *event,
+                      struct filter_arg *arg, struct pevent_record *record);
+
+static const char *
+get_comm(struct event_format *event, struct pevent_record *record)
+{
+       const char *comm;
+       int pid;
+
+       pid = pevent_data_pid(event->pevent, record);
+       comm = pevent_data_comm_from_pid(event->pevent, pid);
+       return comm;
+}
+
+static unsigned long long
+get_value(struct event_format *event,
+         struct format_field *field, struct pevent_record *record)
+{
+       unsigned long long val;
+
+       /* Handle our dummy "comm" field */
+       if (field == &comm) {
+               const char *name;
+
+               name = get_comm(event, record);
+               return (unsigned long long)name;
+       }
+
+       pevent_read_number_field(field, record->data, &val);
+
+       if (!(field->flags & FIELD_IS_SIGNED))
+               return val;
+
+       switch (field->size) {
+       case 1:
+               return (char)val;
+       case 2:
+               return (short)val;
+       case 4:
+               return (int)val;
+       case 8:
+               return (long long)val;
+       }
+       return val;
+}
+
+static unsigned long long
+get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record);
+
+static unsigned long long
+get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record)
+{
+       unsigned long long lval, rval;
+
+       lval = get_arg_value(event, arg->exp.left, record);
+       rval = get_arg_value(event, arg->exp.right, record);
+
+       switch (arg->exp.type) {
+       case FILTER_EXP_ADD:
+               return lval + rval;
+
+       case FILTER_EXP_SUB:
+               return lval - rval;
+
+       case FILTER_EXP_MUL:
+               return lval * rval;
+
+       case FILTER_EXP_DIV:
+               return lval / rval;
+
+       case FILTER_EXP_MOD:
+               return lval % rval;
+
+       case FILTER_EXP_RSHIFT:
+               return lval >> rval;
+
+       case FILTER_EXP_LSHIFT:
+               return lval << rval;
+
+       case FILTER_EXP_AND:
+               return lval & rval;
+
+       case FILTER_EXP_OR:
+               return lval | rval;
+
+       case FILTER_EXP_XOR:
+               return lval ^ rval;
+
+       case FILTER_EXP_NOT:
+       default:
+               die("error in exp");
+       }
+       return 0;
+}
+
+static unsigned long long
+get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record)
+{
+       switch (arg->type) {
+       case FILTER_ARG_FIELD:
+               return get_value(event, arg->field.field, record);
+
+       case FILTER_ARG_VALUE:
+               if (arg->value.type != FILTER_NUMBER)
+                       die("must have number field!");
+               return arg->value.val;
+
+       case FILTER_ARG_EXP:
+               return get_exp_value(event, arg, record);
+
+       default:
+               die("oops in filter");
+       }
+       return 0;
+}
+
+static int test_num(struct event_format *event,
+                   struct filter_arg *arg, struct pevent_record *record)
+{
+       unsigned long long lval, rval;
+
+       lval = get_arg_value(event, arg->num.left, record);
+       rval = get_arg_value(event, arg->num.right, record);
+
+       switch (arg->num.type) {
+       case FILTER_CMP_EQ:
+               return lval == rval;
+
+       case FILTER_CMP_NE:
+               return lval != rval;
+
+       case FILTER_CMP_GT:
+               return lval > rval;
+
+       case FILTER_CMP_LT:
+               return lval < rval;
+
+       case FILTER_CMP_GE:
+               return lval >= rval;
+
+       case FILTER_CMP_LE:
+               return lval <= rval;
+
+       default:
+               /* ?? */
+               return 0;
+       }
+}
+
+static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record)
+{
+       const char *val = record->data + arg->str.field->offset;
+
+       /*
+        * We need to copy the data since we can't be sure the field
+        * is null terminated.
+        */
+       if (*(val + arg->str.field->size - 1)) {
+               /* copy it */
+               memcpy(arg->str.buffer, val, arg->str.field->size);
+               /* the buffer is already NULL terminated */
+               val = arg->str.buffer;
+       }
+       return val;
+}
+
+static int test_str(struct event_format *event,
+                   struct filter_arg *arg, struct pevent_record *record)
+{
+       const char *val;
+
+       if (arg->str.field == &comm)
+               val = get_comm(event, record);
+       else
+               val = get_field_str(arg, record);
+
+       switch (arg->str.type) {
+       case FILTER_CMP_MATCH:
+               return strcmp(val, arg->str.val) == 0;
+
+       case FILTER_CMP_NOT_MATCH:
+               return strcmp(val, arg->str.val) != 0;
+
+       case FILTER_CMP_REGEX:
+               /* Returns zero on match */
+               return !regexec(&arg->str.reg, val, 0, NULL, 0);
+
+       case FILTER_CMP_NOT_REGEX:
+               return regexec(&arg->str.reg, val, 0, NULL, 0);
+
+       default:
+               /* ?? */
+               return 0;
+       }
+}
+
+static int test_op(struct event_format *event,
+                  struct filter_arg *arg, struct pevent_record *record)
+{
+       switch (arg->op.type) {
+       case FILTER_OP_AND:
+               return test_filter(event, arg->op.left, record) &&
+                       test_filter(event, arg->op.right, record);
+
+       case FILTER_OP_OR:
+               return test_filter(event, arg->op.left, record) ||
+                       test_filter(event, arg->op.right, record);
+
+       case FILTER_OP_NOT:
+               return !test_filter(event, arg->op.right, record);
+
+       default:
+               /* ?? */
+               return 0;
+       }
+}
+
+static int test_filter(struct event_format *event,
+                      struct filter_arg *arg, struct pevent_record *record)
+{
+       switch (arg->type) {
+       case FILTER_ARG_BOOLEAN:
+               /* easy case */
+               return arg->boolean.value;
+
+       case FILTER_ARG_OP:
+               return test_op(event, arg, record);
+
+       case FILTER_ARG_NUM:
+               return test_num(event, arg, record);
+
+       case FILTER_ARG_STR:
+               return test_str(event, arg, record);
+
+       case FILTER_ARG_EXP:
+       case FILTER_ARG_VALUE:
+       case FILTER_ARG_FIELD:
+               /*
+                * Expressions, fields and values evaluate
+                * to true if they return non zero
+                */
+               return !!get_arg_value(event, arg, record);
+
+       default:
+               die("oops!");
+               /* ?? */
+               return 0;
+       }
+}
+
+/**
+ * pevent_event_filtered - return true if event has filter
+ * @filter: filter struct with filter information
+ * @event_id: event id to test if filter exists
+ *
+ * Returns 1 if filter found for @event_id
+ *   otherwise 0;
+ */
+int pevent_event_filtered(struct event_filter *filter,
+                         int event_id)
+{
+       struct filter_type *filter_type;
+
+       if (!filter->filters)
+               return 0;
+
+       filter_type = find_filter_type(filter, event_id);
+
+       return filter_type ? 1 : 0;
+}
+
+/**
+ * pevent_filter_match - test if a record matches a filter
+ * @filter: filter struct with filter information
+ * @record: the record to test against the filter
+ *
+ * Returns:
+ *  1 - filter found for event and @record matches
+ *  0 - filter found for event and @record does not match
+ * -1 - no filter found for @record's event
+ * -2 - if no filters exist
+ */
+int pevent_filter_match(struct event_filter *filter,
+                       struct pevent_record *record)
+{
+       struct pevent *pevent = filter->pevent;
+       struct filter_type *filter_type;
+       int event_id;
+
+       if (!filter->filters)
+               return FILTER_NONE;
+
+       event_id = pevent_data_type(pevent, record);
+
+       filter_type = find_filter_type(filter, event_id);
+
+       if (!filter_type)
+               return FILTER_NOEXIST;
+
+       return test_filter(filter_type->event, filter_type->filter, record) ?
+               FILTER_MATCH : FILTER_MISS;
+}
+
+static char *op_to_str(struct event_filter *filter, struct filter_arg *arg)
+{
+       char *str = NULL;
+       char *left = NULL;
+       char *right = NULL;
+       char *op = NULL;
+       int left_val = -1;
+       int right_val = -1;
+       int val;
+       int len;
+
+       switch (arg->op.type) {
+       case FILTER_OP_AND:
+               op = "&&";
+               /* fall through */
+       case FILTER_OP_OR:
+               if (!op)
+                       op = "||";
+
+               left = arg_to_str(filter, arg->op.left);
+               right = arg_to_str(filter, arg->op.right);
+               if (!left || !right)
+                       break;
+
+               /* Try to consolidate boolean values */
+               if (strcmp(left, "TRUE") == 0)
+                       left_val = 1;
+               else if (strcmp(left, "FALSE") == 0)
+                       left_val = 0;
+
+               if (strcmp(right, "TRUE") == 0)
+                       right_val = 1;
+               else if (strcmp(right, "FALSE") == 0)
+                       right_val = 0;
+
+               if (left_val >= 0) {
+                       if ((arg->op.type == FILTER_OP_AND && !left_val) ||
+                           (arg->op.type == FILTER_OP_OR && left_val)) {
+                               /* Just return left value */
+                               str = left;
+                               left = NULL;
+                               break;
+                       }
+                       if (right_val >= 0) {
+                               /* just evaluate this. */
+                               val = 0;
+                               switch (arg->op.type) {
+                               case FILTER_OP_AND:
+                                       val = left_val && right_val;
+                                       break;
+                               case FILTER_OP_OR:
+                                       val = left_val || right_val;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               str = malloc_or_die(6);
+                               if (val)
+                                       strcpy(str, "TRUE");
+                               else
+                                       strcpy(str, "FALSE");
+                               break;
+                       }
+               }
+               if (right_val >= 0) {
+                       if ((arg->op.type == FILTER_OP_AND && !right_val) ||
+                           (arg->op.type == FILTER_OP_OR && right_val)) {
+                               /* Just return right value */
+                               str = right;
+                               right = NULL;
+                               break;
+                       }
+                       /* The right value is meaningless */
+                       str = left;
+                       left = NULL;
+                       break;
+               }
+
+               len = strlen(left) + strlen(right) + strlen(op) + 10;
+               str = malloc_or_die(len);
+               snprintf(str, len, "(%s) %s (%s)",
+                        left, op, right);
+               break;
+
+       case FILTER_OP_NOT:
+               op = "!";
+               right = arg_to_str(filter, arg->op.right);
+               if (!right)
+                       break;
+
+               /* See if we can consolidate */
+               if (strcmp(right, "TRUE") == 0)
+                       right_val = 1;
+               else if (strcmp(right, "FALSE") == 0)
+                       right_val = 0;
+               if (right_val >= 0) {
+                       /* just return the opposite */
+                       str = malloc_or_die(6);
+                       if (right_val)
+                               strcpy(str, "FALSE");
+                       else
+                               strcpy(str, "TRUE");
+                       break;
+               }
+               len = strlen(right) + strlen(op) + 3;
+               str = malloc_or_die(len);
+               snprintf(str, len, "%s(%s)", op, right);
+               break;
+
+       default:
+               /* ?? */
+               break;
+       }
+       free(left);
+       free(right);
+       return str;
+}
+
+static char *val_to_str(struct event_filter *filter, struct filter_arg *arg)
+{
+       char *str;
+
+       str = malloc_or_die(30);
+
+       snprintf(str, 30, "%lld", arg->value.val);
+
+       return str;
+}
+
+static char *field_to_str(struct event_filter *filter, struct filter_arg *arg)
+{
+       return strdup(arg->field.field->name);
+}
+
+static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg)
+{
+       char *lstr;
+       char *rstr;
+       char *op;
+       char *str;
+       int len;
+
+       lstr = arg_to_str(filter, arg->exp.left);
+       rstr = arg_to_str(filter, arg->exp.right);
+
+       switch (arg->exp.type) {
+       case FILTER_EXP_ADD:
+               op = "+";
+               break;
+       case FILTER_EXP_SUB:
+               op = "-";
+               break;
+       case FILTER_EXP_MUL:
+               op = "*";
+               break;
+       case FILTER_EXP_DIV:
+               op = "/";
+               break;
+       case FILTER_EXP_MOD:
+               op = "%";
+               break;
+       case FILTER_EXP_RSHIFT:
+               op = ">>";
+               break;
+       case FILTER_EXP_LSHIFT:
+               op = "<<";
+               break;
+       case FILTER_EXP_AND:
+               op = "&";
+               break;
+       case FILTER_EXP_OR:
+               op = "|";
+               break;
+       case FILTER_EXP_XOR:
+               op = "^";
+               break;
+       default:
+               die("oops in exp");
+       }
+
+       len = strlen(op) + strlen(lstr) + strlen(rstr) + 4;
+       str = malloc_or_die(len);
+       snprintf(str, len, "%s %s %s", lstr, op, rstr);
+       free(lstr);
+       free(rstr);
+
+       return str;
+}
+
+static char *num_to_str(struct event_filter *filter, struct filter_arg *arg)
+{
+       char *lstr;
+       char *rstr;
+       char *str = NULL;
+       char *op = NULL;
+       int len;
+
+       lstr = arg_to_str(filter, arg->num.left);
+       rstr = arg_to_str(filter, arg->num.right);
+
+       switch (arg->num.type) {
+       case FILTER_CMP_EQ:
+               op = "==";
+               /* fall through */
+       case FILTER_CMP_NE:
+               if (!op)
+                       op = "!=";
+               /* fall through */
+       case FILTER_CMP_GT:
+               if (!op)
+                       op = ">";
+               /* fall through */
+       case FILTER_CMP_LT:
+               if (!op)
+                       op = "<";
+               /* fall through */
+       case FILTER_CMP_GE:
+               if (!op)
+                       op = ">=";
+               /* fall through */
+       case FILTER_CMP_LE:
+               if (!op)
+                       op = "<=";
+
+               len = strlen(lstr) + strlen(op) + strlen(rstr) + 4;
+               str = malloc_or_die(len);
+               sprintf(str, "%s %s %s", lstr, op, rstr);
+
+               break;
+
+       default:
+               /* ?? */
+               break;
+       }
+
+       free(lstr);
+       free(rstr);
+       return str;
+}
+
+static char *str_to_str(struct event_filter *filter, struct filter_arg *arg)
+{
+       char *str = NULL;
+       char *op = NULL;
+       int len;
+
+       switch (arg->str.type) {
+       case FILTER_CMP_MATCH:
+               op = "==";
+               /* fall through */
+       case FILTER_CMP_NOT_MATCH:
+               if (!op)
+                       op = "!=";
+               /* fall through */
+       case FILTER_CMP_REGEX:
+               if (!op)
+                       op = "=~";
+               /* fall through */
+       case FILTER_CMP_NOT_REGEX:
+               if (!op)
+                       op = "!~";
+
+               len = strlen(arg->str.field->name) + strlen(op) +
+                       strlen(arg->str.val) + 6;
+               str = malloc_or_die(len);
+               snprintf(str, len, "%s %s \"%s\"",
+                        arg->str.field->name,
+                        op, arg->str.val);
+               break;
+
+       default:
+               /* ?? */
+               break;
+       }
+       return str;
+}
+
+static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg)
+{
+       char *str;
+
+       switch (arg->type) {
+       case FILTER_ARG_BOOLEAN:
+               str = malloc_or_die(6);
+               if (arg->boolean.value)
+                       strcpy(str, "TRUE");
+               else
+                       strcpy(str, "FALSE");
+               return str;
+
+       case FILTER_ARG_OP:
+               return op_to_str(filter, arg);
+
+       case FILTER_ARG_NUM:
+               return num_to_str(filter, arg);
+
+       case FILTER_ARG_STR:
+               return str_to_str(filter, arg);
+
+       case FILTER_ARG_VALUE:
+               return val_to_str(filter, arg);
+
+       case FILTER_ARG_FIELD:
+               return field_to_str(filter, arg);
+
+       case FILTER_ARG_EXP:
+               return exp_to_str(filter, arg);
+
+       default:
+               /* ?? */
+               return NULL;
+       }
+
+}
+
+/**
+ * pevent_filter_make_string - return a string showing the filter
+ * @filter: filter struct with filter information
+ * @event_id: the event id to return the filter string with
+ *
+ * Returns a string that displays the filter contents.
+ *  This string must be freed with free(str).
+ *  NULL is returned if no filter is found.
+ */
+char *
+pevent_filter_make_string(struct event_filter *filter, int event_id)
+{
+       struct filter_type *filter_type;
+
+       if (!filter->filters)
+               return NULL;
+
+       filter_type = find_filter_type(filter, event_id);
+
+       if (!filter_type)
+               return NULL;
+
+       return arg_to_str(filter, filter_type->filter);
+}
+
+/**
+ * pevent_filter_compare - compare two filters and return if they are the same
+ * @filter1: Filter to compare with @filter2
+ * @filter2: Filter to compare with @filter1
+ *
+ * Returns:
+ *  1 if the two filters hold the same content.
+ *  0 if they do not.
+ */
+int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2)
+{
+       struct filter_type *filter_type1;
+       struct filter_type *filter_type2;
+       char *str1, *str2;
+       int result;
+       int i;
+
+       /* Do the easy checks first */
+       if (filter1->filters != filter2->filters)
+               return 0;
+       if (!filter1->filters && !filter2->filters)
+               return 1;
+
+       /*
+        * Now take a look at each of the events to see if they have the same
+        * filters to them.
+        */
+       for (i = 0; i < filter1->filters; i++) {
+               filter_type1 = &filter1->event_filters[i];
+               filter_type2 = find_filter_type(filter2, filter_type1->event_id);
+               if (!filter_type2)
+                       break;
+               if (filter_type1->filter->type != filter_type2->filter->type)
+                       break;
+               switch (filter_type1->filter->type) {
+               case FILTER_TRIVIAL_FALSE:
+               case FILTER_TRIVIAL_TRUE:
+                       /* trivial types just need the type compared */
+                       continue;
+               default:
+                       break;
+               }
+               /* The best way to compare complex filters is with strings */
+               str1 = arg_to_str(filter1, filter_type1->filter);
+               str2 = arg_to_str(filter2, filter_type2->filter);
+               result = strcmp(str1, str2) != 0;
+               free(str1);
+               free(str2);
+               if (result)
+                       break;
+       }
+
+       if (i < filter1->filters)
+               return 0;
+       return 1;
+}
+
diff --git a/tools/lib/traceevent/parse-utils.c b/tools/lib/traceevent/parse-utils.c
new file mode 100644 (file)
index 0000000..f023a13
--- /dev/null
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#define __weak __attribute__((weak))
+
+void __vdie(const char *fmt, va_list ap)
+{
+       int ret = errno;
+
+       if (errno)
+               perror("trace-cmd");
+       else
+               ret = -1;
+
+       fprintf(stderr, "  ");
+       vfprintf(stderr, fmt, ap);
+
+       fprintf(stderr, "\n");
+       exit(ret);
+}
+
+void __die(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       __vdie(fmt, ap);
+       va_end(ap);
+}
+
+void __weak die(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       __vdie(fmt, ap);
+       va_end(ap);
+}
+
+void __vwarning(const char *fmt, va_list ap)
+{
+       if (errno)
+               perror("trace-cmd");
+       errno = 0;
+
+       fprintf(stderr, "  ");
+       vfprintf(stderr, fmt, ap);
+
+       fprintf(stderr, "\n");
+}
+
+void __warning(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       __vwarning(fmt, ap);
+       va_end(ap);
+}
+
+void __weak warning(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       __vwarning(fmt, ap);
+       va_end(ap);
+}
+
+void __vpr_stat(const char *fmt, va_list ap)
+{
+       vprintf(fmt, ap);
+       printf("\n");
+}
+
+void __pr_stat(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       __vpr_stat(fmt, ap);
+       va_end(ap);
+}
+
+void __weak vpr_stat(const char *fmt, va_list ap)
+{
+       __vpr_stat(fmt, ap);
+}
+
+void __weak pr_stat(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       __vpr_stat(fmt, ap);
+       va_end(ap);
+}
+
+void __weak *malloc_or_die(unsigned int size)
+{
+       void *data;
+
+       data = malloc(size);
+       if (!data)
+               die("malloc");
+       return data;
+}
diff --git a/tools/lib/traceevent/trace-seq.c b/tools/lib/traceevent/trace-seq.c
new file mode 100644 (file)
index 0000000..b1ccc92
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "event-parse.h"
+#include "event-utils.h"
+
+/*
+ * The TRACE_SEQ_POISON is to catch the use of using
+ * a trace_seq structure after it was destroyed.
+ */
+#define TRACE_SEQ_POISON       ((void *)0xdeadbeef)
+#define TRACE_SEQ_CHECK(s)                                             \
+do {                                                                   \
+       if ((s)->buffer == TRACE_SEQ_POISON)                    \
+               die("Usage of trace_seq after it was destroyed");       \
+} while (0)
+
+/**
+ * trace_seq_init - initialize the trace_seq structure
+ * @s: a pointer to the trace_seq structure to initialize
+ */
+void trace_seq_init(struct trace_seq *s)
+{
+       s->len = 0;
+       s->readpos = 0;
+       s->buffer_size = TRACE_SEQ_BUF_SIZE;
+       s->buffer = malloc_or_die(s->buffer_size);
+}
+
+/**
+ * trace_seq_destroy - free up memory of a trace_seq
+ * @s: a pointer to the trace_seq to free the buffer
+ *
+ * Only frees the buffer, not the trace_seq struct itself.
+ */
+void trace_seq_destroy(struct trace_seq *s)
+{
+       if (!s)
+               return;
+       TRACE_SEQ_CHECK(s);
+       free(s->buffer);
+       s->buffer = TRACE_SEQ_POISON;
+}
+
+static void expand_buffer(struct trace_seq *s)
+{
+       s->buffer_size += TRACE_SEQ_BUF_SIZE;
+       s->buffer = realloc(s->buffer, s->buffer_size);
+       if (!s->buffer)
+               die("Can't allocate trace_seq buffer memory");
+}
+
+/**
+ * trace_seq_printf - sequence printing of trace information
+ * @s: trace sequence descriptor
+ * @fmt: printf format string
+ *
+ * It returns 0 if the trace oversizes the buffer's free
+ * space, 1 otherwise.
+ *
+ * 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, ...)
+{
+       va_list ap;
+       int len;
+       int ret;
+
+       TRACE_SEQ_CHECK(s);
+
+ try_again:
+       len = (s->buffer_size - 1) - s->len;
+
+       va_start(ap, fmt);
+       ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
+       va_end(ap);
+
+       if (ret >= len) {
+               expand_buffer(s);
+               goto try_again;
+       }
+
+       s->len += ret;
+
+       return 1;
+}
+
+/**
+ * trace_seq_vprintf - 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_vprintf(struct trace_seq *s, const char *fmt, va_list args)
+{
+       int len;
+       int ret;
+
+       TRACE_SEQ_CHECK(s);
+
+ try_again:
+       len = (s->buffer_size - 1) - s->len;
+
+       ret = vsnprintf(s->buffer + s->len, len, fmt, args);
+
+       if (ret >= len) {
+               expand_buffer(s);
+               goto try_again;
+       }
+
+       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;
+
+       TRACE_SEQ_CHECK(s);
+
+       len = strlen(str);
+
+       while (len > ((s->buffer_size - 1) - s->len))
+               expand_buffer(s);
+
+       memcpy(s->buffer + s->len, str, len);
+       s->len += len;
+
+       return len;
+}
+
+int trace_seq_putc(struct trace_seq *s, unsigned char c)
+{
+       TRACE_SEQ_CHECK(s);
+
+       while (s->len >= (s->buffer_size - 1))
+               expand_buffer(s);
+
+       s->buffer[s->len++] = c;
+
+       return 1;
+}
+
+void trace_seq_terminate(struct trace_seq *s)
+{
+       TRACE_SEQ_CHECK(s);
+
+       /* There's always one character left on the buffer */
+       s->buffer[s->len] = 0;
+}
+
+int trace_seq_do_printf(struct trace_seq *s)
+{
+       TRACE_SEQ_CHECK(s);
+       return printf("%.*s", s->len, s->buffer);
+}
index d144866..42c6fd2 100644 (file)
@@ -6,6 +6,7 @@
        normal = black, lightgray
        selected = lightgray, magenta
        code = blue, lightgray
+       addr = magenta, lightgray
 
 [tui]
 
index 92271d3..fa37cd5 100644 (file)
@@ -1,18 +1,10 @@
-ifeq ("$(origin O)", "command line")
-       OUTPUT := $(O)/
-endif
+include ../scripts/Makefile.include
 
 # The default target of this Makefile is...
 all:
 
 include config/utilities.mak
 
-ifneq ($(OUTPUT),)
-# check that the output directory actually exists
-OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
-$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
-endif
-
 # Define V to have a more verbose compile.
 #
 # Define O to save output files in a separate directory.
@@ -84,31 +76,6 @@ ifneq ($(WERROR),0)
        CFLAGS_WERROR := -Werror
 endif
 
-#
-# Include saner warnings here, which can catch bugs:
-#
-
-EXTRA_WARNINGS := -Wformat
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement
-
 ifeq ("$(origin DEBUG)", "command line")
   PERF_DEBUG = $(DEBUG)
 endif
@@ -182,7 +149,7 @@ endif
 
 ### --- END CONFIGURATION SECTION ---
 
-BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
+BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -I$(EVENT_PARSE_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
 BASIC_LDFLAGS =
 
 # Guard against environment variables
@@ -211,6 +178,17 @@ $(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
 
+EVENT_PARSE_DIR = ../lib/traceevent/
+
+ifeq ("$(origin O)", "command line")
+       EP_PATH=$(OUTPUT)/
+else
+       EP_PATH=$(EVENT_PARSE_DIR)/
+endif
+
+LIBPARSEVENT = $(EP_PATH)libtraceevent.a
+EP_LIB := -L$(EP_PATH) -ltraceevent
+
 #
 # Single 'perf' binary right now:
 #
@@ -333,6 +311,8 @@ LIB_H += util/cpumap.h
 LIB_H += util/top.h
 LIB_H += $(ARCH_INCLUDE)
 LIB_H += util/cgroup.h
+LIB_H += $(EVENT_PARSE_DIR)event-parse.h
+LIB_H += util/target.h
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
 LIB_OBJS += $(OUTPUT)util/alias.o
@@ -394,6 +374,7 @@ LIB_OBJS += $(OUTPUT)util/util.o
 LIB_OBJS += $(OUTPUT)util/xyarray.o
 LIB_OBJS += $(OUTPUT)util/cpumap.o
 LIB_OBJS += $(OUTPUT)util/cgroup.o
+LIB_OBJS += $(OUTPUT)util/target.o
 
 BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
 
@@ -429,7 +410,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
 BUILTIN_OBJS += $(OUTPUT)builtin-test.o
 BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
 
-PERFLIBS = $(LIB_FILE)
+PERFLIBS = $(LIB_FILE) $(LIBPARSEVENT)
 
 # Files needed for the python binding, perf.so
 # pyrf is just an internal name needed for all those wrappers.
@@ -506,22 +487,23 @@ else
                # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
                BASIC_CFLAGS += -I/usr/include/slang
                EXTLIBS += -lnewt -lslang
-               LIB_OBJS += $(OUTPUT)util/ui/setup.o
-               LIB_OBJS += $(OUTPUT)util/ui/browser.o
-               LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
-               LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
-               LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
-               LIB_OBJS += $(OUTPUT)util/ui/helpline.o
-               LIB_OBJS += $(OUTPUT)util/ui/progress.o
-               LIB_OBJS += $(OUTPUT)util/ui/util.o
-               LIB_H += util/ui/browser.h
-               LIB_H += util/ui/browsers/map.h
-               LIB_H += util/ui/helpline.h
-               LIB_H += util/ui/keysyms.h
-               LIB_H += util/ui/libslang.h
-               LIB_H += util/ui/progress.h
-               LIB_H += util/ui/util.h
-               LIB_H += util/ui/ui.h
+               LIB_OBJS += $(OUTPUT)ui/setup.o
+               LIB_OBJS += $(OUTPUT)ui/browser.o
+               LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
+               LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
+               LIB_OBJS += $(OUTPUT)ui/browsers/map.o
+               LIB_OBJS += $(OUTPUT)ui/helpline.o
+               LIB_OBJS += $(OUTPUT)ui/progress.o
+               LIB_OBJS += $(OUTPUT)ui/util.o
+               LIB_OBJS += $(OUTPUT)ui/tui/setup.o
+               LIB_H += ui/browser.h
+               LIB_H += ui/browsers/map.h
+               LIB_H += ui/helpline.h
+               LIB_H += ui/keysyms.h
+               LIB_H += ui/libslang.h
+               LIB_H += ui/progress.h
+               LIB_H += ui/util.h
+               LIB_H += ui/ui.h
        endif
 endif
 
@@ -535,7 +517,12 @@ else
        else
                BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
                EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
-               LIB_OBJS += $(OUTPUT)util/gtk/browser.o
+               LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
+               LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
+               # Make sure that it'd be included only once.
+               ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
+                       LIB_OBJS += $(OUTPUT)ui/setup.o
+               endif
        endif
 endif
 
@@ -678,18 +665,6 @@ else
        endif
 endif
 
-ifneq ($(findstring $(MAKEFLAGS),s),s)
-ifndef V
-       QUIET_CC       = @echo '   ' CC $@;
-       QUIET_AR       = @echo '   ' AR $@;
-       QUIET_LINK     = @echo '   ' LINK $@;
-       QUIET_MKDIR    = @echo '   ' MKDIR $@;
-       QUIET_GEN      = @echo '   ' GEN $@;
-       QUIET_FLEX     = @echo '   ' FLEX $@;
-       QUIET_BISON    = @echo '   ' BISON $@;
-endif
-endif
-
 ifdef ASCIIDOC8
        export ASCIIDOC8
 endif
@@ -800,16 +775,16 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
 $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
 
-$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
 
-$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
 
-$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
 
-$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
 
 $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
@@ -844,6 +819,10 @@ $(sort $(dir $(DIRECTORY_DEPS))):
 $(LIB_FILE): $(LIB_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
 
+# libparsevent.a
+$(LIBPARSEVENT):
+       make -C $(EVENT_PARSE_DIR) $(COMMAND_O) libtraceevent.a
+
 help:
        @echo 'Perf make targets:'
        @echo '  doc            - make *all* documentation (see below)'
index 39104c0..547af48 100644 (file)
@@ -192,7 +192,7 @@ static void insert_caller_stat(unsigned long call_site,
 }
 
 static void process_alloc_event(void *data,
-                               struct event *event,
+                               struct event_format *event,
                                int cpu,
                                u64 timestamp __used,
                                struct thread *thread __used,
@@ -253,7 +253,7 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr,
 }
 
 static void process_free_event(void *data,
-                              struct event *event,
+                              struct event_format *event,
                               int cpu,
                               u64 timestamp __used,
                               struct thread *thread __used)
@@ -281,7 +281,7 @@ static void process_free_event(void *data,
 static void process_raw_event(union perf_event *raw_event __used, void *data,
                              int cpu, u64 timestamp, struct thread *thread)
 {
-       struct event *event;
+       struct event_format *event;
        int type;
 
        type = trace_parse_common_type(data);
index 12c8148..fd53319 100644 (file)
@@ -356,25 +356,25 @@ struct trace_release_event {
 
 struct trace_lock_handler {
        void (*acquire_event)(struct trace_acquire_event *,
-                             struct event *,
+                             struct event_format *,
                              int cpu,
                              u64 timestamp,
                              struct thread *thread);
 
        void (*acquired_event)(struct trace_acquired_event *,
-                              struct event *,
+                              struct event_format *,
                               int cpu,
                               u64 timestamp,
                               struct thread *thread);
 
        void (*contended_event)(struct trace_contended_event *,
-                               struct event *,
+                               struct event_format *,
                                int cpu,
                                u64 timestamp,
                                struct thread *thread);
 
        void (*release_event)(struct trace_release_event *,
-                             struct event *,
+                             struct event_format *,
                              int cpu,
                              u64 timestamp,
                              struct thread *thread);
@@ -416,7 +416,7 @@ enum acquire_flags {
 
 static void
 report_lock_acquire_event(struct trace_acquire_event *acquire_event,
-                       struct event *__event __used,
+                       struct event_format *__event __used,
                        int cpu __used,
                        u64 timestamp __used,
                        struct thread *thread __used)
@@ -480,7 +480,7 @@ end:
 
 static void
 report_lock_acquired_event(struct trace_acquired_event *acquired_event,
-                        struct event *__event __used,
+                        struct event_format *__event __used,
                         int cpu __used,
                         u64 timestamp __used,
                         struct thread *thread __used)
@@ -536,7 +536,7 @@ end:
 
 static void
 report_lock_contended_event(struct trace_contended_event *contended_event,
-                         struct event *__event __used,
+                         struct event_format *__event __used,
                          int cpu __used,
                          u64 timestamp __used,
                          struct thread *thread __used)
@@ -583,7 +583,7 @@ end:
 
 static void
 report_lock_release_event(struct trace_release_event *release_event,
-                       struct event *__event __used,
+                       struct event_format *__event __used,
                        int cpu __used,
                        u64 timestamp __used,
                        struct thread *thread __used)
@@ -647,7 +647,7 @@ static struct trace_lock_handler *trace_handler;
 
 static void
 process_lock_acquire_event(void *data,
-                          struct event *event __used,
+                          struct event_format *event __used,
                           int cpu __used,
                           u64 timestamp __used,
                           struct thread *thread __used)
@@ -666,7 +666,7 @@ process_lock_acquire_event(void *data,
 
 static void
 process_lock_acquired_event(void *data,
-                           struct event *event __used,
+                           struct event_format *event __used,
                            int cpu __used,
                            u64 timestamp __used,
                            struct thread *thread __used)
@@ -684,7 +684,7 @@ process_lock_acquired_event(void *data,
 
 static void
 process_lock_contended_event(void *data,
-                            struct event *event __used,
+                            struct event_format *event __used,
                             int cpu __used,
                             u64 timestamp __used,
                             struct thread *thread __used)
@@ -702,7 +702,7 @@ process_lock_contended_event(void *data,
 
 static void
 process_lock_release_event(void *data,
-                          struct event *event __used,
+                          struct event_format *event __used,
                           int cpu __used,
                           u64 timestamp __used,
                           struct thread *thread __used)
@@ -721,7 +721,7 @@ process_lock_release_event(void *data,
 static void
 process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)
 {
-       struct event *event;
+       struct event_format *event;
        int type;
 
        type = trace_parse_common_type(data);
index be4e1ee..8a3dfac 100644 (file)
@@ -44,7 +44,6 @@ struct perf_record {
        struct perf_evlist      *evlist;
        struct perf_session     *session;
        const char              *progname;
-       const char              *uid_str;
        int                     output;
        unsigned int            page_size;
        int                     realtime_prio;
@@ -218,7 +217,7 @@ try_again:
                        if (err == EPERM || err == EACCES) {
                                ui__error_paranoid();
                                exit(EXIT_FAILURE);
-                       } else if (err ==  ENODEV && opts->cpu_list) {
+                       } else if (err ==  ENODEV && opts->target.cpu_list) {
                                die("No such device - did you specify"
                                        " an out-of-range profile CPU?\n");
                        } else if (err == EINVAL) {
@@ -243,9 +242,13 @@ try_again:
                        /*
                         * If it's cycles then fall back to hrtimer
                         * based cpu-clock-tick sw counter, which
-                        * is always available even if no PMU support:
+                        * is always available even if no PMU support.
+                        *
+                        * PPC returns ENXIO until 2.6.37 (behavior changed
+                        * with commit b0a873e).
                         */
-                       if (attr->type == PERF_TYPE_HARDWARE
+                       if ((err == ENOENT || err == ENXIO)
+                                       && attr->type == PERF_TYPE_HARDWARE
                                        && attr->config == PERF_COUNT_HW_CPU_CYCLES) {
 
                                if (verbose)
@@ -253,6 +256,10 @@ try_again:
                                                    "trying to fall back to cpu-clock-ticks\n");
                                attr->type = PERF_TYPE_SOFTWARE;
                                attr->config = PERF_COUNT_SW_CPU_CLOCK;
+                               if (pos->name) {
+                                       free(pos->name);
+                                       pos->name = NULL;
+                               }
                                goto try_again;
                        }
 
@@ -578,7 +585,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
                perf_session__process_machines(session, tool,
                                               perf_event__synthesize_guest_os);
 
-       if (!opts->system_wide)
+       if (!opts->target.system_wide)
                perf_event__synthesize_thread_map(tool, evsel_list->threads,
                                                  process_synthesized_event,
                                                  machine);
@@ -747,6 +754,9 @@ static struct perf_record record = {
                .user_freq           = UINT_MAX,
                .user_interval       = ULLONG_MAX,
                .freq                = 1000,
+               .target              = {
+                       .uses_mmap   = true,
+               },
        },
        .write_mode = WRITE_FORCE,
        .file_new   = true,
@@ -765,9 +775,9 @@ const struct option record_options[] = {
                     parse_events_option),
        OPT_CALLBACK(0, "filter", &record.evlist, "filter",
                     "event filter", parse_filter),
-       OPT_STRING('p', "pid", &record.opts.target_pid, "pid",
+       OPT_STRING('p', "pid", &record.opts.target.pid, "pid",
                    "record events on existing process id"),
-       OPT_STRING('t', "tid", &record.opts.target_tid, "tid",
+       OPT_STRING('t', "tid", &record.opts.target.tid, "tid",
                    "record events on existing thread id"),
        OPT_INTEGER('r', "realtime", &record.realtime_prio,
                    "collect data with this RT SCHED_FIFO priority"),
@@ -775,11 +785,11 @@ const struct option record_options[] = {
                    "collect data without buffering"),
        OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples,
                    "collect raw sample records from all opened counters"),
-       OPT_BOOLEAN('a', "all-cpus", &record.opts.system_wide,
+       OPT_BOOLEAN('a', "all-cpus", &record.opts.target.system_wide,
                            "system-wide collection from all CPUs"),
        OPT_BOOLEAN('A', "append", &record.append_file,
                            "append to the output file to do incremental profiling"),
-       OPT_STRING('C', "cpu", &record.opts.cpu_list, "cpu",
+       OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu",
                    "list of cpus to monitor"),
        OPT_BOOLEAN('f', "force", &record.force,
                        "overwrite existing data file (deprecated)"),
@@ -813,7 +823,8 @@ const struct option record_options[] = {
        OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
                     "monitor event in cgroup name only",
                     parse_cgroups),
-       OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"),
+       OPT_STRING('u', "uid", &record.opts.target.uid_str, "user",
+                  "user to profile"),
 
        OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack,
                     "branch any", "sample any taken branches",
@@ -831,6 +842,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
        struct perf_evsel *pos;
        struct perf_evlist *evsel_list;
        struct perf_record *rec = &record;
+       char errbuf[BUFSIZ];
 
        perf_header__set_cmdline(argc, argv);
 
@@ -842,8 +854,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
 
        argc = parse_options(argc, argv, record_options, record_usage,
                            PARSE_OPT_STOP_AT_NON_OPTION);
-       if (!argc && !rec->opts.target_pid && !rec->opts.target_tid &&
-               !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str)
+       if (!argc && perf_target__none(&rec->opts.target))
                usage_with_options(record_usage, record_options);
 
        if (rec->force && rec->append_file) {
@@ -856,7 +867,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
                rec->write_mode = WRITE_FORCE;
        }
 
-       if (nr_cgroups && !rec->opts.system_wide) {
+       if (nr_cgroups && !rec->opts.target.system_wide) {
                fprintf(stderr, "cgroup monitoring only available in"
                        " system-wide mode\n");
                usage_with_options(record_usage, record_options);
@@ -883,17 +894,25 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
                goto out_symbol_exit;
        }
 
-       rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid,
-                                        rec->opts.target_pid);
-       if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1)
-               goto out_free_fd;
+       err = perf_target__validate(&rec->opts.target);
+       if (err) {
+               perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+               ui__warning("%s", errbuf);
+       }
+
+       err = perf_target__parse_uid(&rec->opts.target);
+       if (err) {
+               int saved_errno = errno;
 
-       if (rec->opts.target_pid)
-               rec->opts.target_tid = rec->opts.target_pid;
+               perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+               ui__warning("%s", errbuf);
+
+               err = -saved_errno;
+               goto out_free_fd;
+       }
 
-       if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
-                                    rec->opts.target_tid, rec->opts.uid,
-                                    rec->opts.cpu_list) < 0)
+       err = -ENOMEM;
+       if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0)
                usage_with_options(record_usage, record_options);
 
        list_for_each_entry(pos, &evsel_list->entries, node) {
index cdae9b2..d58e414 100644 (file)
@@ -296,12 +296,15 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
 {
        size_t ret;
        char unit;
-       unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
+       unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
+       u64 nr_events = self->stats.total_period;
 
-       nr_events = convert_unit(nr_events, &unit);
-       ret = fprintf(fp, "# Events: %lu%c", nr_events, unit);
+       nr_samples = convert_unit(nr_samples, &unit);
+       ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
        if (evname != NULL)
-               ret += fprintf(fp, " %s", evname);
+               ret += fprintf(fp, " of event '%s'", evname);
+
+       ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
        return ret + fprintf(fp, "\n#\n");
 }
 
@@ -680,14 +683,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
 
        }
 
-       if (strcmp(report.input_name, "-") != 0) {
-               if (report.use_gtk)
-                       perf_gtk_setup_browser(argc, argv, true);
-               else
-                       setup_browser(true);
-       } else {
+       if (strcmp(report.input_name, "-") != 0)
+               setup_browser(true);
+       else
                use_browser = 0;
-       }
 
        /*
         * Only in the newt browser we are doing integrated annotation,
index 1cad3af..b125e07 100644 (file)
@@ -728,34 +728,34 @@ struct trace_migrate_task_event {
 struct trace_sched_handler {
        void (*switch_event)(struct trace_switch_event *,
                             struct machine *,
-                            struct event *,
+                            struct event_format *,
                             int cpu,
                             u64 timestamp,
                             struct thread *thread);
 
        void (*runtime_event)(struct trace_runtime_event *,
                              struct machine *,
-                             struct event *,
+                             struct event_format *,
                              int cpu,
                              u64 timestamp,
                              struct thread *thread);
 
        void (*wakeup_event)(struct trace_wakeup_event *,
                             struct machine *,
-                            struct event *,
+                            struct event_format *,
                             int cpu,
                             u64 timestamp,
                             struct thread *thread);
 
        void (*fork_event)(struct trace_fork_event *,
-                          struct event *,
+                          struct event_format *,
                           int cpu,
                           u64 timestamp,
                           struct thread *thread);
 
        void (*migrate_task_event)(struct trace_migrate_task_event *,
                           struct machine *machine,
-                          struct event *,
+                          struct event_format *,
                           int cpu,
                           u64 timestamp,
                           struct thread *thread);
@@ -765,7 +765,7 @@ struct trace_sched_handler {
 static void
 replay_wakeup_event(struct trace_wakeup_event *wakeup_event,
                    struct machine *machine __used,
-                   struct event *event,
+                   struct event_format *event,
                    int cpu __used,
                    u64 timestamp __used,
                    struct thread *thread __used)
@@ -792,7 +792,7 @@ static u64 cpu_last_switched[MAX_CPUS];
 static void
 replay_switch_event(struct trace_switch_event *switch_event,
                    struct machine *machine __used,
-                   struct event *event,
+                   struct event_format *event,
                    int cpu,
                    u64 timestamp,
                    struct thread *thread __used)
@@ -835,7 +835,7 @@ replay_switch_event(struct trace_switch_event *switch_event,
 
 static void
 replay_fork_event(struct trace_fork_event *fork_event,
-                 struct event *event,
+                 struct event_format *event,
                  int cpu __used,
                  u64 timestamp __used,
                  struct thread *thread __used)
@@ -944,7 +944,7 @@ static void thread_atoms_insert(struct thread *thread)
 
 static void
 latency_fork_event(struct trace_fork_event *fork_event __used,
-                  struct event *event __used,
+                  struct event_format *event __used,
                   int cpu __used,
                   u64 timestamp __used,
                   struct thread *thread __used)
@@ -1026,7 +1026,7 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
 static void
 latency_switch_event(struct trace_switch_event *switch_event,
                     struct machine *machine,
-                    struct event *event __used,
+                    struct event_format *event __used,
                     int cpu,
                     u64 timestamp,
                     struct thread *thread __used)
@@ -1079,7 +1079,7 @@ latency_switch_event(struct trace_switch_event *switch_event,
 static void
 latency_runtime_event(struct trace_runtime_event *runtime_event,
                     struct machine *machine,
-                    struct event *event __used,
+                    struct event_format *event __used,
                     int cpu,
                     u64 timestamp,
                     struct thread *this_thread __used)
@@ -1102,7 +1102,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event,
 static void
 latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
                     struct machine *machine,
-                    struct event *__event __used,
+                    struct event_format *__event __used,
                     int cpu __used,
                     u64 timestamp,
                     struct thread *thread __used)
@@ -1150,7 +1150,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
 static void
 latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
                     struct machine *machine,
-                    struct event *__event __used,
+                    struct event_format *__event __used,
                     int cpu __used,
                     u64 timestamp,
                     struct thread *thread __used)
@@ -1361,7 +1361,7 @@ static struct trace_sched_handler *trace_handler;
 
 static void
 process_sched_wakeup_event(struct perf_tool *tool __used,
-                          struct event *event,
+                          struct event_format *event,
                           struct perf_sample *sample,
                           struct machine *machine,
                           struct thread *thread)
@@ -1398,7 +1398,7 @@ static char next_shortname2 = '0';
 static void
 map_switch_event(struct trace_switch_event *switch_event,
                 struct machine *machine,
-                struct event *event __used,
+                struct event_format *event __used,
                 int this_cpu,
                 u64 timestamp,
                 struct thread *thread __used)
@@ -1476,7 +1476,7 @@ map_switch_event(struct trace_switch_event *switch_event,
 
 static void
 process_sched_switch_event(struct perf_tool *tool __used,
-                          struct event *event,
+                          struct event_format *event,
                           struct perf_sample *sample,
                           struct machine *machine,
                           struct thread *thread)
@@ -1512,7 +1512,7 @@ process_sched_switch_event(struct perf_tool *tool __used,
 
 static void
 process_sched_runtime_event(struct perf_tool *tool __used,
-                           struct event *event,
+                           struct event_format *event,
                            struct perf_sample *sample,
                            struct machine *machine,
                            struct thread *thread)
@@ -1532,7 +1532,7 @@ process_sched_runtime_event(struct perf_tool *tool __used,
 
 static void
 process_sched_fork_event(struct perf_tool *tool __used,
-                        struct event *event,
+                        struct event_format *event,
                         struct perf_sample *sample,
                         struct machine *machine __used,
                         struct thread *thread)
@@ -1554,7 +1554,7 @@ process_sched_fork_event(struct perf_tool *tool __used,
 
 static void
 process_sched_exit_event(struct perf_tool *tool __used,
-                        struct event *event,
+                        struct event_format *event,
                         struct perf_sample *sample __used,
                         struct machine *machine __used,
                         struct thread *thread __used)
@@ -1565,7 +1565,7 @@ process_sched_exit_event(struct perf_tool *tool __used,
 
 static void
 process_sched_migrate_task_event(struct perf_tool *tool __used,
-                                struct event *event,
+                                struct event_format *event,
                                 struct perf_sample *sample,
                                 struct machine *machine,
                                 struct thread *thread)
@@ -1586,7 +1586,7 @@ process_sched_migrate_task_event(struct perf_tool *tool __used,
                                                  sample->time, thread);
 }
 
-typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event *event,
+typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event_format *event,
                                   struct perf_sample *sample,
                                   struct machine *machine,
                                   struct thread *thread);
index d4ce733..8e395a5 100644 (file)
@@ -261,7 +261,7 @@ static void print_sample_start(struct perf_sample *sample,
                               struct perf_event_attr *attr)
 {
        int type;
-       struct event *event;
+       struct event_format *event;
        const char *evname = NULL;
        unsigned long secs;
        unsigned long usecs;
index 1e5e9b2..62ae30d 100644 (file)
@@ -173,24 +173,23 @@ static struct perf_event_attr very_very_detailed_attrs[] = {
 
 
 
-struct perf_evlist             *evsel_list;
+static struct perf_evlist      *evsel_list;
 
-static bool                    system_wide                     =  false;
-static int                     run_idx                         =  0;
+static struct perf_target      target = {
+       .uid    = UINT_MAX,
+};
 
+static int                     run_idx                         =  0;
 static int                     run_count                       =  1;
 static bool                    no_inherit                      = false;
 static bool                    scale                           =  true;
 static bool                    no_aggr                         = false;
-static const char              *target_pid;
-static const char              *target_tid;
 static pid_t                   child_pid                       = -1;
 static bool                    null_run                        =  false;
 static int                     detailed_run                    =  0;
 static bool                    sync_run                        =  false;
 static bool                    big_num                         =  true;
 static int                     big_num_opt                     =  -1;
-static const char              *cpu_list;
 static const char              *csv_sep                        = NULL;
 static bool                    csv_output                      = false;
 static bool                    group                           = false;
@@ -265,18 +264,18 @@ static double stddev_stats(struct stats *stats)
        return sqrt(variance_mean);
 }
 
-struct stats                   runtime_nsecs_stats[MAX_NR_CPUS];
-struct stats                   runtime_cycles_stats[MAX_NR_CPUS];
-struct stats                   runtime_stalled_cycles_front_stats[MAX_NR_CPUS];
-struct stats                   runtime_stalled_cycles_back_stats[MAX_NR_CPUS];
-struct stats                   runtime_branches_stats[MAX_NR_CPUS];
-struct stats                   runtime_cacherefs_stats[MAX_NR_CPUS];
-struct stats                   runtime_l1_dcache_stats[MAX_NR_CPUS];
-struct stats                   runtime_l1_icache_stats[MAX_NR_CPUS];
-struct stats                   runtime_ll_cache_stats[MAX_NR_CPUS];
-struct stats                   runtime_itlb_cache_stats[MAX_NR_CPUS];
-struct stats                   runtime_dtlb_cache_stats[MAX_NR_CPUS];
-struct stats                   walltime_nsecs_stats;
+static struct stats runtime_nsecs_stats[MAX_NR_CPUS];
+static struct stats runtime_cycles_stats[MAX_NR_CPUS];
+static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS];
+static struct stats runtime_stalled_cycles_back_stats[MAX_NR_CPUS];
+static struct stats runtime_branches_stats[MAX_NR_CPUS];
+static struct stats runtime_cacherefs_stats[MAX_NR_CPUS];
+static struct stats runtime_l1_dcache_stats[MAX_NR_CPUS];
+static struct stats runtime_l1_icache_stats[MAX_NR_CPUS];
+static struct stats runtime_ll_cache_stats[MAX_NR_CPUS];
+static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
+static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
+static struct stats walltime_nsecs_stats;
 
 static int create_perf_stat_counter(struct perf_evsel *evsel,
                                    struct perf_evsel *first)
@@ -299,15 +298,15 @@ retry:
        if (exclude_guest_missing)
                evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
 
-       if (system_wide) {
+       if (perf_target__has_cpu(&target)) {
                ret = perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
-                                               group, group_fd);
+                                              group, group_fd);
                if (ret)
                        goto check_ret;
                return 0;
        }
 
-       if (!target_pid && !target_tid && (!group || evsel == first)) {
+       if (!perf_target__has_task(&target) && (!group || evsel == first)) {
                attr->disabled = 1;
                attr->enable_on_exec = 1;
        }
@@ -471,7 +470,7 @@ static int run_perf_stat(int argc __used, const char **argv)
                        exit(-1);
                }
 
-               if (!target_tid && !target_pid && !system_wide)
+               if (perf_target__none(&target))
                        evsel_list->threads->map[0] = child_pid;
 
                /*
@@ -506,7 +505,7 @@ static int run_perf_stat(int argc __used, const char **argv)
                                error("You may not have permission to collect %sstats.\n"
                                      "\t Consider tweaking"
                                      " /proc/sys/kernel/perf_event_paranoid or running as root.",
-                                     system_wide ? "system-wide " : "");
+                                     target.system_wide ? "system-wide " : "");
                        } else {
                                error("open_counter returned with %d (%s). "
                                      "/bin/dmesg may provide additional information.\n",
@@ -998,14 +997,14 @@ static void print_stat(int argc, const char **argv)
        if (!csv_output) {
                fprintf(output, "\n");
                fprintf(output, " Performance counter stats for ");
-               if (!target_pid && !target_tid) {
+               if (!perf_target__has_task(&target)) {
                        fprintf(output, "\'%s", argv[0]);
                        for (i = 1; i < argc; i++)
                                fprintf(output, " %s", argv[i]);
-               } else if (target_pid)
-                       fprintf(output, "process id \'%s", target_pid);
+               } else if (target.pid)
+                       fprintf(output, "process id \'%s", target.pid);
                else
-                       fprintf(output, "thread id \'%s", target_tid);
+                       fprintf(output, "thread id \'%s", target.tid);
 
                fprintf(output, "\'");
                if (run_count > 1)
@@ -1079,11 +1078,11 @@ static const struct option options[] = {
                     "event filter", parse_filter),
        OPT_BOOLEAN('i', "no-inherit", &no_inherit,
                    "child tasks do not inherit counters"),
-       OPT_STRING('p', "pid", &target_pid, "pid",
+       OPT_STRING('p', "pid", &target.pid, "pid",
                   "stat events on existing process id"),
-       OPT_STRING('t', "tid", &target_tid, "tid",
+       OPT_STRING('t', "tid", &target.tid, "tid",
                   "stat events on existing thread id"),
-       OPT_BOOLEAN('a', "all-cpus", &system_wide,
+       OPT_BOOLEAN('a', "all-cpus", &target.system_wide,
                    "system-wide collection from all CPUs"),
        OPT_BOOLEAN('g', "group", &group,
                    "put the counters into a counter group"),
@@ -1102,7 +1101,7 @@ static const struct option options[] = {
        OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, 
                           "print large numbers with thousands\' separators",
                           stat__set_big_num),
-       OPT_STRING('C', "cpu", &cpu_list, "cpu",
+       OPT_STRING('C', "cpu", &target.cpu_list, "cpu",
                    "list of cpus to monitor in system-wide"),
        OPT_BOOLEAN('A', "no-aggr", &no_aggr,
                    "disable CPU count aggregation"),
@@ -1220,13 +1219,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
        } else if (big_num_opt == 0) /* User passed --no-big-num */
                big_num = false;
 
-       if (!argc && !target_pid && !target_tid)
+       if (!argc && !perf_target__has_task(&target))
                usage_with_options(stat_usage, options);
        if (run_count <= 0)
                usage_with_options(stat_usage, options);
 
        /* no_aggr, cgroup are for system-wide only */
-       if ((no_aggr || nr_cgroups) && !system_wide) {
+       if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) {
                fprintf(stderr, "both cgroup and no-aggregation "
                        "modes only available in system-wide mode\n");
 
@@ -1236,23 +1235,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
        if (add_default_attributes())
                goto out;
 
-       if (target_pid)
-               target_tid = target_pid;
+       perf_target__validate(&target);
 
-       evsel_list->threads = thread_map__new_str(target_pid,
-                                                 target_tid, UINT_MAX);
-       if (evsel_list->threads == NULL) {
-               pr_err("Problems finding threads of monitor\n");
-               usage_with_options(stat_usage, options);
-       }
-
-       if (system_wide)
-               evsel_list->cpus = cpu_map__new(cpu_list);
-       else
-               evsel_list->cpus = cpu_map__dummy_new();
+       if (perf_evlist__create_maps(evsel_list, &target) < 0) {
+               if (perf_target__has_task(&target))
+                       pr_err("Problems finding threads of monitor\n");
+               if (perf_target__has_cpu(&target))
+                       perror("failed to parse CPUs map");
 
-       if (evsel_list->cpus == NULL) {
-               perror("failed to parse CPUs map");
                usage_with_options(stat_usage, options);
                return -1;
        }
index 223ffdc..6c47376 100644 (file)
@@ -1195,6 +1195,10 @@ realloc:
 static int test__PERF_RECORD(void)
 {
        struct perf_record_opts opts = {
+               .target = {
+                       .uid = UINT_MAX,
+                       .uses_mmap = true,
+               },
                .no_delay   = true,
                .freq       = 10,
                .mmap_pages = 256,
@@ -1237,8 +1241,7 @@ static int test__PERF_RECORD(void)
         * perf_evlist__prepare_workload we'll fill in the only thread
         * we're monitoring, the one forked there.
         */
-       err = perf_evlist__create_maps(evlist, opts.target_pid,
-                                      opts.target_tid, UINT_MAX, opts.cpu_list);
+       err = perf_evlist__create_maps(evlist, &opts.target);
        if (err < 0) {
                pr_debug("Not enough memory to create thread/cpu maps\n");
                goto out_delete_evlist;
@@ -1579,8 +1582,6 @@ static int __test__rdpmc(void)
        sa.sa_sigaction = segfault_handler;
        sigaction(SIGSEGV, &sa, NULL);
 
-       fprintf(stderr, "\n\n");
-
        fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
        if (fd < 0) {
                die("Error: sys_perf_event_open() syscall returned "
@@ -1605,7 +1606,7 @@ static int __test__rdpmc(void)
                loops *= 10;
 
                delta = now - stamp;
-               fprintf(stderr, "%14d: %14Lu\n", n, (long long)delta);
+               pr_debug("%14d: %14Lu\n", n, (long long)delta);
 
                delta_sum += delta;
        }
@@ -1613,7 +1614,7 @@ static int __test__rdpmc(void)
        munmap(addr, page_size);
        close(fd);
 
-       fprintf(stderr, "   ");
+       pr_debug("   ");
 
        if (!delta_sum)
                return -1;
index 8ef59f8..3e981a7 100644 (file)
@@ -588,7 +588,7 @@ static void *display_thread_tui(void *arg)
         * via --uid.
         */
        list_for_each_entry(pos, &top->evlist->entries, node)
-               pos->hists.uid_filter_str = top->uid_str;
+               pos->hists.uid_filter_str = top->target.uid_str;
 
        perf_evlist__tui_browse_hists(top->evlist, help,
                                      perf_top__sort_new_samples,
@@ -948,6 +948,10 @@ try_again:
 
                                attr->type = PERF_TYPE_SOFTWARE;
                                attr->config = PERF_COUNT_SW_CPU_CLOCK;
+                               if (counter->name) {
+                                       free(counter->name);
+                                       counter->name = strdup(event_name(counter));
+                               }
                                goto try_again;
                        }
 
@@ -1016,7 +1020,7 @@ static int __cmd_top(struct perf_top *top)
        if (ret)
                goto out_delete;
 
-       if (top->target_tid || top->uid != UINT_MAX)
+       if (perf_target__has_task(&top->target))
                perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
                                                  perf_event__process,
                                                  &top->session->host_machine);
@@ -1150,14 +1154,17 @@ static const char * const top_usage[] = {
 int cmd_top(int argc, const char **argv, const char *prefix __used)
 {
        struct perf_evsel *pos;
-       int status = -ENOMEM;
+       int status;
+       char errbuf[BUFSIZ];
        struct perf_top top = {
                .count_filter        = 5,
                .delay_secs          = 2,
-               .uid                 = UINT_MAX,
                .freq                = 1000, /* 1 KHz */
                .mmap_pages          = 128,
                .sym_pcnt_filter     = 5,
+               .target              = {
+                       .uses_mmap   = true,
+               },
        };
        char callchain_default_opt[] = "fractal,0.5,callee";
        const struct option options[] = {
@@ -1166,13 +1173,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
                     parse_events_option),
        OPT_INTEGER('c', "count", &top.default_interval,
                    "event period to sample"),
-       OPT_STRING('p', "pid", &top.target_pid, "pid",
+       OPT_STRING('p', "pid", &top.target.pid, "pid",
                    "profile events on existing process id"),
-       OPT_STRING('t', "tid", &top.target_tid, "tid",
+       OPT_STRING('t', "tid", &top.target.tid, "tid",
                    "profile events on existing thread id"),
-       OPT_BOOLEAN('a', "all-cpus", &top.system_wide,
+       OPT_BOOLEAN('a', "all-cpus", &top.target.system_wide,
                            "system-wide collection from all CPUs"),
-       OPT_STRING('C', "cpu", &top.cpu_list, "cpu",
+       OPT_STRING('C', "cpu", &top.target.cpu_list, "cpu",
                    "list of cpus to monitor"),
        OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
                   "file", "vmlinux pathname"),
@@ -1227,7 +1234,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
                    "Display raw encoding of assembly instructions (default)"),
        OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
                   "Specify disassembler style (e.g. -M intel for intel syntax)"),
-       OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"),
+       OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"),
        OPT_END()
        };
 
@@ -1253,22 +1260,27 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
 
        setup_browser(false);
 
-       top.uid = parse_target_uid(top.uid_str, top.target_tid, top.target_pid);
-       if (top.uid_str != NULL && top.uid == UINT_MAX - 1)
-               goto out_delete_evlist;
+       status = perf_target__validate(&top.target);
+       if (status) {
+               perf_target__strerror(&top.target, status, errbuf, BUFSIZ);
+               ui__warning("%s", errbuf);
+       }
 
-       /* CPU and PID are mutually exclusive */
-       if (top.target_tid && top.cpu_list) {
-               printf("WARNING: PID switch overriding CPU\n");
-               sleep(1);
-               top.cpu_list = NULL;
+       status = perf_target__parse_uid(&top.target);
+       if (status) {
+               int saved_errno = errno;
+
+               perf_target__strerror(&top.target, status, errbuf, BUFSIZ);
+               ui__warning("%s", errbuf);
+
+               status = -saved_errno;
+               goto out_delete_evlist;
        }
 
-       if (top.target_pid)
-               top.target_tid = top.target_pid;
+       if (perf_target__none(&top.target))
+               top.target.system_wide = true;
 
-       if (perf_evlist__create_maps(top.evlist, top.target_pid,
-                                    top.target_tid, top.uid, top.cpu_list) < 0)
+       if (perf_evlist__create_maps(top.evlist, &top.target) < 0)
                usage_with_options(top_usage, options);
 
        if (!top.evlist->nr_entries &&
index 89e3355..14f1034 100644 (file)
@@ -207,10 +207,10 @@ extern const char perf_version_string[];
 
 void pthread__unblock_sigwinch(void);
 
+#include "util/target.h"
+
 struct perf_record_opts {
-       const char   *target_pid;
-       const char   *target_tid;
-       uid_t        uid;
+       struct perf_target target;
        bool         call_graph;
        bool         group;
        bool         inherit_stat;
@@ -223,7 +223,6 @@ struct perf_record_opts {
        bool         sample_time;
        bool         sample_id_all_missing;
        bool         exclude_guest_missing;
-       bool         system_wide;
        bool         period;
        unsigned int freq;
        unsigned int mmap_pages;
@@ -231,7 +230,6 @@ struct perf_record_opts {
        int          branch_stack;
        u64          default_interval;
        u64          user_interval;
-       const char   *cpu_list;
 };
 
 #endif
similarity index 81%
rename from tools/perf/util/ui/browser.c
rename to tools/perf/ui/browser.c
index 5568291..cde4d0f 100644 (file)
@@ -27,9 +27,12 @@ static int ui_browser__percent_color(struct ui_browser *browser,
        return HE_COLORSET_NORMAL;
 }
 
-void ui_browser__set_color(struct ui_browser *self __used, int color)
+int ui_browser__set_color(struct ui_browser *browser, int color)
 {
+       int ret = browser->current_color;
+       browser->current_color = color;
        SLsmg_set_color(color);
+       return ret;
 }
 
 void ui_browser__set_percent_color(struct ui_browser *self,
@@ -502,6 +505,12 @@ static struct ui_browser__colorset {
                .fg       = "blue",
                .bg       = "default",
        },
+       {
+               .colorset = HE_COLORSET_ADDR,
+               .name     = "addr",
+               .fg       = "magenta",
+               .bg       = "default",
+       },
        {
                .name = NULL,
        }
@@ -584,6 +593,111 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
        return row;
 }
 
+void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
+                        u16 start, u16 end)
+{
+       SLsmg_set_char_set(1);
+       ui_browser__gotorc(browser, start, column);
+       SLsmg_draw_vline(end - start + 1);
+       SLsmg_set_char_set(0);
+}
+
+void ui_browser__write_graph(struct ui_browser *browser __used, int graph)
+{
+       SLsmg_set_char_set(1);
+       SLsmg_write_char(graph);
+       SLsmg_set_char_set(0);
+}
+
+static void __ui_browser__line_arrow_up(struct ui_browser *browser,
+                                       unsigned int column,
+                                       u64 start, u64 end)
+{
+       unsigned int row, end_row;
+
+       SLsmg_set_char_set(1);
+
+       if (start < browser->top_idx + browser->height) {
+               row = start - browser->top_idx;
+               ui_browser__gotorc(browser, row, column);
+               SLsmg_write_char(SLSMG_LLCORN_CHAR);
+               ui_browser__gotorc(browser, row, column + 1);
+               SLsmg_draw_hline(2);
+
+               if (row-- == 0)
+                       goto out;
+       } else
+               row = browser->height - 1;
+
+       if (end > browser->top_idx)
+               end_row = end - browser->top_idx;
+       else
+               end_row = 0;
+
+       ui_browser__gotorc(browser, end_row, column);
+       SLsmg_draw_vline(row - end_row + 1);
+
+       ui_browser__gotorc(browser, end_row, column);
+       if (end >= browser->top_idx) {
+               SLsmg_write_char(SLSMG_ULCORN_CHAR);
+               ui_browser__gotorc(browser, end_row, column + 1);
+               SLsmg_write_char(SLSMG_HLINE_CHAR);
+               ui_browser__gotorc(browser, end_row, column + 2);
+               SLsmg_write_char(SLSMG_RARROW_CHAR);
+       }
+out:
+       SLsmg_set_char_set(0);
+}
+
+static void __ui_browser__line_arrow_down(struct ui_browser *browser,
+                                         unsigned int column,
+                                         u64 start, u64 end)
+{
+       unsigned int row, end_row;
+
+       SLsmg_set_char_set(1);
+
+       if (start >= browser->top_idx) {
+               row = start - browser->top_idx;
+               ui_browser__gotorc(browser, row, column);
+               SLsmg_write_char(SLSMG_ULCORN_CHAR);
+               ui_browser__gotorc(browser, row, column + 1);
+               SLsmg_draw_hline(2);
+
+               if (row++ == 0)
+                       goto out;
+       } else
+               row = 0;
+
+       if (end >= browser->top_idx + browser->height)
+               end_row = browser->height - 1;
+       else
+               end_row = end - browser->top_idx;;
+
+       ui_browser__gotorc(browser, row, column);
+       SLsmg_draw_vline(end_row - row + 1);
+
+       ui_browser__gotorc(browser, end_row, column);
+       if (end < browser->top_idx + browser->height) {
+               SLsmg_write_char(SLSMG_LLCORN_CHAR);
+               ui_browser__gotorc(browser, end_row, column + 1);
+               SLsmg_write_char(SLSMG_HLINE_CHAR);
+               ui_browser__gotorc(browser, end_row, column + 2);
+               SLsmg_write_char(SLSMG_RARROW_CHAR);
+       }
+out:
+       SLsmg_set_char_set(0);
+}
+
+void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
+                             u64 start, u64 end)
+{
+       if (start > end)
+               __ui_browser__line_arrow_up(browser, column, start, end);
+       else
+               __ui_browser__line_arrow_down(browser, column, start, end);
+}
+
 void ui_browser__init(void)
 {
        int i = 0;
similarity index 86%
rename from tools/perf/util/ui/browser.h
rename to tools/perf/ui/browser.h
index 6ee82f6..dd96d82 100644 (file)
 #define HE_COLORSET_NORMAL     52
 #define HE_COLORSET_SELECTED   53
 #define HE_COLORSET_CODE       54
+#define HE_COLORSET_ADDR       55
 
 struct ui_browser {
        u64           index, top_idx;
        void          *top, *entries;
        u16           y, x, width, height;
+       int           current_color;
        void          *priv;
        const char    *title;
        char          *helpline;
@@ -27,7 +29,7 @@ struct ui_browser {
        bool          use_navkeypressed;
 };
 
-void ui_browser__set_color(struct ui_browser *self, int color);
+int  ui_browser__set_color(struct ui_browser *browser, int color);
 void ui_browser__set_percent_color(struct ui_browser *self,
                                   double percent, bool current);
 bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
@@ -35,6 +37,9 @@ void ui_browser__refresh_dimensions(struct ui_browser *self);
 void ui_browser__reset_index(struct ui_browser *self);
 
 void ui_browser__gotorc(struct ui_browser *self, int y, int x);
+void ui_browser__write_graph(struct ui_browser *browser, int graph);
+void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
+                             u64 start, u64 end);
 void __ui_browser__show_title(struct ui_browser *browser, const char *title);
 void ui_browser__show_title(struct ui_browser *browser, const char *title);
 int ui_browser__show(struct ui_browser *self, const char *title,
@@ -44,6 +49,8 @@ int ui_browser__refresh(struct ui_browser *self);
 int ui_browser__run(struct ui_browser *browser, int delay_secs);
 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries);
 void ui_browser__handle_resize(struct ui_browser *browser);
+void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
+                        u16 start, u16 end);
 
 int ui_browser__warning(struct ui_browser *browser, int timeout,
                        const char *format, ...);
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
new file mode 100644 (file)
index 0000000..6e0ef79
--- /dev/null
@@ -0,0 +1,867 @@
+#include "../../util/util.h"
+#include "../browser.h"
+#include "../helpline.h"
+#include "../libslang.h"
+#include "../ui.h"
+#include "../util.h"
+#include "../../util/annotate.h"
+#include "../../util/hist.h"
+#include "../../util/sort.h"
+#include "../../util/symbol.h"
+#include <pthread.h>
+#include <newt.h>
+
+struct browser_disasm_line {
+       struct rb_node  rb_node;
+       double          percent;
+       u32             idx;
+       int             idx_asm;
+       int             jump_sources;
+};
+
+struct annotate_browser {
+       struct ui_browser b;
+       struct rb_root    entries;
+       struct rb_node    *curr_hot;
+       struct disasm_line        *selection;
+       struct disasm_line  **offsets;
+       u64                 start;
+       int                 nr_asm_entries;
+       int                 nr_entries;
+       int                 max_jump_sources;
+       int                 nr_jumps;
+       bool                hide_src_code;
+       bool                use_offset;
+       bool                jump_arrows;
+       bool                show_nr_jumps;
+       bool                searching_backwards;
+       u8                  addr_width;
+       u8                  jumps_width;
+       u8                  target_width;
+       u8                  min_addr_width;
+       u8                  max_addr_width;
+       char                search_bf[128];
+};
+
+static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
+{
+       return (struct browser_disasm_line *)(dl + 1);
+}
+
+static bool disasm_line__filter(struct ui_browser *browser, void *entry)
+{
+       struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+
+       if (ab->hide_src_code) {
+               struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
+               return dl->offset == -1;
+       }
+
+       return false;
+}
+
+static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
+                                                int nr, bool current)
+{
+       if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
+               return HE_COLORSET_SELECTED;
+       if (nr == browser->max_jump_sources)
+               return HE_COLORSET_TOP;
+       if (nr > 1)
+               return HE_COLORSET_MEDIUM;
+       return HE_COLORSET_NORMAL;
+}
+
+static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
+                                                    int nr, bool current)
+{
+        int color = annotate_browser__jumps_percent_color(browser, nr, current);
+        return ui_browser__set_color(&browser->b, color);
+}
+
+static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
+{
+       struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
+       struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
+       struct browser_disasm_line *bdl = disasm_line__browser(dl);
+       bool current_entry = ui_browser__is_current_entry(self, row);
+       bool change_color = (!ab->hide_src_code &&
+                            (!current_entry || (self->use_navkeypressed &&
+                                                !self->navkeypressed)));
+       int width = self->width, printed;
+       char bf[256];
+
+       if (dl->offset != -1 && bdl->percent != 0.0) {
+               ui_browser__set_percent_color(self, bdl->percent, current_entry);
+               slsmg_printf("%6.2f ", bdl->percent);
+       } else {
+               ui_browser__set_percent_color(self, 0, current_entry);
+               slsmg_write_nstring(" ", 7);
+       }
+
+       SLsmg_write_char(' ');
+
+       /* The scroll bar isn't being used */
+       if (!self->navkeypressed)
+               width += 1;
+
+       if (!*dl->line)
+               slsmg_write_nstring(" ", width - 7);
+       else if (dl->offset == -1) {
+               printed = scnprintf(bf, sizeof(bf), "%*s  ",
+                                   ab->addr_width, " ");
+               slsmg_write_nstring(bf, printed);
+               slsmg_write_nstring(dl->line, width - printed - 6);
+       } else {
+               u64 addr = dl->offset;
+               int color = -1;
+
+               if (!ab->use_offset)
+                       addr += ab->start;
+
+               if (!ab->use_offset) {
+                       printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
+               } else {
+                       if (bdl->jump_sources) {
+                               if (ab->show_nr_jumps) {
+                                       int prev;
+                                       printed = scnprintf(bf, sizeof(bf), "%*d ",
+                                                           ab->jumps_width,
+                                                           bdl->jump_sources);
+                                       prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
+                                                                                        current_entry);
+                                       slsmg_write_nstring(bf, printed);
+                                       ui_browser__set_color(self, prev);
+                               }
+
+                               printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
+                                                   ab->target_width, addr);
+                       } else {
+                               printed = scnprintf(bf, sizeof(bf), "%*s  ",
+                                                   ab->addr_width, " ");
+                       }
+               }
+
+               if (change_color)
+                       color = ui_browser__set_color(self, HE_COLORSET_ADDR);
+               slsmg_write_nstring(bf, printed);
+               if (change_color)
+                       ui_browser__set_color(self, color);
+               if (dl->ins && dl->ins->ops->scnprintf) {
+                       if (ins__is_jump(dl->ins)) {
+                               bool fwd = dl->ops.target.offset > (u64)dl->offset;
+
+                               ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
+                                                                   SLSMG_UARROW_CHAR);
+                               SLsmg_write_char(' ');
+                       } else if (ins__is_call(dl->ins)) {
+                               ui_browser__write_graph(self, SLSMG_RARROW_CHAR);
+                               SLsmg_write_char(' ');
+                       } else {
+                               slsmg_write_nstring(" ", 2);
+                       }
+               } else {
+                       if (strcmp(dl->name, "retq")) {
+                               slsmg_write_nstring(" ", 2);
+                       } else {
+                               ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
+                               SLsmg_write_char(' ');
+                       }
+               }
+
+               disasm_line__scnprintf(dl, bf, sizeof(bf), !ab->use_offset);
+               slsmg_write_nstring(bf, width - 10 - printed);
+       }
+
+       if (current_entry)
+               ab->selection = dl;
+}
+
+static void annotate_browser__draw_current_jump(struct ui_browser *browser)
+{
+       struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+       struct disasm_line *cursor = ab->selection, *target;
+       struct browser_disasm_line *btarget, *bcursor;
+       unsigned int from, to;
+
+       if (!cursor->ins || !ins__is_jump(cursor->ins) ||
+           !disasm_line__has_offset(cursor))
+               return;
+
+       target = ab->offsets[cursor->ops.target.offset];
+       if (!target)
+               return;
+
+       bcursor = disasm_line__browser(cursor);
+       btarget = disasm_line__browser(target);
+
+       if (ab->hide_src_code) {
+               from = bcursor->idx_asm;
+               to = btarget->idx_asm;
+       } else {
+               from = (u64)bcursor->idx;
+               to = (u64)btarget->idx;
+       }
+
+       ui_browser__set_color(browser, HE_COLORSET_CODE);
+       __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
+}
+
+static unsigned int annotate_browser__refresh(struct ui_browser *browser)
+{
+       struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+       int ret = ui_browser__list_head_refresh(browser);
+
+       if (ab->jump_arrows)
+               annotate_browser__draw_current_jump(browser);
+
+       ui_browser__set_color(browser, HE_COLORSET_NORMAL);
+       __ui_browser__vline(browser, 7, 0, browser->height - 1);
+       return ret;
+}
+
+static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
+{
+       double percent = 0.0;
+
+       if (dl->offset != -1) {
+               int len = sym->end - sym->start;
+               unsigned int hits = 0;
+               struct annotation *notes = symbol__annotation(sym);
+               struct source_line *src_line = notes->src->lines;
+               struct sym_hist *h = annotation__histogram(notes, evidx);
+               s64 offset = dl->offset;
+               struct disasm_line *next;
+
+               next = disasm__get_next_ip_line(&notes->src->source, dl);
+               while (offset < (s64)len &&
+                      (next == NULL || offset < next->offset)) {
+                       if (src_line) {
+                               percent += src_line[offset].percent;
+                       } else
+                               hits += h->addr[offset];
+
+                       ++offset;
+               }
+               /*
+                * If the percentage wasn't already calculated in
+                * symbol__get_source_line, do it now:
+                */
+               if (src_line == NULL && h->sum)
+                       percent = 100.0 * hits / h->sum;
+       }
+
+       return percent;
+}
+
+static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
+{
+       struct rb_node **p = &root->rb_node;
+       struct rb_node *parent = NULL;
+       struct browser_disasm_line *l;
+
+       while (*p != NULL) {
+               parent = *p;
+               l = rb_entry(parent, struct browser_disasm_line, rb_node);
+               if (bdl->percent < l->percent)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+       rb_link_node(&bdl->rb_node, parent, p);
+       rb_insert_color(&bdl->rb_node, root);
+}
+
+static void annotate_browser__set_top(struct annotate_browser *self,
+                                     struct disasm_line *pos, u32 idx)
+{
+       unsigned back;
+
+       ui_browser__refresh_dimensions(&self->b);
+       back = self->b.height / 2;
+       self->b.top_idx = self->b.index = idx;
+
+       while (self->b.top_idx != 0 && back != 0) {
+               pos = list_entry(pos->node.prev, struct disasm_line, node);
+
+               if (disasm_line__filter(&self->b, &pos->node))
+                       continue;
+
+               --self->b.top_idx;
+               --back;
+       }
+
+       self->b.top = pos;
+       self->b.navkeypressed = true;
+}
+
+static void annotate_browser__set_rb_top(struct annotate_browser *browser,
+                                        struct rb_node *nd)
+{
+       struct browser_disasm_line *bpos;
+       struct disasm_line *pos;
+
+       bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
+       pos = ((struct disasm_line *)bpos) - 1;
+       annotate_browser__set_top(browser, pos, bpos->idx);
+       browser->curr_hot = nd;
+}
+
+static void annotate_browser__calc_percent(struct annotate_browser *browser,
+                                          int evidx)
+{
+       struct map_symbol *ms = browser->b.priv;
+       struct symbol *sym = ms->sym;
+       struct annotation *notes = symbol__annotation(sym);
+       struct disasm_line *pos;
+
+       browser->entries = RB_ROOT;
+
+       pthread_mutex_lock(&notes->lock);
+
+       list_for_each_entry(pos, &notes->src->source, node) {
+               struct browser_disasm_line *bpos = disasm_line__browser(pos);
+               bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
+               if (bpos->percent < 0.01) {
+                       RB_CLEAR_NODE(&bpos->rb_node);
+                       continue;
+               }
+               disasm_rb_tree__insert(&browser->entries, bpos);
+       }
+       pthread_mutex_unlock(&notes->lock);
+
+       browser->curr_hot = rb_last(&browser->entries);
+}
+
+static bool annotate_browser__toggle_source(struct annotate_browser *browser)
+{
+       struct disasm_line *dl;
+       struct browser_disasm_line *bdl;
+       off_t offset = browser->b.index - browser->b.top_idx;
+
+       browser->b.seek(&browser->b, offset, SEEK_CUR);
+       dl = list_entry(browser->b.top, struct disasm_line, node);
+       bdl = disasm_line__browser(dl);
+
+       if (browser->hide_src_code) {
+               if (bdl->idx_asm < offset)
+                       offset = bdl->idx;
+
+               browser->b.nr_entries = browser->nr_entries;
+               browser->hide_src_code = false;
+               browser->b.seek(&browser->b, -offset, SEEK_CUR);
+               browser->b.top_idx = bdl->idx - offset;
+               browser->b.index = bdl->idx;
+       } else {
+               if (bdl->idx_asm < 0) {
+                       ui_helpline__puts("Only available for assembly lines.");
+                       browser->b.seek(&browser->b, -offset, SEEK_CUR);
+                       return false;
+               }
+
+               if (bdl->idx_asm < offset)
+                       offset = bdl->idx_asm;
+
+               browser->b.nr_entries = browser->nr_asm_entries;
+               browser->hide_src_code = true;
+               browser->b.seek(&browser->b, -offset, SEEK_CUR);
+               browser->b.top_idx = bdl->idx_asm - offset;
+               browser->b.index = bdl->idx_asm;
+       }
+
+       return true;
+}
+
+static bool annotate_browser__callq(struct annotate_browser *browser,
+                                   int evidx, void (*timer)(void *arg),
+                                   void *arg, int delay_secs)
+{
+       struct map_symbol *ms = browser->b.priv;
+       struct disasm_line *dl = browser->selection;
+       struct symbol *sym = ms->sym;
+       struct annotation *notes;
+       struct symbol *target;
+       u64 ip;
+
+       if (!ins__is_call(dl->ins))
+               return false;
+
+       ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
+       target = map__find_symbol(ms->map, ip, NULL);
+       if (target == NULL) {
+               ui_helpline__puts("The called function was not found.");
+               return true;
+       }
+
+       notes = symbol__annotation(target);
+       pthread_mutex_lock(&notes->lock);
+
+       if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
+               pthread_mutex_unlock(&notes->lock);
+               ui__warning("Not enough memory for annotating '%s' symbol!\n",
+                           target->name);
+               return true;
+       }
+
+       pthread_mutex_unlock(&notes->lock);
+       symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
+       ui_browser__show_title(&browser->b, sym->name);
+       return true;
+}
+
+static
+struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
+                                         s64 offset, s64 *idx)
+{
+       struct map_symbol *ms = browser->b.priv;
+       struct symbol *sym = ms->sym;
+       struct annotation *notes = symbol__annotation(sym);
+       struct disasm_line *pos;
+
+       *idx = 0;
+       list_for_each_entry(pos, &notes->src->source, node) {
+               if (pos->offset == offset)
+                       return pos;
+               if (!disasm_line__filter(&browser->b, &pos->node))
+                       ++*idx;
+       }
+
+       return NULL;
+}
+
+static bool annotate_browser__jump(struct annotate_browser *browser)
+{
+       struct disasm_line *dl = browser->selection;
+       s64 idx;
+
+       if (!ins__is_jump(dl->ins))
+               return false;
+
+       dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
+       if (dl == NULL) {
+               ui_helpline__puts("Invallid jump offset");
+               return true;
+       }
+
+       annotate_browser__set_top(browser, dl, idx);
+       
+       return true;
+}
+
+static
+struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
+                                         char *s, s64 *idx)
+{
+       struct map_symbol *ms = browser->b.priv;
+       struct symbol *sym = ms->sym;
+       struct annotation *notes = symbol__annotation(sym);
+       struct disasm_line *pos = browser->selection;
+
+       *idx = browser->b.index;
+       list_for_each_entry_continue(pos, &notes->src->source, node) {
+               if (disasm_line__filter(&browser->b, &pos->node))
+                       continue;
+
+               ++*idx;
+
+               if (pos->line && strstr(pos->line, s) != NULL)
+                       return pos;
+       }
+
+       return NULL;
+}
+
+static bool __annotate_browser__search(struct annotate_browser *browser)
+{
+       struct disasm_line *dl;
+       s64 idx;
+
+       dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
+       if (dl == NULL) {
+               ui_helpline__puts("String not found!");
+               return false;
+       }
+
+       annotate_browser__set_top(browser, dl, idx);
+       browser->searching_backwards = false;
+       return true;
+}
+
+static
+struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
+                                                 char *s, s64 *idx)
+{
+       struct map_symbol *ms = browser->b.priv;
+       struct symbol *sym = ms->sym;
+       struct annotation *notes = symbol__annotation(sym);
+       struct disasm_line *pos = browser->selection;
+
+       *idx = browser->b.index;
+       list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
+               if (disasm_line__filter(&browser->b, &pos->node))
+                       continue;
+
+               --*idx;
+
+               if (pos->line && strstr(pos->line, s) != NULL)
+                       return pos;
+       }
+
+       return NULL;
+}
+
+static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
+{
+       struct disasm_line *dl;
+       s64 idx;
+
+       dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
+       if (dl == NULL) {
+               ui_helpline__puts("String not found!");
+               return false;
+       }
+
+       annotate_browser__set_top(browser, dl, idx);
+       browser->searching_backwards = true;
+       return true;
+}
+
+static bool annotate_browser__search_window(struct annotate_browser *browser,
+                                           int delay_secs)
+{
+       if (ui_browser__input_window("Search", "String: ", browser->search_bf,
+                                    "ENTER: OK, ESC: Cancel",
+                                    delay_secs * 2) != K_ENTER ||
+           !*browser->search_bf)
+               return false;
+
+       return true;
+}
+
+static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
+{
+       if (annotate_browser__search_window(browser, delay_secs))
+               return __annotate_browser__search(browser);
+
+       return false;
+}
+
+static bool annotate_browser__continue_search(struct annotate_browser *browser,
+                                             int delay_secs)
+{
+       if (!*browser->search_bf)
+               return annotate_browser__search(browser, delay_secs);
+
+       return __annotate_browser__search(browser);
+}
+
+static bool annotate_browser__search_reverse(struct annotate_browser *browser,
+                                          int delay_secs)
+{
+       if (annotate_browser__search_window(browser, delay_secs))
+               return __annotate_browser__search_reverse(browser);
+
+       return false;
+}
+
+static
+bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
+                                              int delay_secs)
+{
+       if (!*browser->search_bf)
+               return annotate_browser__search_reverse(browser, delay_secs);
+
+       return __annotate_browser__search_reverse(browser);
+}
+
+static int annotate_browser__run(struct annotate_browser *self, int evidx,
+                                void(*timer)(void *arg),
+                                void *arg, int delay_secs)
+{
+       struct rb_node *nd = NULL;
+       struct map_symbol *ms = self->b.priv;
+       struct symbol *sym = ms->sym;
+       const char *help = "Press 'h' for help on key bindings";
+       int key;
+
+       if (ui_browser__show(&self->b, sym->name, help) < 0)
+               return -1;
+
+       annotate_browser__calc_percent(self, evidx);
+
+       if (self->curr_hot) {
+               annotate_browser__set_rb_top(self, self->curr_hot);
+               self->b.navkeypressed = false;
+       }
+
+       nd = self->curr_hot;
+
+       while (1) {
+               key = ui_browser__run(&self->b, delay_secs);
+
+               if (delay_secs != 0) {
+                       annotate_browser__calc_percent(self, evidx);
+                       /*
+                        * Current line focus got out of the list of most active
+                        * lines, NULL it so that if TAB|UNTAB is pressed, we
+                        * move to curr_hot (current hottest line).
+                        */
+                       if (nd != NULL && RB_EMPTY_NODE(nd))
+                               nd = NULL;
+               }
+
+               switch (key) {
+               case K_TIMER:
+                       if (timer != NULL)
+                               timer(arg);
+
+                       if (delay_secs != 0)
+                               symbol__annotate_decay_histogram(sym, evidx);
+                       continue;
+               case K_TAB:
+                       if (nd != NULL) {
+                               nd = rb_prev(nd);
+                               if (nd == NULL)
+                                       nd = rb_last(&self->entries);
+                       } else
+                               nd = self->curr_hot;
+                       break;
+               case K_UNTAB:
+                       if (nd != NULL)
+                               nd = rb_next(nd);
+                               if (nd == NULL)
+                                       nd = rb_first(&self->entries);
+                       else
+                               nd = self->curr_hot;
+                       break;
+               case K_F1:
+               case 'h':
+                       ui_browser__help_window(&self->b,
+               "UP/DOWN/PGUP\n"
+               "PGDN/SPACE    Navigate\n"
+               "q/ESC/CTRL+C  Exit\n\n"
+               "->            Go to target\n"
+               "<-            Exit\n"
+               "h             Cycle thru hottest instructions\n"
+               "j             Toggle showing jump to target arrows\n"
+               "J             Toggle showing number of jump sources on targets\n"
+               "n             Search next string\n"
+               "o             Toggle disassembler output/simplified view\n"
+               "s             Toggle source code view\n"
+               "/             Search string\n"
+               "?             Search previous string\n");
+                       continue;
+               case 'H':
+                       nd = self->curr_hot;
+                       break;
+               case 's':
+                       if (annotate_browser__toggle_source(self))
+                               ui_helpline__puts(help);
+                       continue;
+               case 'o':
+                       self->use_offset = !self->use_offset;
+                       if (self->use_offset)
+                               self->target_width = self->min_addr_width;
+                       else
+                               self->target_width = self->max_addr_width;
+update_addr_width:
+                       self->addr_width = self->target_width;
+                       if (self->show_nr_jumps)
+                               self->addr_width += self->jumps_width + 1;
+                       continue;
+               case 'j':
+                       self->jump_arrows = !self->jump_arrows;
+                       continue;
+               case 'J':
+                       self->show_nr_jumps = !self->show_nr_jumps;
+                       goto update_addr_width;
+               case '/':
+                       if (annotate_browser__search(self, delay_secs)) {
+show_help:
+                               ui_helpline__puts(help);
+                       }
+                       continue;
+               case 'n':
+                       if (self->searching_backwards ?
+                           annotate_browser__continue_search_reverse(self, delay_secs) :
+                           annotate_browser__continue_search(self, delay_secs))
+                               goto show_help;
+                       continue;
+               case '?':
+                       if (annotate_browser__search_reverse(self, delay_secs))
+                               goto show_help;
+                       continue;
+               case K_ENTER:
+               case K_RIGHT:
+                       if (self->selection == NULL)
+                               ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
+                       else if (self->selection->offset == -1)
+                               ui_helpline__puts("Actions are only available for assembly lines.");
+                       else if (!self->selection->ins) {
+                               if (strcmp(self->selection->name, "retq"))
+                                       goto show_sup_ins;
+                               goto out;
+                       } else if (!(annotate_browser__jump(self) ||
+                                    annotate_browser__callq(self, evidx, timer, arg, delay_secs))) {
+show_sup_ins:
+                               ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
+                       }
+                       continue;
+               case K_LEFT:
+               case K_ESC:
+               case 'q':
+               case CTRL('c'):
+                       goto out;
+               default:
+                       continue;
+               }
+
+               if (nd != NULL)
+                       annotate_browser__set_rb_top(self, nd);
+       }
+out:
+       ui_browser__hide(&self->b);
+       return key;
+}
+
+int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
+                            void(*timer)(void *arg), void *arg, int delay_secs)
+{
+       return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
+                                   timer, arg, delay_secs);
+}
+
+static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
+                                               size_t size)
+{
+       u64 offset;
+
+       for (offset = 0; offset < size; ++offset) {
+               struct disasm_line *dl = browser->offsets[offset], *dlt;
+               struct browser_disasm_line *bdlt;
+
+               if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
+                   !disasm_line__has_offset(dl))
+                       continue;
+
+               if (dl->ops.target.offset >= size) {
+                       ui__error("jump to after symbol!\n"
+                                 "size: %zx, jump target: %" PRIx64,
+                                 size, dl->ops.target.offset);
+                       continue;
+               }
+
+               dlt = browser->offsets[dl->ops.target.offset];
+               /*
+                * FIXME: Oops, no jump target? Buggy disassembler? Or do we
+                * have to adjust to the previous offset?
+                */
+               if (dlt == NULL)
+                       continue;
+
+               bdlt = disasm_line__browser(dlt);
+               if (++bdlt->jump_sources > browser->max_jump_sources)
+                       browser->max_jump_sources = bdlt->jump_sources;
+
+               ++browser->nr_jumps;
+       }
+               
+}
+
+static inline int width_jumps(int n)
+{
+       if (n >= 100)
+               return 5;
+       if (n / 10)
+               return 2;
+       return 1;
+}
+
+int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
+                        void(*timer)(void *arg), void *arg,
+                        int delay_secs)
+{
+       struct disasm_line *pos, *n;
+       struct annotation *notes;
+       const size_t size = symbol__size(sym);
+       struct map_symbol ms = {
+               .map = map,
+               .sym = sym,
+       };
+       struct annotate_browser browser = {
+               .b = {
+                       .refresh = annotate_browser__refresh,
+                       .seek    = ui_browser__list_head_seek,
+                       .write   = annotate_browser__write,
+                       .filter  = disasm_line__filter,
+                       .priv    = &ms,
+                       .use_navkeypressed = true,
+               },
+               .use_offset = true,
+               .jump_arrows = true,
+       };
+       int ret = -1;
+
+       if (sym == NULL)
+               return -1;
+
+       if (map->dso->annotate_warned)
+               return -1;
+
+       browser.offsets = zalloc(size * sizeof(struct disasm_line *));
+       if (browser.offsets == NULL) {
+               ui__error("Not enough memory!");
+               return -1;
+       }
+
+       if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
+               ui__error("%s", ui_helpline__last_msg);
+               goto out_free_offsets;
+       }
+
+       ui_helpline__push("Press <- or ESC to exit");
+
+       notes = symbol__annotation(sym);
+       browser.start = map__rip_2objdump(map, sym->start);
+
+       list_for_each_entry(pos, &notes->src->source, node) {
+               struct browser_disasm_line *bpos;
+               size_t line_len = strlen(pos->line);
+
+               if (browser.b.width < line_len)
+                       browser.b.width = line_len;
+               bpos = disasm_line__browser(pos);
+               bpos->idx = browser.nr_entries++;
+               if (pos->offset != -1) {
+                       bpos->idx_asm = browser.nr_asm_entries++;
+                       /*
+                        * FIXME: short term bandaid to cope with assembly
+                        * routines that comes with labels in the same column
+                        * as the address in objdump, sigh.
+                        *
+                        * E.g. copy_user_generic_unrolled
+                        */
+                       if (pos->offset < (s64)size)
+                               browser.offsets[pos->offset] = pos;
+               } else
+                       bpos->idx_asm = -1;
+       }
+
+       annotate_browser__mark_jump_targets(&browser, size);
+
+       browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
+       browser.max_addr_width = hex_width(sym->end);
+       browser.jumps_width = width_jumps(browser.max_jump_sources);
+       browser.b.nr_entries = browser.nr_entries;
+       browser.b.entries = &notes->src->source,
+       browser.b.width += 18; /* Percentage */
+       ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
+       list_for_each_entry_safe(pos, n, &notes->src->source, node) {
+               list_del(&pos->node);
+               disasm_line__free(pos);
+       }
+
+out_free_offsets:
+       free(browser.offsets);
+       return ret;
+}
similarity index 98%
rename from tools/perf/util/ui/browsers/hists.c
rename to tools/perf/ui/browsers/hists.c
index 2f83e5d..a372a4b 100644 (file)
@@ -5,12 +5,12 @@
 #include <newt.h>
 #include <linux/rbtree.h>
 
-#include "../../evsel.h"
-#include "../../evlist.h"
-#include "../../hist.h"
-#include "../../pstack.h"
-#include "../../sort.h"
-#include "../../util.h"
+#include "../../util/evsel.h"
+#include "../../util/evlist.h"
+#include "../../util/hist.h"
+#include "../../util/pstack.h"
+#include "../../util/sort.h"
+#include "../../util/util.h"
 
 #include "../browser.h"
 #include "../helpline.h"
@@ -840,10 +840,14 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
        int printed;
        const struct dso *dso = self->dso_filter;
        const struct thread *thread = self->thread_filter;
-       unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
+       unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
+       u64 nr_events = self->stats.total_period;
+
+       nr_samples = convert_unit(nr_samples, &unit);
+       printed = scnprintf(bf, size,
+                          "Samples: %lu%c of event '%s', Event count (approx.): %lu",
+                          nr_samples, unit, ev_name, nr_events);
 
-       nr_events = convert_unit(nr_events, &unit);
-       printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
 
        if (self->uid_filter_str)
                printed += snprintf(bf + printed, size - printed,
@@ -937,7 +941,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                        goto zoom_dso;
                case 't':
                        goto zoom_thread;
-               case 's':
+               case '/':
                        if (ui_browser__input_window("Symbol to show",
                                        "Please enter the name of symbol you want to see",
                                        buf, "ENTER: OK, ESC: Cancel",
@@ -965,7 +969,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                        "E             Expand all callchains\n"
                                        "d             Zoom into current DSO\n"
                                        "t             Zoom into current Thread\n"
-                                       "s             Filter symbol by name");
+                                       "/             Filter symbol by name");
                        continue;
                case K_ENTER:
                case K_RIGHT:
similarity index 97%
rename from tools/perf/util/ui/browsers/map.c
rename to tools/perf/ui/browsers/map.c
index eca6575..98851d5 100644 (file)
@@ -5,9 +5,9 @@
 #include <sys/ttydefaults.h>
 #include <string.h>
 #include <linux/bitops.h>
-#include "../../util.h"
-#include "../../debug.h"
-#include "../../symbol.h"
+#include "../../util/util.h"
+#include "../../util/debug.h"
+#include "../../util/symbol.h"
 #include "../browser.h"
 #include "../helpline.h"
 #include "map.h"
similarity index 85%
rename from tools/perf/util/gtk/browser.c
rename to tools/perf/ui/gtk/browser.c
index 258352a..0656c38 100644 (file)
@@ -9,24 +9,13 @@
 
 #define MAX_COLUMNS                    32
 
-void perf_gtk_setup_browser(int argc, const char *argv[],
-                           bool fallback_to_pager __used)
-{
-       gtk_init(&argc, (char ***)&argv);
-}
-
-void perf_gtk_exit_browser(bool wait_for_ok __used)
-{
-       gtk_main_quit();
-}
-
-static void perf_gtk_signal(int sig)
+static void perf_gtk__signal(int sig)
 {
        psignal(sig, "perf");
        gtk_main_quit();
 }
 
-static void perf_gtk_resize_window(GtkWidget *window)
+static void perf_gtk__resize_window(GtkWidget *window)
 {
        GdkRectangle rect;
        GdkScreen *screen;
@@ -46,7 +35,7 @@ static void perf_gtk_resize_window(GtkWidget *window)
        gtk_window_resize(GTK_WINDOW(window), width, height);
 }
 
-static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists)
+static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
 {
        GType col_types[MAX_COLUMNS];
        GtkCellRenderer *renderer;
@@ -142,11 +131,11 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
        GtkWidget *notebook;
        GtkWidget *window;
 
-       signal(SIGSEGV, perf_gtk_signal);
-       signal(SIGFPE,  perf_gtk_signal);
-       signal(SIGINT,  perf_gtk_signal);
-       signal(SIGQUIT, perf_gtk_signal);
-       signal(SIGTERM, perf_gtk_signal);
+       signal(SIGSEGV, perf_gtk__signal);
+       signal(SIGFPE,  perf_gtk__signal);
+       signal(SIGINT,  perf_gtk__signal);
+       signal(SIGQUIT, perf_gtk__signal);
+       signal(SIGTERM, perf_gtk__signal);
 
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
@@ -168,7 +157,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
                                                        GTK_POLICY_AUTOMATIC,
                                                        GTK_POLICY_AUTOMATIC);
 
-               perf_gtk_show_hists(scrolled_window, hists);
+               perf_gtk__show_hists(scrolled_window, hists);
 
                tab_label = gtk_label_new(evname);
 
@@ -179,7 +168,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
 
        gtk_widget_show_all(window);
 
-       perf_gtk_resize_window(window);
+       perf_gtk__resize_window(window);
 
        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 
diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c
new file mode 100644 (file)
index 0000000..8295299
--- /dev/null
@@ -0,0 +1,12 @@
+#include "gtk.h"
+#include "../../util/cache.h"
+
+int perf_gtk__init(void)
+{
+       return gtk_init_check(NULL, NULL) ? 0 : -1;
+}
+
+void perf_gtk__exit(bool wait_for_ok __used)
+{
+       gtk_main_quit();
+}
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
new file mode 100644 (file)
index 0000000..9f5f888
--- /dev/null
@@ -0,0 +1,45 @@
+#include "../cache.h"
+#include "../debug.h"
+
+
+void setup_browser(bool fallback_to_pager)
+{
+       if (!isatty(1) || dump_trace)
+               use_browser = 0;
+
+       /* default to TUI */
+       if (use_browser < 0)
+               use_browser = 1;
+
+       switch (use_browser) {
+       case 2:
+               if (perf_gtk__init() == 0)
+                       break;
+               /* fall through */
+       case 1:
+               use_browser = 1;
+               if (ui__init() == 0)
+                       break;
+               /* fall through */
+       default:
+               if (fallback_to_pager)
+                       setup_pager();
+               break;
+       }
+}
+
+void exit_browser(bool wait_for_ok)
+{
+       switch (use_browser) {
+       case 2:
+               perf_gtk__exit(wait_for_ok);
+               break;
+
+       case 1:
+               ui__exit(wait_for_ok);
+               break;
+
+       default:
+               break;
+       }
+}
similarity index 74%
rename from tools/perf/util/ui/setup.c
rename to tools/perf/ui/tui/setup.c
index 85a69fa..d33e943 100644 (file)
@@ -2,14 +2,14 @@
 #include <signal.h>
 #include <stdbool.h>
 
-#include "../cache.h"
-#include "../debug.h"
-#include "browser.h"
-#include "helpline.h"
-#include "ui.h"
-#include "util.h"
-#include "libslang.h"
-#include "keysyms.h"
+#include "../../util/cache.h"
+#include "../../util/debug.h"
+#include "../browser.h"
+#include "../helpline.h"
+#include "../ui.h"
+#include "../util.h"
+#include "../libslang.h"
+#include "../keysyms.h"
 
 pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
 
@@ -93,45 +93,26 @@ static void newt_suspend(void *d __used)
        newtResume();
 }
 
-static int ui__init(void)
-{
-       int err = SLkp_init();
-
-       if (err < 0)
-               goto out;
-
-       SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
-out:
-       return err;
-}
-
-static void ui__exit(void)
-{
-       SLtt_set_cursor_visibility(1);
-       SLsmg_refresh();
-       SLsmg_reset_smg();
-       SLang_reset_tty();
-}
-
 static void ui__signal(int sig)
 {
-       ui__exit();
+       ui__exit(false);
        psignal(sig, "perf");
        exit(0);
 }
 
-void setup_browser(bool fallback_to_pager)
+int ui__init(void)
 {
-       if (!isatty(1) || !use_browser || dump_trace) {
-               use_browser = 0;
-               if (fallback_to_pager)
-                       setup_pager();
-               return;
-       }
+       int err;
 
-       use_browser = 1;
        newtInit();
-       ui__init();
+       err = SLkp_init();
+       if (err < 0) {
+               pr_err("TUI initialization failed.\n");
+               goto out;
+       }
+
+       SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
+
        newtSetSuspendCallback(newt_suspend, NULL);
        ui_helpline__init();
        ui_browser__init();
@@ -141,15 +122,19 @@ void setup_browser(bool fallback_to_pager)
        signal(SIGINT, ui__signal);
        signal(SIGQUIT, ui__signal);
        signal(SIGTERM, ui__signal);
+out:
+       return err;
 }
 
-void exit_browser(bool wait_for_ok)
+void ui__exit(bool wait_for_ok)
 {
-       if (use_browser > 0) {
-               if (wait_for_ok)
-                       ui__question_window("Fatal Error",
-                                           ui_helpline__last_msg,
-                                           "Press any key...", 0);
-               ui__exit();
-       }
+       if (wait_for_ok)
+               ui__question_window("Fatal Error",
+                                   ui_helpline__last_msg,
+                                   "Press any key...", 0);
+
+       SLtt_set_cursor_visibility(1);
+       SLsmg_refresh();
+       SLsmg_reset_smg();
+       SLang_reset_tty();
 }
similarity index 100%
rename from tools/perf/util/ui/ui.h
rename to tools/perf/ui/ui.h
index 08c6d13..8069dfb 100644 (file)
 
 const char     *disassembler_style;
 
+static struct ins *ins__find(const char *name);
+static int disasm_line__parse(char *line, char **namep, char **rawp);
+
+static void ins__delete(struct ins_operands *ops)
+{
+       free(ops->source.raw);
+       free(ops->source.name);
+       free(ops->target.raw);
+       free(ops->target.name);
+}
+
+static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size,
+                             struct ins_operands *ops)
+{
+       return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);
+}
+
+int ins__scnprintf(struct ins *ins, char *bf, size_t size,
+                 struct ins_operands *ops)
+{
+       if (ins->ops->scnprintf)
+               return ins->ops->scnprintf(ins, bf, size, ops);
+
+       return ins__raw_scnprintf(ins, bf, size, ops);
+}
+
+static int call__parse(struct ins_operands *ops)
+{
+       char *endptr, *tok, *name;
+
+       ops->target.addr = strtoull(ops->raw, &endptr, 16);
+
+       name = strchr(endptr, '<');
+       if (name == NULL)
+               goto indirect_call;
+
+       name++;
+
+       tok = strchr(name, '>');
+       if (tok == NULL)
+               return -1;
+
+       *tok = '\0';
+       ops->target.name = strdup(name);
+       *tok = '>';
+
+       return ops->target.name == NULL ? -1 : 0;
+
+indirect_call:
+       tok = strchr(endptr, '(');
+       if (tok != NULL) {
+               ops->target.addr = 0;
+               return 0;
+       }
+
+       tok = strchr(endptr, '*');
+       if (tok == NULL)
+               return -1;
+
+       ops->target.addr = strtoull(tok + 1, NULL, 16);
+       return 0;
+}
+
+static int call__scnprintf(struct ins *ins, char *bf, size_t size,
+                          struct ins_operands *ops)
+{
+       if (ops->target.name)
+               return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name);
+
+       if (ops->target.addr == 0)
+               return ins__raw_scnprintf(ins, bf, size, ops);
+
+       return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr);
+}
+
+static struct ins_ops call_ops = {
+       .parse     = call__parse,
+       .scnprintf = call__scnprintf,
+};
+
+bool ins__is_call(const struct ins *ins)
+{
+       return ins->ops == &call_ops;
+}
+
+static int jump__parse(struct ins_operands *ops)
+{
+       const char *s = strchr(ops->raw, '+');
+
+       ops->target.addr = strtoll(ops->raw, NULL, 16);
+
+       if (s++ != NULL)
+               ops->target.offset = strtoll(s, NULL, 16);
+       else
+               ops->target.offset = UINT64_MAX;
+
+       return 0;
+}
+
+static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
+                          struct ins_operands *ops)
+{
+       return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset);
+}
+
+static struct ins_ops jump_ops = {
+       .parse     = jump__parse,
+       .scnprintf = jump__scnprintf,
+};
+
+bool ins__is_jump(const struct ins *ins)
+{
+       return ins->ops == &jump_ops;
+}
+
+static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
+{
+       char *endptr, *name, *t;
+
+       if (strstr(raw, "(%rip)") == NULL)
+               return 0;
+
+       *addrp = strtoull(comment, &endptr, 16);
+       name = strchr(endptr, '<');
+       if (name == NULL)
+               return -1;
+
+       name++;
+
+       t = strchr(name, '>');
+       if (t == NULL)
+               return 0;
+
+       *t = '\0';
+       *namep = strdup(name);
+       *t = '>';
+
+       return 0;
+}
+
+static int lock__parse(struct ins_operands *ops)
+{
+       char *name;
+
+       ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
+       if (ops->locked.ops == NULL)
+               return 0;
+
+       if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0)
+               goto out_free_ops;
+
+        ops->locked.ins = ins__find(name);
+        if (ops->locked.ins == NULL)
+                goto out_free_ops;
+
+        if (!ops->locked.ins->ops)
+                return 0;
+
+        if (ops->locked.ins->ops->parse)
+                ops->locked.ins->ops->parse(ops->locked.ops);
+
+       return 0;
+
+out_free_ops:
+       free(ops->locked.ops);
+       ops->locked.ops = NULL;
+       return 0;
+}
+
+static int lock__scnprintf(struct ins *ins, char *bf, size_t size,
+                          struct ins_operands *ops)
+{
+       int printed;
+
+       if (ops->locked.ins == NULL)
+               return ins__raw_scnprintf(ins, bf, size, ops);
+
+       printed = scnprintf(bf, size, "%-6.6s ", ins->name);
+       return printed + ins__scnprintf(ops->locked.ins, bf + printed,
+                                       size - printed, ops->locked.ops);
+}
+
+static void lock__delete(struct ins_operands *ops)
+{
+       free(ops->locked.ops);
+       free(ops->target.raw);
+       free(ops->target.name);
+}
+
+static struct ins_ops lock_ops = {
+       .free      = lock__delete,
+       .parse     = lock__parse,
+       .scnprintf = lock__scnprintf,
+};
+
+static int mov__parse(struct ins_operands *ops)
+{
+       char *s = strchr(ops->raw, ','), *target, *comment, prev;
+
+       if (s == NULL)
+               return -1;
+
+       *s = '\0';
+       ops->source.raw = strdup(ops->raw);
+       *s = ',';
+       
+       if (ops->source.raw == NULL)
+               return -1;
+
+       target = ++s;
+
+       while (s[0] != '\0' && !isspace(s[0]))
+               ++s;
+       prev = *s;
+       *s = '\0';
+
+       ops->target.raw = strdup(target);
+       *s = prev;
+
+       if (ops->target.raw == NULL)
+               goto out_free_source;
+
+       comment = strchr(s, '#');
+       if (comment == NULL)
+               return 0;
+
+       while (comment[0] != '\0' && isspace(comment[0]))
+               ++comment;
+
+       comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name);
+       comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name);
+
+       return 0;
+
+out_free_source:
+       free(ops->source.raw);
+       ops->source.raw = NULL;
+       return -1;
+}
+
+static int mov__scnprintf(struct ins *ins, char *bf, size_t size,
+                          struct ins_operands *ops)
+{
+       return scnprintf(bf, size, "%-6.6s %s,%s", ins->name,
+                        ops->source.name ?: ops->source.raw,
+                        ops->target.name ?: ops->target.raw);
+}
+
+static struct ins_ops mov_ops = {
+       .parse     = mov__parse,
+       .scnprintf = mov__scnprintf,
+};
+
+static int dec__parse(struct ins_operands *ops)
+{
+       char *target, *comment, *s, prev;
+
+       target = s = ops->raw;
+
+       while (s[0] != '\0' && !isspace(s[0]))
+               ++s;
+       prev = *s;
+       *s = '\0';
+
+       ops->target.raw = strdup(target);
+       *s = prev;
+
+       if (ops->target.raw == NULL)
+               return -1;
+
+       comment = strchr(s, '#');
+       if (comment == NULL)
+               return 0;
+
+       while (comment[0] != '\0' && isspace(comment[0]))
+               ++comment;
+
+       comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name);
+
+       return 0;
+}
+
+static int dec__scnprintf(struct ins *ins, char *bf, size_t size,
+                          struct ins_operands *ops)
+{
+       return scnprintf(bf, size, "%-6.6s %s", ins->name,
+                        ops->target.name ?: ops->target.raw);
+}
+
+static struct ins_ops dec_ops = {
+       .parse     = dec__parse,
+       .scnprintf = dec__scnprintf,
+};
+
+static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size,
+                         struct ins_operands *ops __used)
+{
+       return scnprintf(bf, size, "%-6.6s", "nop");
+}
+
+static struct ins_ops nop_ops = {
+       .scnprintf = nop__scnprintf,
+};
+
+/*
+ * Must be sorted by name!
+ */
+static struct ins instructions[] = {
+       { .name = "add",   .ops  = &mov_ops, },
+       { .name = "addl",  .ops  = &mov_ops, },
+       { .name = "addq",  .ops  = &mov_ops, },
+       { .name = "addw",  .ops  = &mov_ops, },
+       { .name = "and",   .ops  = &mov_ops, },
+       { .name = "bts",   .ops  = &mov_ops, },
+       { .name = "call",  .ops  = &call_ops, },
+       { .name = "callq", .ops  = &call_ops, },
+       { .name = "cmp",   .ops  = &mov_ops, },
+       { .name = "cmpb",  .ops  = &mov_ops, },
+       { .name = "cmpl",  .ops  = &mov_ops, },
+       { .name = "cmpq",  .ops  = &mov_ops, },
+       { .name = "cmpw",  .ops  = &mov_ops, },
+       { .name = "cmpxch", .ops  = &mov_ops, },
+       { .name = "dec",   .ops  = &dec_ops, },
+       { .name = "decl",  .ops  = &dec_ops, },
+       { .name = "imul",  .ops  = &mov_ops, },
+       { .name = "inc",   .ops  = &dec_ops, },
+       { .name = "incl",  .ops  = &dec_ops, },
+       { .name = "ja",    .ops  = &jump_ops, },
+       { .name = "jae",   .ops  = &jump_ops, },
+       { .name = "jb",    .ops  = &jump_ops, },
+       { .name = "jbe",   .ops  = &jump_ops, },
+       { .name = "jc",    .ops  = &jump_ops, },
+       { .name = "jcxz",  .ops  = &jump_ops, },
+       { .name = "je",    .ops  = &jump_ops, },
+       { .name = "jecxz", .ops  = &jump_ops, },
+       { .name = "jg",    .ops  = &jump_ops, },
+       { .name = "jge",   .ops  = &jump_ops, },
+       { .name = "jl",    .ops  = &jump_ops, },
+       { .name = "jle",   .ops  = &jump_ops, },
+       { .name = "jmp",   .ops  = &jump_ops, },
+       { .name = "jmpq",  .ops  = &jump_ops, },
+       { .name = "jna",   .ops  = &jump_ops, },
+       { .name = "jnae",  .ops  = &jump_ops, },
+       { .name = "jnb",   .ops  = &jump_ops, },
+       { .name = "jnbe",  .ops  = &jump_ops, },
+       { .name = "jnc",   .ops  = &jump_ops, },
+       { .name = "jne",   .ops  = &jump_ops, },
+       { .name = "jng",   .ops  = &jump_ops, },
+       { .name = "jnge",  .ops  = &jump_ops, },
+       { .name = "jnl",   .ops  = &jump_ops, },
+       { .name = "jnle",  .ops  = &jump_ops, },
+       { .name = "jno",   .ops  = &jump_ops, },
+       { .name = "jnp",   .ops  = &jump_ops, },
+       { .name = "jns",   .ops  = &jump_ops, },
+       { .name = "jnz",   .ops  = &jump_ops, },
+       { .name = "jo",    .ops  = &jump_ops, },
+       { .name = "jp",    .ops  = &jump_ops, },
+       { .name = "jpe",   .ops  = &jump_ops, },
+       { .name = "jpo",   .ops  = &jump_ops, },
+       { .name = "jrcxz", .ops  = &jump_ops, },
+       { .name = "js",    .ops  = &jump_ops, },
+       { .name = "jz",    .ops  = &jump_ops, },
+       { .name = "lea",   .ops  = &mov_ops, },
+       { .name = "lock",  .ops  = &lock_ops, },
+       { .name = "mov",   .ops  = &mov_ops, },
+       { .name = "movb",  .ops  = &mov_ops, },
+       { .name = "movdqa",.ops  = &mov_ops, },
+       { .name = "movl",  .ops  = &mov_ops, },
+       { .name = "movq",  .ops  = &mov_ops, },
+       { .name = "movslq", .ops  = &mov_ops, },
+       { .name = "movzbl", .ops  = &mov_ops, },
+       { .name = "movzwl", .ops  = &mov_ops, },
+       { .name = "nop",   .ops  = &nop_ops, },
+       { .name = "nopl",  .ops  = &nop_ops, },
+       { .name = "nopw",  .ops  = &nop_ops, },
+       { .name = "or",    .ops  = &mov_ops, },
+       { .name = "orl",   .ops  = &mov_ops, },
+       { .name = "test",  .ops  = &mov_ops, },
+       { .name = "testb", .ops  = &mov_ops, },
+       { .name = "testl", .ops  = &mov_ops, },
+       { .name = "xadd",  .ops  = &mov_ops, },
+};
+
+static int ins__cmp(const void *name, const void *insp)
+{
+       const struct ins *ins = insp;
+
+       return strcmp(name, ins->name);
+}
+
+static struct ins *ins__find(const char *name)
+{
+       const int nmemb = ARRAY_SIZE(instructions);
+
+       return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp);
+}
+
 int symbol__annotate_init(struct map *map __used, struct symbol *sym)
 {
        struct annotation *notes = symbol__annotation(sym);
@@ -28,7 +425,7 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym)
 int symbol__alloc_hist(struct symbol *sym)
 {
        struct annotation *notes = symbol__annotation(sym);
-       const size_t size = sym->end - sym->start + 1;
+       const size_t size = symbol__size(sym);
        size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
 
        notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
@@ -78,31 +475,110 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
        return 0;
 }
 
-static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
+static void disasm_line__init_ins(struct disasm_line *dl)
+{
+       dl->ins = ins__find(dl->name);
+
+       if (dl->ins == NULL)
+               return;
+
+       if (!dl->ins->ops)
+               return;
+
+       if (dl->ins->ops->parse)
+               dl->ins->ops->parse(&dl->ops);
+}
+
+static int disasm_line__parse(char *line, char **namep, char **rawp)
+{
+       char *name = line, tmp;
+
+       while (isspace(name[0]))
+               ++name;
+
+       if (name[0] == '\0')
+               return -1;
+
+       *rawp = name + 1;
+
+       while ((*rawp)[0] != '\0' && !isspace((*rawp)[0]))
+               ++*rawp;
+
+       tmp = (*rawp)[0];
+       (*rawp)[0] = '\0';
+       *namep = strdup(name);
+
+       if (*namep == NULL)
+               goto out_free_name;
+
+       (*rawp)[0] = tmp;
+
+       if ((*rawp)[0] != '\0') {
+               (*rawp)++;
+               while (isspace((*rawp)[0]))
+                       ++(*rawp);
+       }
+
+       return 0;
+
+out_free_name:
+       free(*namep);
+       *namep = NULL;
+       return -1;
+}
+
+static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
 {
-       struct objdump_line *self = malloc(sizeof(*self) + privsize);
+       struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
 
-       if (self != NULL) {
-               self->offset = offset;
-               self->line = line;
+       if (dl != NULL) {
+               dl->offset = offset;
+               dl->line = strdup(line);
+               if (dl->line == NULL)
+                       goto out_delete;
+
+               if (offset != -1) {
+                       if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0)
+                               goto out_free_line;
+
+                       disasm_line__init_ins(dl);
+               }
        }
 
-       return self;
+       return dl;
+
+out_free_line:
+       free(dl->line);
+out_delete:
+       free(dl);
+       return NULL;
+}
+
+void disasm_line__free(struct disasm_line *dl)
+{
+       free(dl->line);
+       free(dl->name);
+       if (dl->ins && dl->ins->ops->free)
+               dl->ins->ops->free(&dl->ops);
+       else
+               ins__delete(&dl->ops);
+       free(dl);
 }
 
-void objdump_line__free(struct objdump_line *self)
+int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw)
 {
-       free(self->line);
-       free(self);
+       if (raw || !dl->ins)
+               return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw);
+
+       return ins__scnprintf(dl->ins, bf, size, &dl->ops);
 }
 
-static void objdump__add_line(struct list_head *head, struct objdump_line *line)
+static void disasm__add(struct list_head *head, struct disasm_line *line)
 {
        list_add_tail(&line->node, head);
 }
 
-struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
-                                              struct objdump_line *pos)
+struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos)
 {
        list_for_each_entry_continue(pos, head, node)
                if (pos->offset >= 0)
@@ -111,15 +587,14 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
        return NULL;
 }
 
-static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
-                              int evidx, u64 len, int min_pcnt,
-                              int printed, int max_lines,
-                              struct objdump_line *queue)
+static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start,
+                     int evidx, u64 len, int min_pcnt, int printed,
+                     int max_lines, struct disasm_line *queue)
 {
        static const char *prev_line;
        static const char *prev_color;
 
-       if (oline->offset != -1) {
+       if (dl->offset != -1) {
                const char *path = NULL;
                unsigned int hits = 0;
                double percent = 0.0;
@@ -127,10 +602,11 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
                struct annotation *notes = symbol__annotation(sym);
                struct source_line *src_line = notes->src->lines;
                struct sym_hist *h = annotation__histogram(notes, evidx);
-               s64 offset = oline->offset;
-               struct objdump_line *next;
+               s64 offset = dl->offset;
+               const u64 addr = start + offset;
+               struct disasm_line *next;
 
-               next = objdump__get_next_ip_line(&notes->src->source, oline);
+               next = disasm__get_next_ip_line(&notes->src->source, dl);
 
                while (offset < (s64)len &&
                       (next == NULL || offset < next->offset)) {
@@ -155,9 +631,9 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
 
                if (queue != NULL) {
                        list_for_each_entry_from(queue, &notes->src->source, node) {
-                               if (queue == oline)
+                               if (queue == dl)
                                        break;
-                               objdump_line__print(queue, sym, evidx, len,
+                               disasm_line__print(queue, sym, start, evidx, len,
                                                    0, 0, 1, NULL);
                        }
                }
@@ -180,17 +656,18 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
 
                color_fprintf(stdout, color, " %7.2f", percent);
                printf(" :      ");
-               color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line);
+               color_fprintf(stdout, PERF_COLOR_MAGENTA, "  %" PRIx64 ":", addr);
+               color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line);
        } else if (max_lines && printed >= max_lines)
                return 1;
        else {
                if (queue)
                        return -1;
 
-               if (!*oline->line)
+               if (!*dl->line)
                        printf("         :\n");
                else
-                       printf("         :      %s\n", oline->line);
+                       printf("         :      %s\n", dl->line);
        }
 
        return 0;
@@ -200,8 +677,8 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
                                      FILE *file, size_t privsize)
 {
        struct annotation *notes = symbol__annotation(sym);
-       struct objdump_line *objdump_line;
-       char *line = NULL, *tmp, *tmp2, *c;
+       struct disasm_line *dl;
+       char *line = NULL, *parsed_line, *tmp, *tmp2, *c;
        size_t line_len;
        s64 line_ip, offset = -1;
 
@@ -219,6 +696,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
                *c = 0;
 
        line_ip = -1;
+       parsed_line = line;
 
        /*
         * Strip leading spaces:
@@ -246,14 +724,17 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
                offset = line_ip - start;
                if (offset < 0 || (u64)line_ip > end)
                        offset = -1;
+               else
+                       parsed_line = tmp2 + 1;
        }
 
-       objdump_line = objdump_line__new(offset, line, privsize);
-       if (objdump_line == NULL) {
-               free(line);
+       dl = disasm_line__new(offset, parsed_line, privsize);
+       free(line);
+
+       if (dl == NULL)
                return -1;
-       }
-       objdump__add_line(&notes->src->source, objdump_line);
+
+       disasm__add(&notes->src->source, dl);
 
        return 0;
 }
@@ -476,7 +957,7 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx)
 {
        struct annotation *notes = symbol__annotation(sym);
        struct sym_hist *h = annotation__histogram(notes, evidx);
-       u64 len = sym->end - sym->start, offset;
+       u64 len = symbol__size(sym), offset;
 
        for (offset = 0; offset < len; ++offset)
                if (h->addr[offset] != 0)
@@ -492,7 +973,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
        struct dso *dso = map->dso;
        const char *filename = dso->long_name, *d_filename;
        struct annotation *notes = symbol__annotation(sym);
-       struct objdump_line *pos, *queue = NULL;
+       struct disasm_line *pos, *queue = NULL;
+       u64 start = map__rip_2objdump(map, sym->start);
        int printed = 2, queue_len = 0;
        int more = 0;
        u64 len;
@@ -502,7 +984,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
        else
                d_filename = basename(filename);
 
-       len = sym->end - sym->start;
+       len = symbol__size(sym);
 
        printf(" Percent |      Source code & Disassembly of %s\n", d_filename);
        printf("------------------------------------------------\n");
@@ -516,8 +998,9 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
                        queue_len = 0;
                }
 
-               switch (objdump_line__print(pos, sym, evidx, len, min_pcnt,
-                                           printed, max_lines, queue)) {
+               switch (disasm_line__print(pos, sym, start, evidx, len,
+                                           min_pcnt, printed, max_lines,
+                                           queue)) {
                case 0:
                        ++printed;
                        if (context) {
@@ -561,7 +1044,7 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
 {
        struct annotation *notes = symbol__annotation(sym);
        struct sym_hist *h = annotation__histogram(notes, evidx);
-       int len = sym->end - sym->start, offset;
+       int len = symbol__size(sym), offset;
 
        h->sum = 0;
        for (offset = 0; offset < len; ++offset) {
@@ -570,14 +1053,42 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
        }
 }
 
-void objdump_line_list__purge(struct list_head *head)
+void disasm__purge(struct list_head *head)
 {
-       struct objdump_line *pos, *n;
+       struct disasm_line *pos, *n;
 
        list_for_each_entry_safe(pos, n, head, node) {
                list_del(&pos->node);
-               objdump_line__free(pos);
+               disasm_line__free(pos);
+       }
+}
+
+static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
+{
+       size_t printed;
+
+       if (dl->offset == -1)
+               return fprintf(fp, "%s\n", dl->line);
+
+       printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name);
+
+       if (dl->ops.raw[0] != '\0') {
+               printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",
+                                  dl->ops.raw);
        }
+
+       return printed + fprintf(fp, "\n");
+}
+
+size_t disasm__fprintf(struct list_head *head, FILE *fp)
+{
+       struct disasm_line *pos;
+       size_t printed = 0;
+
+       list_for_each_entry(pos, head, node)
+               printed += disasm_line__fprintf(pos, fp);
+
+       return printed;
 }
 
 int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
@@ -592,7 +1103,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
        if (symbol__annotate(sym, map, 0) < 0)
                return -1;
 
-       len = sym->end - sym->start;
+       len = symbol__size(sym);
 
        if (print_lines) {
                symbol__get_source_line(sym, map, evidx, &source_line,
@@ -605,7 +1116,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
        if (print_lines)
                symbol__free_source_line(sym, len);
 
-       objdump_line_list__purge(&symbol__annotation(sym)->src->source);
+       disasm__purge(&symbol__annotation(sym)->src->source);
 
        return 0;
 }
index efa5dc8..78a5692 100644 (file)
@@ -2,20 +2,69 @@
 #define __PERF_ANNOTATE_H
 
 #include <stdbool.h>
+#include <stdint.h>
 #include "types.h"
 #include "symbol.h"
 #include <linux/list.h>
 #include <linux/rbtree.h>
 
-struct objdump_line {
-       struct list_head node;
-       s64              offset;
-       char             *line;
+struct ins;
+
+struct ins_operands {
+       char    *raw;
+       struct {
+               char    *raw;
+               char    *name;
+               u64     addr;
+               u64     offset;
+       } target;
+       union {
+               struct {
+                       char    *raw;
+                       char    *name;
+                       u64     addr;
+               } source;
+               struct {
+                       struct ins *ins;
+                       struct ins_operands *ops;
+               } locked;
+       };
 };
 
-void objdump_line__free(struct objdump_line *self);
-struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
-                                              struct objdump_line *pos);
+struct ins_ops {
+       void (*free)(struct ins_operands *ops);
+       int (*parse)(struct ins_operands *ops);
+       int (*scnprintf)(struct ins *ins, char *bf, size_t size,
+                        struct ins_operands *ops);
+};
+
+struct ins {
+       const char     *name;
+       struct ins_ops *ops;
+};
+
+bool ins__is_jump(const struct ins *ins);
+bool ins__is_call(const struct ins *ins);
+int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops);
+
+struct disasm_line {
+       struct list_head    node;
+       s64                 offset;
+       char                *line;
+       char                *name;
+       struct ins          *ins;
+       struct ins_operands ops;
+};
+
+static inline bool disasm_line__has_offset(const struct disasm_line *dl)
+{
+       return dl->ops.target.offset != UINT64_MAX;
+}
+
+void disasm_line__free(struct disasm_line *dl);
+struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos);
+int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw);
+size_t disasm__fprintf(struct list_head *head, FILE *fp);
 
 struct sym_hist {
        u64             sum;
@@ -32,7 +81,7 @@ struct source_line {
  *
  * @histogram: Array of addr hit histograms per event being monitored
  * @lines: If 'print_lines' is specified, per source code line percentages
- * @source: source parsed from objdump -dS
+ * @source: source parsed from a disassembler like objdump -dS
  *
  * lines is allocated, percentages calculated and all sorted by percentage
  * when the annotation is about to be presented, so the percentages are for
@@ -82,7 +131,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
                            int context);
 void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);
 void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
-void objdump_line_list__purge(struct list_head *head);
+void disasm__purge(struct list_head *head);
 
 int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
                         bool print_lines, bool full_paths, int min_pcnt,
index 8dd224d..cff18c6 100644 (file)
@@ -33,7 +33,7 @@ extern int pager_use_color;
 
 extern int use_browser;
 
-#ifdef NO_NEWT_SUPPORT
+#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
 static inline void setup_browser(bool fallback_to_pager)
 {
        if (fallback_to_pager)
@@ -43,19 +43,29 @@ static inline void exit_browser(bool wait_for_ok __used) {}
 #else
 void setup_browser(bool fallback_to_pager);
 void exit_browser(bool wait_for_ok);
+
+#ifdef NO_NEWT_SUPPORT
+static inline int ui__init(void)
+{
+       return -1;
+}
+static inline void ui__exit(bool wait_for_ok __used) {}
+#else
+int ui__init(void);
+void ui__exit(bool wait_for_ok);
 #endif
 
 #ifdef NO_GTK2_SUPPORT
-static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager)
+static inline int perf_gtk__init(void)
 {
-       if (fallback_to_pager)
-               setup_pager();
+       return -1;
 }
-static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {}
+static inline void perf_gtk__exit(bool wait_for_ok __used) {}
 #else
-void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager);
-void perf_gtk_exit_browser(bool wait_for_ok);
+int perf_gtk__init(void);
+void perf_gtk__exit(bool wait_for_ok);
 #endif
+#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
 
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
index 26817da..efb1fce 100644 (file)
@@ -11,6 +11,7 @@
 #include "event.h"
 #include "debug.h"
 #include "util.h"
+#include "target.h"
 
 int verbose;
 bool dump_trace = false, quiet = false;
index f2ce88d..6bebe7f 100644 (file)
@@ -26,7 +26,7 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,
 #else
 extern char ui_helpline__last_msg[];
 int ui_helpline__show_help(const char *format, va_list ap);
-#include "ui/progress.h"
+#include "../ui/progress.h"
 int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
 #endif
 
index 1986d80..4ac5f5a 100644 (file)
@@ -11,6 +11,7 @@
 #include <poll.h>
 #include "cpumap.h"
 #include "thread_map.h"
+#include "target.h"
 #include "evlist.h"
 #include "evsel.h"
 #include <unistd.h>
@@ -599,18 +600,21 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
        return perf_evlist__mmap_per_cpu(evlist, prot, mask);
 }
 
-int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
-                            const char *target_tid, uid_t uid, const char *cpu_list)
+int perf_evlist__create_maps(struct perf_evlist *evlist,
+                            struct perf_target *target)
 {
-       evlist->threads = thread_map__new_str(target_pid, target_tid, uid);
+       evlist->threads = thread_map__new_str(target->pid, target->tid,
+                                             target->uid);
 
        if (evlist->threads == NULL)
                return -1;
 
-       if (uid != UINT_MAX || (cpu_list == NULL && target_tid))
+       if (perf_target__has_task(target))
+               evlist->cpus = cpu_map__dummy_new();
+       else if (!perf_target__has_cpu(target) && !target->uses_mmap)
                evlist->cpus = cpu_map__dummy_new();
        else
-               evlist->cpus = cpu_map__new(cpu_list);
+               evlist->cpus = cpu_map__new(target->cpu_list);
 
        if (evlist->cpus == NULL)
                goto out_delete_threads;
@@ -827,7 +831,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,
                exit(-1);
        }
 
-       if (!opts->system_wide && !opts->target_tid && !opts->target_pid)
+       if (perf_target__none(&opts->target))
                evlist->threads->map[0] = evlist->workload.pid;
 
        close(child_ready_pipe[1]);
index 21f1c9e..58abb63 100644 (file)
@@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
        evlist->threads = threads;
 }
 
-int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
-                            const char *tid, uid_t uid, const char *cpu_list);
+int perf_evlist__create_maps(struct perf_evlist *evlist,
+                            struct perf_target *target);
 void perf_evlist__delete_maps(struct perf_evlist *evlist);
 int perf_evlist__set_filters(struct perf_evlist *evlist);
 
index 8c13dbc..f4f427c 100644 (file)
@@ -14,6 +14,7 @@
 #include "util.h"
 #include "cpumap.h"
 #include "thread_map.h"
+#include "target.h"
 
 #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
 #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
@@ -69,6 +70,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
        struct perf_event_attr *attr = &evsel->attr;
        int track = !evsel->idx; /* only the first counter needs these */
 
+       attr->disabled = 1;
        attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
        attr->inherit       = !opts->no_inherit;
        attr->read_format   = PERF_FORMAT_TOTAL_TIME_ENABLED |
@@ -106,15 +108,15 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
        if (opts->call_graph)
                attr->sample_type       |= PERF_SAMPLE_CALLCHAIN;
 
-       if (opts->system_wide)
+       if (opts->target.system_wide)
                attr->sample_type       |= PERF_SAMPLE_CPU;
 
        if (opts->period)
                attr->sample_type       |= PERF_SAMPLE_PERIOD;
 
        if (!opts->sample_id_all_missing &&
-           (opts->sample_time || opts->system_wide ||
-            !opts->no_inherit || opts->cpu_list))
+           (opts->sample_time || !opts->no_inherit ||
+            perf_target__has_cpu(&opts->target)))
                attr->sample_type       |= PERF_SAMPLE_TIME;
 
        if (opts->raw_samples) {
@@ -135,9 +137,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
        attr->mmap = track;
        attr->comm = track;
 
-       if (!opts->target_pid && !opts->target_tid && !opts->system_wide &&
+       if (perf_target__none(&opts->target) &&
            (!opts->group || evsel == first)) {
-               attr->disabled = 1;
                attr->enable_on_exec = 1;
        }
 }
index c0b70c6..5385980 100644 (file)
@@ -31,21 +31,16 @@ static const char **header_argv;
 
 int perf_header__push_event(u64 id, const char *name)
 {
+       struct perf_trace_event_type *nevents;
+
        if (strlen(name) > MAX_EVENT_NAME)
                pr_warning("Event %s will be truncated\n", name);
 
-       if (!events) {
-               events = malloc(sizeof(struct perf_trace_event_type));
-               if (events == NULL)
-                       return -ENOMEM;
-       } else {
-               struct perf_trace_event_type *nevents;
+       nevents = realloc(events, (event_count + 1) * sizeof(*events));
+       if (nevents == NULL)
+               return -ENOMEM;
+       events = nevents;
 
-               nevents = realloc(events, (event_count + 1) * sizeof(*events));
-               if (nevents == NULL)
-                       return -ENOMEM;
-               events = nevents;
-       }
        memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
        events[event_count].event_id = id;
        strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
index 9f6d630..1293b5e 100644 (file)
@@ -599,7 +599,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
        if (chain->ms.sym)
                ret += fprintf(fp, "%s\n", chain->ms.sym->name);
        else
-               ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
+               ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
 
        return ret;
 }
index 2cae9df..cfc64e2 100644 (file)
@@ -138,7 +138,7 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
 #define K_LEFT -1
 #define K_RIGHT -2
 #else
-#include "ui/keysyms.h"
+#include "../ui/keysyms.h"
 int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
                             void(*timer)(void *arg), void *arg, int delay_secs);
 
index 5b3a0ef..c7fc18a 100644 (file)
@@ -593,17 +593,27 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
 static int config_term(struct perf_event_attr *attr,
                       struct parse_events__term *term)
 {
-       switch (term->type) {
+#define CHECK_TYPE_VAL(type)                                   \
+do {                                                           \
+       if (PARSE_EVENTS__TERM_TYPE_ ## type != term->type_val) \
+               return -EINVAL;                                 \
+} while (0)
+
+       switch (term->type_term) {
        case PARSE_EVENTS__TERM_TYPE_CONFIG:
+               CHECK_TYPE_VAL(NUM);
                attr->config = term->val.num;
                break;
        case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+               CHECK_TYPE_VAL(NUM);
                attr->config1 = term->val.num;
                break;
        case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+               CHECK_TYPE_VAL(NUM);
                attr->config2 = term->val.num;
                break;
        case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+               CHECK_TYPE_VAL(NUM);
                attr->sample_period = term->val.num;
                break;
        case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
@@ -615,7 +625,9 @@ static int config_term(struct perf_event_attr *attr,
        default:
                return -EINVAL;
        }
+
        return 0;
+#undef CHECK_TYPE_VAL
 }
 
 static int config_attr(struct perf_event_attr *attr,
@@ -1015,11 +1027,12 @@ void print_events(const char *event_glob)
 
 int parse_events__is_hardcoded_term(struct parse_events__term *term)
 {
-       return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX;
+       return term->type_term != PARSE_EVENTS__TERM_TYPE_USER;
 }
 
-int parse_events__new_term(struct parse_events__term **_term, int type,
-                          char *config, char *str, long num)
+static int new_term(struct parse_events__term **_term, int type_val,
+                   int type_term, char *config,
+                   char *str, long num)
 {
        struct parse_events__term *term;
 
@@ -1028,15 +1041,11 @@ int parse_events__new_term(struct parse_events__term **_term, int type,
                return -ENOMEM;
 
        INIT_LIST_HEAD(&term->list);
-       term->type = type;
+       term->type_val  = type_val;
+       term->type_term = type_term;
        term->config = config;
 
-       switch (type) {
-       case PARSE_EVENTS__TERM_TYPE_CONFIG:
-       case PARSE_EVENTS__TERM_TYPE_CONFIG1:
-       case PARSE_EVENTS__TERM_TYPE_CONFIG2:
-       case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
-       case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+       switch (type_val) {
        case PARSE_EVENTS__TERM_TYPE_NUM:
                term->val.num = num;
                break;
@@ -1051,6 +1060,20 @@ int parse_events__new_term(struct parse_events__term **_term, int type,
        return 0;
 }
 
+int parse_events__term_num(struct parse_events__term **term,
+                          int type_term, char *config, long num)
+{
+       return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term,
+                       config, NULL, num);
+}
+
+int parse_events__term_str(struct parse_events__term **term,
+                          int type_term, char *config, char *str)
+{
+       return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term,
+                       config, str, 0);
+}
+
 void parse_events__free_terms(struct list_head *terms)
 {
        struct parse_events__term *term, *h;
index ca069f8..3fddd61 100644 (file)
@@ -4,7 +4,9 @@
  * Parse symbolic events/counts passed in as options:
  */
 
+#include <stdbool.h>
 #include "../../../include/linux/perf_event.h"
+#include "types.h"
 
 struct list_head;
 struct perf_evsel;
@@ -34,16 +36,17 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
 #define EVENTS_HELP_MAX (128*1024)
 
 enum {
+       PARSE_EVENTS__TERM_TYPE_NUM,
+       PARSE_EVENTS__TERM_TYPE_STR,
+};
+
+enum {
+       PARSE_EVENTS__TERM_TYPE_USER,
        PARSE_EVENTS__TERM_TYPE_CONFIG,
        PARSE_EVENTS__TERM_TYPE_CONFIG1,
        PARSE_EVENTS__TERM_TYPE_CONFIG2,
        PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
        PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
-       PARSE_EVENTS__TERM_TYPE_NUM,
-       PARSE_EVENTS__TERM_TYPE_STR,
-
-       PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX =
-               PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
 };
 
 struct parse_events__term {
@@ -52,14 +55,16 @@ struct parse_events__term {
                char *str;
                long  num;
        } val;
-       int type;
-
+       int type_val;
+       int type_term;
        struct list_head list;
 };
 
 int parse_events__is_hardcoded_term(struct parse_events__term *term);
-int parse_events__new_term(struct parse_events__term **term, int type,
-                          char *config, char *str, long num);
+int parse_events__term_num(struct parse_events__term **_term,
+                          int type_term, char *config, long num);
+int parse_events__term_str(struct parse_events__term **_term,
+                          int type_term, char *config, char *str);
 void parse_events__free_terms(struct list_head *terms);
 int parse_events_modifier(struct list_head *list __used, char *str __used);
 int parse_events_add_tracepoint(struct list_head *list, int *idx,
index d9637da..936913e 100644 (file)
@@ -176,8 +176,8 @@ PE_NAME '=' PE_NAME
 {
        struct parse_events__term *term;
 
-       ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR,
-                $1, $3, 0));
+       ABORT_ON(parse_events__term_str(&term, PARSE_EVENTS__TERM_TYPE_USER,
+                                       $1, $3));
        $$ = term;
 }
 |
@@ -185,8 +185,8 @@ PE_NAME '=' PE_VALUE
 {
        struct parse_events__term *term;
 
-       ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
-                $1, NULL, $3));
+       ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+                                       $1, $3));
        $$ = term;
 }
 |
@@ -194,8 +194,8 @@ PE_NAME
 {
        struct parse_events__term *term;
 
-       ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
-                $1, NULL, 1));
+       ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+                                       $1, 1));
        $$ = term;
 }
 |
@@ -203,7 +203,7 @@ PE_TERM '=' PE_VALUE
 {
        struct parse_events__term *term;
 
-       ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3));
+       ABORT_ON(parse_events__term_num(&term, $1, NULL, $3));
        $$ = term;
 }
 |
@@ -211,7 +211,7 @@ PE_TERM
 {
        struct parse_events__term *term;
 
-       ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1));
+       ABORT_ON(parse_events__term_num(&term, $1, NULL, 1));
        $$ = term;
 }
 
index cb08a11..8ee219b 100644 (file)
@@ -225,7 +225,7 @@ static int pmu_config_term(struct list_head *formats,
        if (parse_events__is_hardcoded_term(term))
                return 0;
 
-       if (term->type != PARSE_EVENTS__TERM_TYPE_NUM)
+       if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM)
                return -EINVAL;
 
        format = pmu_find_format(formats, term->config);
@@ -246,6 +246,11 @@ static int pmu_config_term(struct list_head *formats,
                return -EINVAL;
        }
 
+       /*
+        * XXX If we ever decide to go with string values for
+        * non-hardcoded terms, here's the place to translate
+        * them into value.
+        */
        *vp |= pmu_format_value(format->bits, term->val.num);
        return 0;
 }
@@ -324,49 +329,58 @@ static struct test_format {
 /* Simulated users input. */
 static struct parse_events__term test_terms[] = {
        {
-               .config  = (char *) "krava01",
-               .val.num = 15,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava01",
+               .val.num   = 15,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava02",
-               .val.num = 170,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava02",
+               .val.num   = 170,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava03",
-               .val.num = 1,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava03",
+               .val.num   = 1,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava11",
-               .val.num = 27,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava11",
+               .val.num   = 27,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava12",
-               .val.num = 1,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava12",
+               .val.num   = 1,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava13",
-               .val.num = 2,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava13",
+               .val.num   = 2,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava21",
-               .val.num = 119,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava21",
+               .val.num   = 119,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava22",
-               .val.num = 11,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava22",
+               .val.num   = 11,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
        {
-               .config  = (char *) "krava23",
-               .val.num = 2,
-               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+               .config    = (char *) "krava23",
+               .val.num   = 2,
+               .type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
+               .type_term = PARSE_EVENTS__TERM_TYPE_USER,
        },
 };
 #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
index c2623c6..acb9795 100644 (file)
@@ -37,7 +37,7 @@ PyMODINIT_FUNC initperf_trace_context(void);
 #define FTRACE_MAX_EVENT                               \
        ((1 << (sizeof(unsigned short) * 8)) - 1)
 
-struct event *events[FTRACE_MAX_EVENT];
+struct event_format *events[FTRACE_MAX_EVENT];
 
 #define MAX_FIELDS     64
 #define N_COMMON_FIELDS        7
@@ -136,7 +136,7 @@ static void define_field(enum print_arg_type field_type,
        Py_DECREF(t);
 }
 
-static void define_event_symbols(struct event *event,
+static void define_event_symbols(struct event_format *event,
                                 const char *ev_name,
                                 struct print_arg *args)
 {
@@ -178,6 +178,10 @@ static void define_event_symbols(struct event *event,
                define_event_symbols(event, ev_name, args->op.right);
                break;
        default:
+               /* gcc warns for these? */
+       case PRINT_BSTRING:
+       case PRINT_DYNAMIC_ARRAY:
+       case PRINT_FUNC:
                /* we should warn... */
                return;
        }
@@ -186,10 +190,10 @@ static void define_event_symbols(struct event *event,
                define_event_symbols(event, ev_name, args->next);
 }
 
-static inline struct event *find_cache_event(int type)
+static inline struct event_format *find_cache_event(int type)
 {
        static char ev_name[256];
-       struct event *event;
+       struct event_format *event;
 
        if (events[type])
                return events[type];
@@ -216,7 +220,7 @@ static void python_process_event(union perf_event *pevent __unused,
        struct format_field *field;
        unsigned long long val;
        unsigned long s, ns;
-       struct event *event;
+       struct event_format *event;
        unsigned n = 0;
        int type;
        int pid;
@@ -436,7 +440,7 @@ out:
 
 static int python_generate_script(const char *outfile)
 {
-       struct event *event = NULL;
+       struct event_format *event = NULL;
        struct format_field *f;
        char fname[PATH_MAX];
        int not_first, count;
index 1efd3be..4dcc8f3 100644 (file)
@@ -1108,16 +1108,10 @@ more:
        }
 
        if ((skip = perf_session__process_event(self, &event, tool, head)) < 0) {
-               dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
-                           head, event.header.size, event.header.type);
-               /*
-                * assume we lost track of the stream, check alignment, and
-                * increment a single u64 in the hope to catch on again 'soon'.
-                */
-               if (unlikely(head & 7))
-                       head &= ~7ULL;
-
-               size = 8;
+               pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
+                      head, event.header.size, event.header.type);
+               err = -EINVAL;
+               goto out_err;
        }
 
        head += size;
@@ -1226,17 +1220,11 @@ more:
 
        if (size == 0 ||
            perf_session__process_event(session, event, tool, file_pos) < 0) {
-               dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
-                           file_offset + head, event->header.size,
-                           event->header.type);
-               /*
-                * assume we lost track of the stream, check alignment, and
-                * increment a single u64 in the hope to catch on again 'soon'.
-                */
-               if (unlikely(head & 7))
-                       head &= ~7ULL;
-
-               size = 8;
+               pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
+                      file_offset + head, event->header.size,
+                      event->header.type);
+               err = -EINVAL;
+               goto out_err;
        }
 
        head += size;
index ac49ef2..1f00388 100644 (file)
@@ -65,6 +65,11 @@ struct symbol {
 
 void symbol__delete(struct symbol *sym);
 
+static inline size_t symbol__size(const struct symbol *sym)
+{
+       return sym->end - sym->start + 1;
+}
+
 struct strlist;
 
 struct symbol_conf {
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c
new file mode 100644 (file)
index 0000000..1064d5b
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Helper functions for handling target threads/cpus
+ *
+ * Copyright (C) 2012, LG Electronics, Namhyung Kim <namhyung.kim@lge.com>
+ *
+ * Released under the GPL v2.
+ */
+
+#include "target.h"
+#include "debug.h"
+
+#include <pwd.h>
+#include <string.h>
+
+
+enum perf_target_errno perf_target__validate(struct perf_target *target)
+{
+       enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS;
+
+       if (target->pid)
+               target->tid = target->pid;
+
+       /* CPU and PID are mutually exclusive */
+       if (target->tid && target->cpu_list) {
+               target->cpu_list = NULL;
+               if (ret == PERF_ERRNO_TARGET__SUCCESS)
+                       ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU;
+       }
+
+       /* UID and PID are mutually exclusive */
+       if (target->tid && target->uid_str) {
+               target->uid_str = NULL;
+               if (ret == PERF_ERRNO_TARGET__SUCCESS)
+                       ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID;
+       }
+
+       /* UID and CPU are mutually exclusive */
+       if (target->uid_str && target->cpu_list) {
+               target->cpu_list = NULL;
+               if (ret == PERF_ERRNO_TARGET__SUCCESS)
+                       ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU;
+       }
+
+       /* PID and SYSTEM are mutually exclusive */
+       if (target->tid && target->system_wide) {
+               target->system_wide = false;
+               if (ret == PERF_ERRNO_TARGET__SUCCESS)
+                       ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM;
+       }
+
+       /* UID and SYSTEM are mutually exclusive */
+       if (target->uid_str && target->system_wide) {
+               target->system_wide = false;
+               if (ret == PERF_ERRNO_TARGET__SUCCESS)
+                       ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM;
+       }
+
+       return ret;
+}
+
+enum perf_target_errno perf_target__parse_uid(struct perf_target *target)
+{
+       struct passwd pwd, *result;
+       char buf[1024];
+       const char *str = target->uid_str;
+
+       target->uid = UINT_MAX;
+       if (str == NULL)
+               return PERF_ERRNO_TARGET__SUCCESS;
+
+       /* Try user name first */
+       getpwnam_r(str, &pwd, buf, sizeof(buf), &result);
+
+       if (result == NULL) {
+               /*
+                * The user name not found. Maybe it's a UID number.
+                */
+               char *endptr;
+               int uid = strtol(str, &endptr, 10);
+
+               if (*endptr != '\0')
+                       return PERF_ERRNO_TARGET__INVALID_UID;
+
+               getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
+
+               if (result == NULL)
+                       return PERF_ERRNO_TARGET__USER_NOT_FOUND;
+       }
+
+       target->uid = result->pw_uid;
+       return PERF_ERRNO_TARGET__SUCCESS;
+}
+
+/*
+ * This must have a same ordering as the enum perf_target_errno.
+ */
+static const char *perf_target__error_str[] = {
+       "PID/TID switch overriding CPU",
+       "PID/TID switch overriding UID",
+       "UID switch overriding CPU",
+       "PID/TID switch overriding SYSTEM",
+       "UID switch overriding SYSTEM",
+       "Invalid User: %s",
+       "Problems obtaining information for user %s",
+};
+
+int perf_target__strerror(struct perf_target *target, int errnum,
+                         char *buf, size_t buflen)
+{
+       int idx;
+       const char *msg;
+
+       if (errnum >= 0) {
+               strerror_r(errnum, buf, buflen);
+               return 0;
+       }
+
+       if (errnum <  __PERF_ERRNO_TARGET__START ||
+           errnum >= __PERF_ERRNO_TARGET__END)
+               return -1;
+
+       idx = errnum - __PERF_ERRNO_TARGET__START;
+       msg = perf_target__error_str[idx];
+
+       switch (errnum) {
+       case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU
+        ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM:
+               snprintf(buf, buflen, "%s", msg);
+               break;
+
+       case PERF_ERRNO_TARGET__INVALID_UID:
+       case PERF_ERRNO_TARGET__USER_NOT_FOUND:
+               snprintf(buf, buflen, msg, target->uid_str);
+               break;
+
+       default:
+               /* cannot reach here */
+               break;
+       }
+
+       return 0;
+}
diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h
new file mode 100644 (file)
index 0000000..a4be857
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef _PERF_TARGET_H
+#define _PERF_TARGET_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+struct perf_target {
+       const char   *pid;
+       const char   *tid;
+       const char   *cpu_list;
+       const char   *uid_str;
+       uid_t        uid;
+       bool         system_wide;
+       bool         uses_mmap;
+};
+
+enum perf_target_errno {
+       PERF_ERRNO_TARGET__SUCCESS              = 0,
+
+       /*
+        * Choose an arbitrary negative big number not to clash with standard
+        * errno since SUS requires the errno has distinct positive values.
+        * See 'Issue 6' in the link below.
+        *
+        * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+        */
+       __PERF_ERRNO_TARGET__START              = -10000,
+
+
+       /* for perf_target__validate() */
+       PERF_ERRNO_TARGET__PID_OVERRIDE_CPU     = __PERF_ERRNO_TARGET__START,
+       PERF_ERRNO_TARGET__PID_OVERRIDE_UID,
+       PERF_ERRNO_TARGET__UID_OVERRIDE_CPU,
+       PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM,
+       PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM,
+
+       /* for perf_target__parse_uid() */
+       PERF_ERRNO_TARGET__INVALID_UID,
+       PERF_ERRNO_TARGET__USER_NOT_FOUND,
+
+       __PERF_ERRNO_TARGET__END,
+};
+
+enum perf_target_errno perf_target__validate(struct perf_target *target);
+enum perf_target_errno perf_target__parse_uid(struct perf_target *target);
+
+int perf_target__strerror(struct perf_target *target, int errnum, char *buf,
+                         size_t buflen);
+
+static inline bool perf_target__has_task(struct perf_target *target)
+{
+       return target->tid || target->pid || target->uid_str;
+}
+
+static inline bool perf_target__has_cpu(struct perf_target *target)
+{
+       return target->system_wide || target->cpu_list;
+}
+
+static inline bool perf_target__none(struct perf_target *target)
+{
+       return !perf_target__has_task(target) && !perf_target__has_cpu(target);
+}
+
+#endif /* _PERF_TARGET_H */
index 7da80f1..f718df8 100644 (file)
@@ -6,7 +6,7 @@
 
 struct thread_map {
        int nr;
-       int map[];
+       pid_t map[];
 };
 
 struct thread_map *thread_map__new_by_pid(pid_t pid);
index 09fe579..abe0e8e 100644 (file)
@@ -69,23 +69,24 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
 
        ret += SNPRINTF(bf + ret, size - ret, "], ");
 
-       if (top->target_pid)
+       if (top->target.pid)
                ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s",
-                               top->target_pid);
-       else if (top->target_tid)
+                               top->target.pid);
+       else if (top->target.tid)
                ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s",
-                               top->target_tid);
-       else if (top->uid_str != NULL)
+                               top->target.tid);
+       else if (top->target.uid_str != NULL)
                ret += SNPRINTF(bf + ret, size - ret, " (uid: %s",
-                               top->uid_str);
+                               top->target.uid_str);
        else
                ret += SNPRINTF(bf + ret, size - ret, " (all");
 
-       if (top->cpu_list)
+       if (top->target.cpu_list)
                ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)",
-                               top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list);
+                               top->evlist->cpus->nr > 1 ? "s" : "",
+                               top->target.cpu_list);
        else {
-               if (top->target_tid)
+               if (top->target.tid)
                        ret += SNPRINTF(bf + ret, size - ret, ")");
                else
                        ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)",
index ce61cb2..33347ca 100644 (file)
@@ -13,6 +13,7 @@ struct perf_session;
 struct perf_top {
        struct perf_tool   tool;
        struct perf_evlist *evlist;
+       struct perf_target target;
        /*
         * Symbols will be added here in perf_event__process_sample and will
         * get out after decayed.
@@ -23,10 +24,7 @@ struct perf_top {
        u64                guest_us_samples, guest_kernel_samples;
        int                print_entries, count_filter, delay_secs;
        int                freq;
-       const char         *target_pid, *target_tid;
-       uid_t              uid;
        bool               hide_kernel_symbols, hide_user_symbols, zero;
-       bool               system_wide;
        bool               use_tui, use_stdio;
        bool               sort_has_symbols;
        bool               dont_use_callchains;
@@ -37,7 +35,6 @@ struct perf_top {
        bool               sample_id_all_missing;
        bool               exclude_guest_missing;
        bool               dump_symtab;
-       const char         *cpu_list;
        struct hist_entry  *sym_filter_entry;
        struct perf_evsel  *sym_evsel;
        struct perf_session *session;
@@ -47,7 +44,6 @@ struct perf_top {
        int                realtime_prio;
        int                sym_pcnt_filter;
        const char         *sym_filter;
-       const char         *uid_str;
 };
 
 size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size);
index fc22cf5..a8d81c3 100644 (file)
@@ -68,7 +68,7 @@ struct events {
 };
 
 
-void *malloc_or_die(unsigned int size)
+static void *malloc_or_die(unsigned int size)
 {
        void *data;
 
@@ -448,6 +448,8 @@ static void tracing_data_header(void)
        else
                buf[0] = 0;
 
+       read_trace_init(buf[0], buf[0]);
+
        write_or_die(buf, 1);
 
        /* save size of long */
index dfd1bd8..df2fddb 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *  The parts for function graph printing was taken and modified from the
- *  Linux Kernel that were written by Frederic Weisbecker.
  */
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 #include <errno.h>
 
 #include "../perf.h"
 #include "util.h"
 #include "trace-event.h"
 
-int header_page_ts_offset;
-int header_page_ts_size;
-int header_page_size_offset;
 int header_page_size_size;
-int header_page_overwrite_offset;
-int header_page_overwrite_size;
+int header_page_ts_size;
 int header_page_data_offset;
-int header_page_data_size;
 
-bool latency_format;
-
-static char *input_buf;
-static unsigned long long input_buf_ptr;
-static unsigned long long input_buf_siz;
+struct pevent *perf_pevent;
+static struct pevent *pevent;
 
-static int cpus;
-static int long_size;
-static int is_flag_field;
-static int is_symbolic_field;
-
-static struct format_field *
-find_any_field(struct event *event, const char *name);
+bool latency_format;
 
-static void init_input_buf(char *buf, unsigned long long size)
+int read_trace_init(int file_bigendian, int host_bigendian)
 {
-       input_buf = buf;
-       input_buf_siz = size;
-       input_buf_ptr = 0;
-}
-
-struct cmdline {
-       char *comm;
-       int pid;
-};
-
-static struct cmdline *cmdlines;
-static int cmdline_count;
+       if (pevent)
+               return 0;
 
-static int cmdline_cmp(const void *a, const void *b)
-{
-       const struct cmdline *ca = a;
-       const struct cmdline *cb = b;
+       perf_pevent = pevent_alloc();
+       pevent = perf_pevent;
 
-       if (ca->pid < cb->pid)
-               return -1;
-       if (ca->pid > cb->pid)
-               return 1;
+       pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT);
+       pevent_set_file_bigendian(pevent, file_bigendian);
+       pevent_set_host_bigendian(pevent, host_bigendian);
 
        return 0;
 }
 
-void parse_cmdlines(char *file, int size __unused)
+static int get_common_field(struct scripting_context *context,
+                           int *offset, int *size, const char *type)
 {
-       struct cmdline_list {
-               struct cmdline_list     *next;
-               char                    *comm;
-               int                     pid;
-       } *list = NULL, *item;
-       char *line;
-       char *next = NULL;
-       int i;
-
-       line = strtok_r(file, "\n", &next);
-       while (line) {
-               item = malloc_or_die(sizeof(*item));
-               sscanf(line, "%d %as", &item->pid,
-                      (float *)(void *)&item->comm); /* workaround gcc warning */
-               item->next = list;
-               list = item;
-               line = strtok_r(NULL, "\n", &next);
-               cmdline_count++;
-       }
+       struct event_format *event;
+       struct format_field *field;
 
-       cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count);
+       if (!*size) {
+               if (!pevent->events)
+                       return 0;
 
-       i = 0;
-       while (list) {
-               cmdlines[i].pid = list->pid;
-               cmdlines[i].comm = list->comm;
-               i++;
-               item = list;
-               list = list->next;
-               free(item);
+               event = pevent->events[0];
+               field = pevent_find_common_field(event, type);
+               if (!field)
+                       return 0;
+               *offset = field->offset;
+               *size = field->size;
        }
 
-       qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+       return pevent_read_number(pevent, context->event_data + *offset, *size);
 }
 
-static struct func_map {
-       unsigned long long              addr;
-       char                            *func;
-       char                            *mod;
-} *func_list;
-static unsigned int func_count;
-
-static int func_cmp(const void *a, const void *b)
+int common_lock_depth(struct scripting_context *context)
 {
-       const struct func_map *fa = a;
-       const struct func_map *fb = b;
+       static int offset;
+       static int size;
+       int ret;
 
-       if (fa->addr < fb->addr)
+       ret = get_common_field(context, &size, &offset,
+                              "common_lock_depth");
+       if (ret < 0)
                return -1;
-       if (fa->addr > fb->addr)
-               return 1;
-
-       return 0;
-}
-
-void parse_proc_kallsyms(char *file, unsigned int size __unused)
-{
-       struct func_list {
-               struct func_list        *next;
-               unsigned long long      addr;
-               char                    *func;
-               char                    *mod;
-       } *list = NULL, *item;
-       char *line;
-       char *next = NULL;
-       char *addr_str;
-       char ch;
-       int ret __used;
-       int i;
-
-       line = strtok_r(file, "\n", &next);
-       while (line) {
-               item = malloc_or_die(sizeof(*item));
-               item->mod = NULL;
-               ret = sscanf(line, "%as %c %as\t[%as",
-                            (float *)(void *)&addr_str, /* workaround gcc warning */
-                            &ch,
-                            (float *)(void *)&item->func,
-                            (float *)(void *)&item->mod);
-               item->addr = strtoull(addr_str, NULL, 16);
-               free(addr_str);
-
-               /* truncate the extra ']' */
-               if (item->mod)
-                       item->mod[strlen(item->mod) - 1] = 0;
 
-
-               item->next = list;
-               list = item;
-               line = strtok_r(NULL, "\n", &next);
-               func_count++;
-       }
-
-       func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1));
-
-       i = 0;
-       while (list) {
-               func_list[i].func = list->func;
-               func_list[i].addr = list->addr;
-               func_list[i].mod = list->mod;
-               i++;
-               item = list;
-               list = list->next;
-               free(item);
-       }
-
-       qsort(func_list, func_count, sizeof(*func_list), func_cmp);
-
-       /*
-        * Add a special record at the end.
-        */
-       func_list[func_count].func = NULL;
-       func_list[func_count].addr = 0;
-       func_list[func_count].mod = NULL;
+       return ret;
 }
 
-/*
- * We are searching for a record in between, not an exact
- * match.
- */
-static int func_bcmp(const void *a, const void *b)
+int common_flags(struct scripting_context *context)
 {
-       const struct func_map *fa = a;
-       const struct func_map *fb = b;
-
-       if ((fa->addr == fb->addr) ||
-
-           (fa->addr > fb->addr &&
-            fa->addr < (fb+1)->addr))
-               return 0;
+       static int offset;
+       static int size;
+       int ret;
 
-       if (fa->addr < fb->addr)
+       ret = get_common_field(context, &size, &offset,
+                              "common_flags");
+       if (ret < 0)
                return -1;
 
-       return 1;
-}
-
-static struct func_map *find_func(unsigned long long addr)
-{
-       struct func_map *func;
-       struct func_map key;
-
-       key.addr = addr;
-
-       func = bsearch(&key, func_list, func_count, sizeof(*func_list),
-                      func_bcmp);
-
-       return func;
-}
-
-void print_funcs(void)
-{
-       int i;
-
-       for (i = 0; i < (int)func_count; i++) {
-               printf("%016llx %s",
-                      func_list[i].addr,
-                      func_list[i].func);
-               if (func_list[i].mod)
-                       printf(" [%s]\n", func_list[i].mod);
-               else
-                       printf("\n");
-       }
+       return ret;
 }
 
-static struct printk_map {
-       unsigned long long              addr;
-       char                            *printk;
-} *printk_list;
-static unsigned int printk_count;
-
-static int printk_cmp(const void *a, const void *b)
+int common_pc(struct scripting_context *context)
 {
-       const struct func_map *fa = a;
-       const struct func_map *fb = b;
+       static int offset;
+       static int size;
+       int ret;
 
-       if (fa->addr < fb->addr)
+       ret = get_common_field(context, &size, &offset,
+                              "common_preempt_count");
+       if (ret < 0)
                return -1;
-       if (fa->addr > fb->addr)
-               return 1;
 
-       return 0;
+       return ret;
 }
 
-static struct printk_map *find_printk(unsigned long long addr)
+unsigned long long
+raw_field_value(struct event_format *event, const char *name, void *data)
 {
-       struct printk_map *printk;
-       struct printk_map key;
+       struct format_field *field;
+       unsigned long long val;
 
-       key.addr = addr;
+       field = pevent_find_any_field(event, name);
+       if (!field)
+               return 0ULL;
 
-       printk = bsearch(&key, printk_list, printk_count, sizeof(*printk_list),
-                        printk_cmp);
+       pevent_read_number_field(field, data, &val);
 
-       return printk;
+       return val;
 }
 
-void parse_ftrace_printk(char *file, unsigned int size __unused)
+void *raw_field_ptr(struct event_format *event, const char *name, void *data)
 {
-       struct printk_list {
-               struct printk_list      *next;
-               unsigned long long      addr;
-               char                    *printk;
-       } *list = NULL, *item;
-       char *line;
-       char *next = NULL;
-       char *addr_str;
-       int i;
-
-       line = strtok_r(file, "\n", &next);
-       while (line) {
-               addr_str = strsep(&line, ":");
-               if (!line) {
-                       warning("error parsing print strings");
-                       break;
-               }
-               item = malloc_or_die(sizeof(*item));
-               item->addr = strtoull(addr_str, NULL, 16);
-               /* fmt still has a space, skip it */
-               item->printk = strdup(line+1);
-               item->next = list;
-               list = item;
-               line = strtok_r(NULL, "\n", &next);
-               printk_count++;
-       }
-
-       printk_list = malloc_or_die(sizeof(*printk_list) * printk_count + 1);
+       struct format_field *field;
 
-       i = 0;
-       while (list) {
-               printk_list[i].printk = list->printk;
-               printk_list[i].addr = list->addr;
-               i++;
-               item = list;
-               list = list->next;
-               free(item);
-       }
+       field = pevent_find_any_field(event, name);
+       if (!field)
+               return NULL;
 
-       qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp);
-}
+       if (field->flags & FIELD_IS_DYNAMIC) {
+               int offset;
 
-void print_printk(void)
-{
-       int i;
+               offset = *(int *)(data + field->offset);
+               offset &= 0xffff;
 
-       for (i = 0; i < (int)printk_count; i++) {
-               printf("%016llx %s\n",
-                      printk_list[i].addr,
-                      printk_list[i].printk);
+               return data + offset;
        }
-}
-
-static struct event *alloc_event(void)
-{
-       struct event *event;
 
-       event = malloc_or_die(sizeof(*event));
-       memset(event, 0, sizeof(*event));
-
-       return event;
+       return data + field->offset;
 }
 
-enum event_type {
-       EVENT_ERROR,
-       EVENT_NONE,
-       EVENT_SPACE,
-       EVENT_NEWLINE,
-       EVENT_OP,
-       EVENT_DELIM,
-       EVENT_ITEM,
-       EVENT_DQUOTE,
-       EVENT_SQUOTE,
-};
-
-static struct event *event_list;
-
-static void add_event(struct event *event)
+int trace_parse_common_type(void *data)
 {
-       event->next = event_list;
-       event_list = event;
-}
+       struct pevent_record record;
 
-static int event_item_type(enum event_type type)
-{
-       switch (type) {
-       case EVENT_ITEM ... EVENT_SQUOTE:
-               return 1;
-       case EVENT_ERROR ... EVENT_DELIM:
-       default:
-               return 0;
-       }
+       record.data = data;
+       return pevent_data_type(pevent, &record);
 }
 
-static void free_arg(struct print_arg *arg)
+int trace_parse_common_pid(void *data)
 {
-       if (!arg)
-               return;
+       struct pevent_record record;
 
-       switch (arg->type) {
-       case PRINT_ATOM:
-               if (arg->atom.atom)
-                       free(arg->atom.atom);
-               break;
-       case PRINT_NULL:
-       case PRINT_FIELD ... PRINT_OP:
-       default:
-               /* todo */
-               break;
-       }
-
-       free(arg);
+       record.data = data;
+       return pevent_data_pid(pevent, &record);
 }
 
-static enum event_type get_type(int ch)
+unsigned long long read_size(void *ptr, int size)
 {
-       if (ch == '\n')
-               return EVENT_NEWLINE;
-       if (isspace(ch))
-               return EVENT_SPACE;
-       if (isalnum(ch) || ch == '_')
-               return EVENT_ITEM;
-       if (ch == '\'')
-               return EVENT_SQUOTE;
-       if (ch == '"')
-               return EVENT_DQUOTE;
-       if (!isprint(ch))
-               return EVENT_NONE;
-       if (ch == '(' || ch == ')' || ch == ',')
-               return EVENT_DELIM;
-
-       return EVENT_OP;
+       return pevent_read_number(pevent, ptr, size);
 }
 
-static int __read_char(void)
+struct event_format *trace_find_event(int type)
 {
-       if (input_buf_ptr >= input_buf_siz)
-               return -1;
-
-       return input_buf[input_buf_ptr++];
+       return pevent_find_event(pevent, type);
 }
 
-static int __peek_char(void)
+
+void print_trace_event(int cpu, void *data, int size)
 {
-       if (input_buf_ptr >= input_buf_siz)
-               return -1;
+       struct event_format *event;
+       struct pevent_record record;
+       struct trace_seq s;
+       int type;
 
-       return input_buf[input_buf_ptr];
-}
+       type = trace_parse_common_type(data);
 
-static enum event_type __read_token(char **tok)
-{
-       char buf[BUFSIZ];
-       int ch, last_ch, quote_ch, next_ch;
-       int i = 0;
-       int tok_size = 0;
-       enum event_type type;
-
-       *tok = NULL;
-
-
-       ch = __read_char();
-       if (ch < 0)
-               return EVENT_NONE;
-
-       type = get_type(ch);
-       if (type == EVENT_NONE)
-               return type;
-
-       buf[i++] = ch;
-
-       switch (type) {
-       case EVENT_NEWLINE:
-       case EVENT_DELIM:
-               *tok = malloc_or_die(2);
-               (*tok)[0] = ch;
-               (*tok)[1] = 0;
-               return type;
-
-       case EVENT_OP:
-               switch (ch) {
-               case '-':
-                       next_ch = __peek_char();
-                       if (next_ch == '>') {
-                               buf[i++] = __read_char();
-                               break;
-                       }
-                       /* fall through */
-               case '+':
-               case '|':
-               case '&':
-               case '>':
-               case '<':
-                       last_ch = ch;
-                       ch = __peek_char();
-                       if (ch != last_ch)
-                               goto test_equal;
-                       buf[i++] = __read_char();
-                       switch (last_ch) {
-                       case '>':
-                       case '<':
-                               goto test_equal;
-                       default:
-                               break;
-                       }
-                       break;
-               case '!':
-               case '=':
-                       goto test_equal;
-               default: /* what should we do instead? */
-                       break;
-               }
-               buf[i] = 0;
-               *tok = strdup(buf);
-               return type;
-
- test_equal:
-               ch = __peek_char();
-               if (ch == '=')
-                       buf[i++] = __read_char();
-               break;
-
-       case EVENT_DQUOTE:
-       case EVENT_SQUOTE:
-               /* don't keep quotes */
-               i--;
-               quote_ch = ch;
-               last_ch = 0;
-               do {
-                       if (i == (BUFSIZ - 1)) {
-                               buf[i] = 0;
-                               if (*tok) {
-                                       *tok = realloc(*tok, tok_size + BUFSIZ);
-                                       if (!*tok)
-                                               return EVENT_NONE;
-                                       strcat(*tok, buf);
-                               } else
-                                       *tok = strdup(buf);
-
-                               if (!*tok)
-                                       return EVENT_NONE;
-                               tok_size += BUFSIZ;
-                               i = 0;
-                       }
-                       last_ch = ch;
-                       ch = __read_char();
-                       buf[i++] = ch;
-                       /* the '\' '\' will cancel itself */
-                       if (ch == '\\' && last_ch == '\\')
-                               last_ch = 0;
-               } while (ch != quote_ch || last_ch == '\\');
-               /* remove the last quote */
-               i--;
-               goto out;
-
-       case EVENT_ERROR ... EVENT_SPACE:
-       case EVENT_ITEM:
-       default:
-               break;
+       event = trace_find_event(type);
+       if (!event) {
+               warning("ug! no event found for type %d", type);
+               return;
        }
 
-       while (get_type(__peek_char()) == type) {
-               if (i == (BUFSIZ - 1)) {
-                       buf[i] = 0;
-                       if (*tok) {
-                               *tok = realloc(*tok, tok_size + BUFSIZ);
-                               if (!*tok)
-                                       return EVENT_NONE;
-                               strcat(*tok, buf);
-                       } else
-                               *tok = strdup(buf);
-
-                       if (!*tok)
-                               return EVENT_NONE;
-                       tok_size += BUFSIZ;
-                       i = 0;
-               }
-               ch = __read_char();
-               buf[i++] = ch;
-       }
+       memset(&record, 0, sizeof(record));
+       record.cpu = cpu;
+       record.size = size;
+       record.data = data;
 
- out:
-       buf[i] = 0;
-       if (*tok) {
-               *tok = realloc(*tok, tok_size + i);
-               if (!*tok)
-                       return EVENT_NONE;
-               strcat(*tok, buf);
-       } else
-               *tok = strdup(buf);
-       if (!*tok)
-               return EVENT_NONE;
-
-       return type;
+       trace_seq_init(&s);
+       pevent_print_event(pevent, &s, &record);
+       trace_seq_do_printf(&s);
+       printf("\n");
 }
 
-static void free_token(char *tok)
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+                 char *comm)
 {
-       if (tok)
-               free(tok);
-}
+       struct pevent_record record;
+       struct trace_seq s;
+       int pid;
 
-static enum event_type read_token(char **tok)
-{
-       enum event_type type;
+       pevent->latency_format = latency_format;
 
-       for (;;) {
-               type = __read_token(tok);
-               if (type != EVENT_SPACE)
-                       return type;
+       record.ts = nsecs;
+       record.cpu = cpu;
+       record.size = size;
+       record.data = data;
+       pid = pevent_data_pid(pevent, &record);
 
-               free_token(*tok);
-       }
+       if (!pevent_pid_is_registered(pevent, pid))
+               pevent_register_comm(pevent, comm, pid);
 
-       /* not reached */
-       return EVENT_NONE;
+       trace_seq_init(&s);
+       pevent_print_event(pevent, &s, &record);
+       trace_seq_do_printf(&s);
+       printf("\n");
 }
 
-/* no newline */
-static enum event_type read_token_item(char **tok)
+void parse_proc_kallsyms(char *file, unsigned int size __unused)
 {
-       enum event_type type;
+       unsigned long long addr;
+       char *func;
+       char *line;
+       char *next = NULL;
+       char *addr_str;
+       char *mod;
+       char ch;
 
-       for (;;) {
-               type = __read_token(tok);
-               if (type != EVENT_SPACE && type != EVENT_NEWLINE)
-                       return type;
+       line = strtok_r(file, "\n", &next);
+       while (line) {
+               mod = NULL;
+               sscanf(line, "%as %c %as\t[%as",
+                      (float *)(void *)&addr_str, /* workaround gcc warning */
+                      &ch, (float *)(void *)&func, (float *)(void *)&mod);
+               addr = strtoull(addr_str, NULL, 16);
+               free(addr_str);
 
-               free_token(*tok);
-       }
+               /* truncate the extra ']' */
+               if (mod)
+                       mod[strlen(mod) - 1] = 0;
 
-       /* not reached */
-       return EVENT_NONE;
-}
+               pevent_register_function(pevent, func, addr, mod);
+               free(func);
+               free(mod);
 
-static int test_type(enum event_type type, enum event_type expect)
-{
-       if (type != expect) {
-               warning("Error: expected type %d but read %d",
-                   expect, type);
-               return -1;
+               line = strtok_r(NULL, "\n", &next);
        }
-       return 0;
 }
 
-static int __test_type_token(enum event_type type, char *token,
-                            enum event_type expect, const char *expect_tok,
-                            bool warn)
+void parse_ftrace_printk(char *file, unsigned int size __unused)
 {
-       if (type != expect) {
-               if (warn)
-                       warning("Error: expected type %d but read %d",
-                               expect, type);
-               return -1;
-       }
+       unsigned long long addr;
+       char *printk;
+       char *line;
+       char *next = NULL;
+       char *addr_str;
+       char *fmt;
 
-       if (strcmp(token, expect_tok) != 0) {
-               if (warn)
-                       warning("Error: expected '%s' but read '%s'",
-                               expect_tok, token);
-               return -1;
+       line = strtok_r(file, "\n", &next);
+       while (line) {
+               addr_str = strtok_r(line, ":", &fmt);
+               if (!addr_str) {
+                       warning("printk format with empty entry");
+                       break;
+               }
+               addr = strtoull(addr_str, NULL, 16);
+               /* fmt still has a space, skip it */
+               printk = strdup(fmt+1);
+               line = strtok_r(NULL, "\n", &next);
+               pevent_register_print_string(pevent, printk, addr);
        }
-       return 0;
-}
-
-static int test_type_token(enum event_type type, char *token,
-                          enum event_type expect, const char *expect_tok)
-{
-       return __test_type_token(type, token, expect, expect_tok, true);
-}
-
-static int __read_expect_type(enum event_type expect, char **tok, int newline_ok)
-{
-       enum event_type type;
-
-       if (newline_ok)
-               type = read_token(tok);
-       else
-               type = read_token_item(tok);
-       return test_type(type, expect);
-}
-
-static int read_expect_type(enum event_type expect, char **tok)
-{
-       return __read_expect_type(expect, tok, 1);
-}
-
-static int __read_expected(enum event_type expect, const char *str,
-                          int newline_ok, bool warn)
-{
-       enum event_type type;
-       char *token;
-       int ret;
-
-       if (newline_ok)
-               type = read_token(&token);
-       else
-               type = read_token_item(&token);
-
-       ret = __test_type_token(type, token, expect, str, warn);
-
-       free_token(token);
-
-       return ret;
 }
 
-static int read_expected(enum event_type expect, const char *str)
+int parse_ftrace_file(char *buf, unsigned long size)
 {
-       return __read_expected(expect, str, 1, true);
+       return pevent_parse_event(pevent, buf, size, "ftrace");
 }
 
-static int read_expected_item(enum event_type expect, const char *str)
+int parse_event_file(char *buf, unsigned long size, char *sys)
 {
-       return __read_expected(expect, str, 0, true);
+       return pevent_parse_event(pevent, buf, size, sys);
 }
 
-static char *event_read_name(void)
+struct event_format *trace_find_next_event(struct event_format *event)
 {
-       char *token;
-
-       if (read_expected(EVENT_ITEM, "name") < 0)
-               return NULL;
+       static int idx;
 
-       if (read_expected(EVENT_OP, ":") < 0)
+       if (!pevent->events)
                return NULL;
 
-       if (read_expect_type(EVENT_ITEM, &token) < 0)
-               goto fail;
+       if (!event) {
+               idx = 0;
+               return pevent->events[0];
+       }
 
-       return token;
+       if (idx < pevent->nr_events && event == pevent->events[idx]) {
+               idx++;
+               if (idx == pevent->nr_events)
+                       return NULL;
+               return pevent->events[idx];
+       }
 
- fail:
-       free_token(token);
+       for (idx = 1; idx < pevent->nr_events; idx++) {
+               if (event == pevent->events[idx - 1])
+                       return pevent->events[idx];
+       }
        return NULL;
 }
 
-static int event_read_id(void)
-{
-       char *token;
-       int id = -1;
-
-       if (read_expected_item(EVENT_ITEM, "ID") < 0)
-               return -1;
+struct flag {
+       const char *name;
+       unsigned long long value;
+};
 
-       if (read_expected(EVENT_OP, ":") < 0)
-               return -1;
+static const struct flag flags[] = {
+       { "HI_SOFTIRQ", 0 },
+       { "TIMER_SOFTIRQ", 1 },
+       { "NET_TX_SOFTIRQ", 2 },
+       { "NET_RX_SOFTIRQ", 3 },
+       { "BLOCK_SOFTIRQ", 4 },
+       { "BLOCK_IOPOLL_SOFTIRQ", 5 },
+       { "TASKLET_SOFTIRQ", 6 },
+       { "SCHED_SOFTIRQ", 7 },
+       { "HRTIMER_SOFTIRQ", 8 },
+       { "RCU_SOFTIRQ", 9 },
 
-       if (read_expect_type(EVENT_ITEM, &token) < 0)
-               goto free;
+       { "HRTIMER_NORESTART", 0 },
+       { "HRTIMER_RESTART", 1 },
+};
 
-       id = strtoul(token, NULL, 0);
+unsigned long long eval_flag(const char *flag)
+{
+       int i;
 
- free:
-       free_token(token);
-       return id;
-}
+       /*
+        * Some flags in the format files do not get converted.
+        * If the flag is not numeric, see if it is something that
+        * we already know about.
+        */
+       if (isdigit(flag[0]))
+               return strtoull(flag, NULL, 0);
 
-static int field_is_string(struct format_field *field)
-{
-       if ((field->flags & FIELD_IS_ARRAY) &&
-           (!strstr(field->type, "char") || !strstr(field->type, "u8") ||
-            !strstr(field->type, "s8")))
-               return 1;
+       for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++)
+               if (strcmp(flags[i].name, flag) == 0)
+                       return flags[i].value;
 
        return 0;
 }
-
-static int field_is_dynamic(struct format_field *field)
-{
-       if (!strncmp(field->type, "__data_loc", 10))
-               return 1;
-
-       return 0;
-}
-
-static int event_read_fields(struct event *event, struct format_field **fields)
-{
-       struct format_field *field = NULL;
-       enum event_type type;
-       char *token;
-       char *last_token;
-       int count = 0;
-
-       do {
-               type = read_token(&token);
-               if (type == EVENT_NEWLINE) {
-                       free_token(token);
-                       return count;
-               }
-
-               count++;
-
-               if (test_type_token(type, token, EVENT_ITEM, "field"))
-                       goto fail;
-               free_token(token);
-
-               type = read_token(&token);
-               /*
-                * The ftrace fields may still use the "special" name.
-                * Just ignore it.
-                */
-               if (event->flags & EVENT_FL_ISFTRACE &&
-                   type == EVENT_ITEM && strcmp(token, "special") == 0) {
-                       free_token(token);
-                       type = read_token(&token);
-               }
-
-               if (test_type_token(type, token, EVENT_OP, ":") < 0)
-                       return -1;
-
-               if (read_expect_type(EVENT_ITEM, &token) < 0)
-                       goto fail;
-
-               last_token = token;
-
-               field = malloc_or_die(sizeof(*field));
-               memset(field, 0, sizeof(*field));
-
-               /* read the rest of the type */
-               for (;;) {
-                       type = read_token(&token);
-                       if (type == EVENT_ITEM ||
-                           (type == EVENT_OP && strcmp(token, "*") == 0) ||
-                           /*
-                            * Some of the ftrace fields are broken and have
-                            * an illegal "." in them.
-                            */
-                           (event->flags & EVENT_FL_ISFTRACE &&
-                            type == EVENT_OP && strcmp(token, ".") == 0)) {
-
-                               if (strcmp(token, "*") == 0)
-                                       field->flags |= FIELD_IS_POINTER;
-
-                               if (field->type) {
-                                       field->type = realloc(field->type,
-                                                             strlen(field->type) +
-                                                             strlen(last_token) + 2);
-                                       strcat(field->type, " ");
-                                       strcat(field->type, last_token);
-                               } else
-                                       field->type = last_token;
-                               last_token = token;
-                               continue;
-                       }
-
-                       break;
-               }
-
-               if (!field->type) {
-                       die("no type found");
-                       goto fail;
-               }
-               field->name = last_token;
-
-               if (test_type(type, EVENT_OP))
-                       goto fail;
-
-               if (strcmp(token, "[") == 0) {
-                       enum event_type last_type = type;
-                       char *brackets = token;
-                       int len;
-
-                       field->flags |= FIELD_IS_ARRAY;
-
-                       type = read_token(&token);
-                       while (strcmp(token, "]") != 0) {
-                               if (last_type == EVENT_ITEM &&
-                                   type == EVENT_ITEM)
-                                       len = 2;
-                               else
-                                       len = 1;
-                               last_type = type;
-
-                               brackets = realloc(brackets,
-                                                  strlen(brackets) +
-                                                  strlen(token) + len);
-                               if (len == 2)
-                                       strcat(brackets, " ");
-                               strcat(brackets, token);
-                               free_token(token);
-                               type = read_token(&token);
-                               if (type == EVENT_NONE) {
-                                       die("failed to find token");
-                                       goto fail;
-                               }
-                       }
-
-                       free_token(token);
-
-                       brackets = realloc(brackets, strlen(brackets) + 2);
-                       strcat(brackets, "]");
-
-                       /* add brackets to type */
-
-                       type = read_token(&token);
-                       /*
-                        * If the next token is not an OP, then it is of
-                        * the format: type [] item;
-                        */
-                       if (type == EVENT_ITEM) {
-                               field->type = realloc(field->type,
-                                                     strlen(field->type) +
-                                                     strlen(field->name) +
-                                                     strlen(brackets) + 2);
-                               strcat(field->type, " ");
-                               strcat(field->type, field->name);
-                               free_token(field->name);
-                               strcat(field->type, brackets);
-                               field->name = token;
-                               type = read_token(&token);
-                       } else {
-                               field->type = realloc(field->type,
-                                                     strlen(field->type) +
-                                                     strlen(brackets) + 1);
-                               strcat(field->type, brackets);
-                       }
-                       free(brackets);
-               }
-
-               if (field_is_string(field)) {
-                       field->flags |= FIELD_IS_STRING;
-                       if (field_is_dynamic(field))
-                               field->flags |= FIELD_IS_DYNAMIC;
-               }
-
-               if (test_type_token(type, token,  EVENT_OP, ";"))
-                       goto fail;
-               free_token(token);
-
-               if (read_expected(EVENT_ITEM, "offset") < 0)
-                       goto fail_expect;
-
-               if (read_expected(EVENT_OP, ":") < 0)
-                       goto fail_expect;
-
-               if (read_expect_type(EVENT_ITEM, &token))
-                       goto fail;
-               field->offset = strtoul(token, NULL, 0);
-               free_token(token);
-
-               if (read_expected(EVENT_OP, ";") < 0)
-                       goto fail_expect;
-
-               if (read_expected(EVENT_ITEM, "size") < 0)
-                       goto fail_expect;
-
-               if (read_expected(EVENT_OP, ":") < 0)
-                       goto fail_expect;
-
-               if (read_expect_type(EVENT_ITEM, &token))
-                       goto fail;
-               field->size = strtoul(token, NULL, 0);
-               free_token(token);
-
-               if (read_expected(EVENT_OP, ";") < 0)
-                       goto fail_expect;
-
-               type = read_token(&token);
-               if (type != EVENT_NEWLINE) {
-                       /* newer versions of the kernel have a "signed" type */
-                       if (test_type_token(type, token, EVENT_ITEM, "signed"))
-                               goto fail;
-
-                       free_token(token);
-
-                       if (read_expected(EVENT_OP, ":") < 0)
-                               goto fail_expect;
-
-                       if (read_expect_type(EVENT_ITEM, &token))
-                               goto fail;
-
-                       if (strtoul(token, NULL, 0))
-                               field->flags |= FIELD_IS_SIGNED;
-
-                       free_token(token);
-                       if (read_expected(EVENT_OP, ";") < 0)
-                               goto fail_expect;
-
-                       if (read_expect_type(EVENT_NEWLINE, &token))
-                               goto fail;
-               }
-
-               free_token(token);
-
-               *fields = field;
-               fields = &field->next;
-
-       } while (1);
-
-       return 0;
-
-fail:
-       free_token(token);
-fail_expect:
-       if (field)
-               free(field);
-       return -1;
-}
-
-static int event_read_format(struct event *event)
-{
-       char *token;
-       int ret;
-
-       if (read_expected_item(EVENT_ITEM, "format") < 0)
-               return -1;
-
-       if (read_expected(EVENT_OP, ":") < 0)
-               return -1;
-
-       if (read_expect_type(EVENT_NEWLINE, &token))
-               goto fail;
-       free_token(token);
-
-       ret = event_read_fields(event, &event->format.common_fields);
-       if (ret < 0)
-               return ret;
-       event->format.nr_common = ret;
-
-       ret = event_read_fields(event, &event->format.fields);
-       if (ret < 0)
-               return ret;
-       event->format.nr_fields = ret;
-
-       return 0;
-
- fail:
-       free_token(token);
-       return -1;
-}
-
-enum event_type
-process_arg_token(struct event *event, struct print_arg *arg,
-                 char **tok, enum event_type type);
-
-static enum event_type
-process_arg(struct event *event, struct print_arg *arg, char **tok)
-{
-       enum event_type type;
-       char *token;
-
-       type = read_token(&token);
-       *tok = token;
-
-       return process_arg_token(event, arg, tok, type);
-}
-
-static enum event_type
-process_cond(struct event *event, struct print_arg *top, char **tok)
-{
-       struct print_arg *arg, *left, *right;
-       enum event_type type;
-       char *token = NULL;
-
-       arg = malloc_or_die(sizeof(*arg));
-       memset(arg, 0, sizeof(*arg));
-
-       left = malloc_or_die(sizeof(*left));
-
-       right = malloc_or_die(sizeof(*right));
-
-       arg->type = PRINT_OP;
-       arg->op.left = left;
-       arg->op.right = right;
-
-       *tok = NULL;
-       type = process_arg(event, left, &token);
-       if (test_type_token(type, token, EVENT_OP, ":"))
-               goto out_free;
-
-       arg->op.op = token;
-
-       type = process_arg(event, right, &token);
-
-       top->op.right = arg;
-
-       *tok = token;
-       return type;
-
-out_free:
-       free_token(*tok);
-       free(right);
-       free(left);
-       free_arg(arg);
-       return EVENT_ERROR;
-}
-
-static enum event_type
-process_array(struct event *event, struct print_arg *top, char **tok)
-{
-       struct print_arg *arg;
-       enum event_type type;
-       char *token = NULL;
-
-       arg = malloc_or_die(sizeof(*arg));
-       memset(arg, 0, sizeof(*arg));
-
-       *tok = NULL;
-       type = process_arg(event, arg, &token);
-       if (test_type_token(type, token, EVENT_OP, "]"))
-               goto out_free;
-
-       top->op.right = arg;
-
-       free_token(token);
-       type = read_token_item(&token);
-       *tok = token;
-
-       return type;
-
-out_free:
-       free_token(*tok);
-       free_arg(arg);
-       return EVENT_ERROR;
-}
-
-static int get_op_prio(char *op)
-{
-       if (!op[1]) {
-               switch (op[0]) {
-               case '*':
-               case '/':
-               case '%':
-                       return 6;
-               case '+':
-               case '-':
-                       return 7;
-                       /* '>>' and '<<' are 8 */
-               case '<':
-               case '>':
-                       return 9;
-                       /* '==' and '!=' are 10 */
-               case '&':
-                       return 11;
-               case '^':
-                       return 12;
-               case '|':
-                       return 13;
-               case '?':
-                       return 16;
-               default:
-                       die("unknown op '%c'", op[0]);
-                       return -1;
-               }
-       } else {
-               if (strcmp(op, "++") == 0 ||
-                   strcmp(op, "--") == 0) {
-                       return 3;
-               } else if (strcmp(op, ">>") == 0 ||
-                          strcmp(op, "<<") == 0) {
-                       return 8;
-               } else if (strcmp(op, ">=") == 0 ||
-                          strcmp(op, "<=") == 0) {
-                       return 9;
-               } else if (strcmp(op, "==") == 0 ||
-                          strcmp(op, "!=") == 0) {
-                       return 10;
-               } else if (strcmp(op, "&&") == 0) {
-                       return 14;
-               } else if (strcmp(op, "||") == 0) {
-                       return 15;
-               } else {
-                       die("unknown op '%s'", op);
-                       return -1;
-               }
-       }
-}
-
-static void set_op_prio(struct print_arg *arg)
-{
-
-       /* single ops are the greatest */
-       if (!arg->op.left || arg->op.left->type == PRINT_NULL) {
-               arg->op.prio = 0;
-               return;
-       }
-
-       arg->op.prio = get_op_prio(arg->op.op);
-}
-
-static enum event_type
-process_op(struct event *event, struct print_arg *arg, char **tok)
-{
-       struct print_arg *left, *right = NULL;
-       enum event_type type;
-       char *token;
-
-       /* the op is passed in via tok */
-       token = *tok;
-
-       if (arg->type == PRINT_OP && !arg->op.left) {
-               /* handle single op */
-               if (token[1]) {
-                       die("bad op token %s", token);
-                       return EVENT_ERROR;
-               }
-               switch (token[0]) {
-               case '!':
-               case '+':
-               case '-':
-                       break;
-               default:
-                       die("bad op token %s", token);
-                       return EVENT_ERROR;
-               }
-
-               /* make an empty left */
-               left = malloc_or_die(sizeof(*left));
-               left->type = PRINT_NULL;
-               arg->op.left = left;
-
-               right = malloc_or_die(sizeof(*right));
-               arg->op.right = right;
-
-               type = process_arg(event, right, tok);
-
-       } else if (strcmp(token, "?") == 0) {
-
-               left = malloc_or_die(sizeof(*left));
-               /* copy the top arg to the left */
-               *left = *arg;
-
-               arg->type = PRINT_OP;
-               arg->op.op = token;
-               arg->op.left = left;
-               arg->op.prio = 0;
-
-               type = process_cond(event, arg, tok);
-
-       } else if (strcmp(token, ">>") == 0 ||
-                  strcmp(token, "<<") == 0 ||
-                  strcmp(token, "&") == 0 ||
-                  strcmp(token, "|") == 0 ||
-                  strcmp(token, "&&") == 0 ||
-                  strcmp(token, "||") == 0 ||
-                  strcmp(token, "-") == 0 ||
-                  strcmp(token, "+") == 0 ||
-                  strcmp(token, "*") == 0 ||
-                  strcmp(token, "^") == 0 ||
-                  strcmp(token, "/") == 0 ||
-                  strcmp(token, "<") == 0 ||
-                  strcmp(token, ">") == 0 ||
-                  strcmp(token, "==") == 0 ||
-                  strcmp(token, "!=") == 0) {
-
-               left = malloc_or_die(sizeof(*left));
-
-               /* copy the top arg to the left */
-               *left = *arg;
-
-               arg->type = PRINT_OP;
-               arg->op.op = token;
-               arg->op.left = left;
-
-               set_op_prio(arg);
-
-               right = malloc_or_die(sizeof(*right));
-
-               type = read_token_item(&token);
-               *tok = token;
-
-               /* could just be a type pointer */
-               if ((strcmp(arg->op.op, "*") == 0) &&
-                   type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
-                       if (left->type != PRINT_ATOM)
-                               die("bad pointer type");
-                       left->atom.atom = realloc(left->atom.atom,
-                                           sizeof(left->atom.atom) + 3);
-                       strcat(left->atom.atom, " *");
-                       *arg = *left;
-                       free(arg);
-
-                       return type;
-               }
-
-               type = process_arg_token(event, right, tok, type);
-
-               arg->op.right = right;
-
-       } else if (strcmp(token, "[") == 0) {
-
-               left = malloc_or_die(sizeof(*left));
-               *left = *arg;
-
-               arg->type = PRINT_OP;
-               arg->op.op = token;
-               arg->op.left = left;
-
-               arg->op.prio = 0;
-               type = process_array(event, arg, tok);
-
-       } else {
-               warning("unknown op '%s'", token);
-               event->flags |= EVENT_FL_FAILED;
-               /* the arg is now the left side */
-               return EVENT_NONE;
-       }
-
-       if (type == EVENT_OP) {
-               int prio;
-
-               /* higher prios need to be closer to the root */
-               prio = get_op_prio(*tok);
-
-               if (prio > arg->op.prio)
-                       return process_op(event, arg, tok);
-
-               return process_op(event, right, tok);
-       }
-
-       return type;
-}
-
-static enum event_type
-process_entry(struct event *event __unused, struct print_arg *arg,
-             char **tok)
-{
-       enum event_type type;
-       char *field;
-       char *token;
-
-       if (read_expected(EVENT_OP, "->") < 0)
-               return EVENT_ERROR;
-
-       if (read_expect_type(EVENT_ITEM, &token) < 0)
-               goto fail;
-       field = token;
-
-       arg->type = PRINT_FIELD;
-       arg->field.name = field;
-
-       if (is_flag_field) {
-               arg->field.field = find_any_field(event, arg->field.name);
-               arg->field.field->flags |= FIELD_IS_FLAG;
-               is_flag_field = 0;
-       } else if (is_symbolic_field) {
-               arg->field.field = find_any_field(event, arg->field.name);
-               arg->field.field->flags |= FIELD_IS_SYMBOLIC;
-               is_symbolic_field = 0;
-       }
-
-       type = read_token(&token);
-       *tok = token;
-
-       return type;
-
-fail:
-       free_token(token);
-       return EVENT_ERROR;
-}
-
-static char *arg_eval (struct print_arg *arg);
-
-static long long arg_num_eval(struct print_arg *arg)
-{
-       long long left, right;
-       long long val = 0;
-
-       switch (arg->type) {
-       case PRINT_ATOM:
-               val = strtoll(arg->atom.atom, NULL, 0);
-               break;
-       case PRINT_TYPE:
-               val = arg_num_eval(arg->typecast.item);
-               break;
-       case PRINT_OP:
-               switch (arg->op.op[0]) {
-               case '|':
-                       left = arg_num_eval(arg->op.left);
-                       right = arg_num_eval(arg->op.right);
-                       if (arg->op.op[1])
-                               val = left || right;
-                       else
-                               val = left | right;
-                       break;
-               case '&':
-                       left = arg_num_eval(arg->op.left);
-                       right = arg_num_eval(arg->op.right);
-                       if (arg->op.op[1])
-                               val = left && right;
-                       else
-                               val = left & right;
-                       break;
-               case '<':
-                       left = arg_num_eval(arg->op.left);
-                       right = arg_num_eval(arg->op.right);
-                       switch (arg->op.op[1]) {
-                       case 0:
-                               val = left < right;
-                               break;
-                       case '<':
-                               val = left << right;
-                               break;
-                       case '=':
-                               val = left <= right;
-                               break;
-                       default:
-                               die("unknown op '%s'", arg->op.op);
-                       }
-                       break;
-               case '>':
-                       left = arg_num_eval(arg->op.left);
-                       right = arg_num_eval(arg->op.right);
-                       switch (arg->op.op[1]) {
-                       case 0:
-                               val = left > right;
-                               break;
-                       case '>':
-                               val = left >> right;
-                               break;
-                       case '=':
-                               val = left >= right;
-                               break;
-                       default:
-                               die("unknown op '%s'", arg->op.op);
-                       }
-                       break;
-               case '=':
-                       left = arg_num_eval(arg->op.left);
-                       right = arg_num_eval(arg->op.right);
-
-                       if (arg->op.op[1] != '=')
-                               die("unknown op '%s'", arg->op.op);
-
-                       val = left == right;
-                       break;
-               case '!':
-                       left = arg_num_eval(arg->op.left);
-                       right = arg_num_eval(arg->op.right);
-
-                       switch (arg->op.op[1]) {
-                       case '=':
-                               val = left != right;
-                               break;
-                       default:
-                               die("unknown op '%s'", arg->op.op);
-                       }
-                       break;
-               case '+':
-                       left = arg_num_eval(arg->op.left);
-                       right = arg_num_eval(arg->op.right);
-                       val = left + right;
-                       break;
-               default:
-                       die("unknown op '%s'", arg->op.op);
-               }
-               break;
-
-       case PRINT_NULL:
-       case PRINT_FIELD ... PRINT_SYMBOL:
-       case PRINT_STRING:
-       default:
-               die("invalid eval type %d", arg->type);
-
-       }
-       return val;
-}
-
-static char *arg_eval (struct print_arg *arg)
-{
-       long long val;
-       static char buf[20];
-
-       switch (arg->type) {
-       case PRINT_ATOM:
-               return arg->atom.atom;
-       case PRINT_TYPE:
-               return arg_eval(arg->typecast.item);
-       case PRINT_OP:
-               val = arg_num_eval(arg);
-               sprintf(buf, "%lld", val);
-               return buf;
-
-       case PRINT_NULL:
-       case PRINT_FIELD ... PRINT_SYMBOL:
-       case PRINT_STRING:
-       default:
-               die("invalid eval type %d", arg->type);
-               break;
-       }
-
-       return NULL;
-}
-
-static enum event_type
-process_fields(struct event *event, struct print_flag_sym **list, char **tok)
-{
-       enum event_type type;
-       struct print_arg *arg = NULL;
-       struct print_flag_sym *field;
-       char *token = NULL;
-       char *value;
-
-       do {
-               free_token(token);
-               type = read_token_item(&token);
-               if (test_type_token(type, token, EVENT_OP, "{"))
-                       break;
-
-               arg = malloc_or_die(sizeof(*arg));
-
-               free_token(token);
-               type = process_arg(event, arg, &token);
-
-               if (type == EVENT_OP)
-                       type = process_op(event, arg, &token);
-
-               if (type == EVENT_ERROR)
-                       goto out_free;
-
-               if (test_type_token(type, token, EVENT_DELIM, ","))
-                       goto out_free;
-
-               field = malloc_or_die(sizeof(*field));
-               memset(field, 0, sizeof(*field));
-
-               value = arg_eval(arg);
-               field->value = strdup(value);
-
-               free_token(token);
-               type = process_arg(event, arg, &token);
-               if (test_type_token(type, token, EVENT_OP, "}"))
-                       goto out_free;
-
-               value = arg_eval(arg);
-               field->str = strdup(value);
-               free_arg(arg);
-               arg = NULL;
-
-               *list = field;
-               list = &field->next;
-
-               free_token(token);
-               type = read_token_item(&token);
-       } while (type == EVENT_DELIM && strcmp(token, ",") == 0);
-
-       *tok = token;
-       return type;
-
-out_free:
-       free_arg(arg);
-       free_token(token);
-
-       return EVENT_ERROR;
-}
-
-static enum event_type
-process_flags(struct event *event, struct print_arg *arg, char **tok)
-{
-       struct print_arg *field;
-       enum event_type type;
-       char *token;
-
-       memset(arg, 0, sizeof(*arg));
-       arg->type = PRINT_FLAGS;
-
-       if (read_expected_item(EVENT_DELIM, "(") < 0)
-               return EVENT_ERROR;
-
-       field = malloc_or_die(sizeof(*field));
-
-       type = process_arg(event, field, &token);
-       while (type == EVENT_OP)
-               type = process_op(event, field, &token);
-       if (test_type_token(type, token, EVENT_DELIM, ","))
-               goto out_free;
-
-       arg->flags.field = field;
-
-       type = read_token_item(&token);
-       if (event_item_type(type)) {
-               arg->flags.delim = token;
-               type = read_token_item(&token);
-       }
-
-       if (test_type_token(type, token, EVENT_DELIM, ","))
-               goto out_free;
-
-       type = process_fields(event, &arg->flags.flags, &token);
-       if (test_type_token(type, token, EVENT_DELIM, ")"))
-               goto out_free;
-
-       free_token(token);
-       type = read_token_item(tok);
-       return type;
-
-out_free:
-       free_token(token);
-       return EVENT_ERROR;
-}
-
-static enum event_type
-process_symbols(struct event *event, struct print_arg *arg, char **tok)
-{
-       struct print_arg *field;
-       enum event_type type;
-       char *token;
-
-       memset(arg, 0, sizeof(*arg));
-       arg->type = PRINT_SYMBOL;
-
-       if (read_expected_item(EVENT_DELIM, "(") < 0)
-               return EVENT_ERROR;
-
-       field = malloc_or_die(sizeof(*field));
-
-       type = process_arg(event, field, &token);
-       if (test_type_token(type, token, EVENT_DELIM, ","))
-               goto out_free;
-
-       arg->symbol.field = field;
-
-       type = process_fields(event, &arg->symbol.symbols, &token);
-       if (test_type_token(type, token, EVENT_DELIM, ")"))
-               goto out_free;
-
-       free_token(token);
-       type = read_token_item(tok);
-       return type;
-
-out_free:
-       free_token(token);
-       return EVENT_ERROR;
-}
-
-static enum event_type
-process_paren(struct event *event, struct print_arg *arg, char **tok)
-{
-       struct print_arg *item_arg;
-       enum event_type type;
-       char *token;
-
-       type = process_arg(event, arg, &token);
-
-       if (type == EVENT_ERROR)
-               return EVENT_ERROR;
-
-       if (type == EVENT_OP)
-               type = process_op(event, arg, &token);
-
-       if (type == EVENT_ERROR)
-               return EVENT_ERROR;
-
-       if (test_type_token(type, token, EVENT_DELIM, ")")) {
-               free_token(token);
-               return EVENT_ERROR;
-       }
-
-       free_token(token);
-       type = read_token_item(&token);
-
-       /*
-        * If the next token is an item or another open paren, then
-        * this was a typecast.
-        */
-       if (event_item_type(type) ||
-           (type == EVENT_DELIM && strcmp(token, "(") == 0)) {
-
-               /* make this a typecast and contine */
-
-               /* prevous must be an atom */
-               if (arg->type != PRINT_ATOM)
-                       die("previous needed to be PRINT_ATOM");
-
-               item_arg = malloc_or_die(sizeof(*item_arg));
-
-               arg->type = PRINT_TYPE;
-               arg->typecast.type = arg->atom.atom;
-               arg->typecast.item = item_arg;
-               type = process_arg_token(event, item_arg, &token, type);
-
-       }
-
-       *tok = token;
-       return type;
-}
-
-
-static enum event_type
-process_str(struct event *event __unused, struct print_arg *arg, char **tok)
-{
-       enum event_type type;
-       char *token;
-
-       if (read_expected(EVENT_DELIM, "(") < 0)
-               return EVENT_ERROR;
-
-       if (read_expect_type(EVENT_ITEM, &token) < 0)
-               goto fail;
-
-       arg->type = PRINT_STRING;
-       arg->string.string = token;
-       arg->string.offset = -1;
-
-       if (read_expected(EVENT_DELIM, ")") < 0)
-               return EVENT_ERROR;
-
-       type = read_token(&token);
-       *tok = token;
-
-       return type;
-fail:
-       free_token(token);
-       return EVENT_ERROR;
-}
-
-enum event_type
-process_arg_token(struct event *event, struct print_arg *arg,
-                 char **tok, enum event_type type)
-{
-       char *token;
-       char *atom;
-
-       token = *tok;
-
-       switch (type) {
-       case EVENT_ITEM:
-               if (strcmp(token, "REC") == 0) {
-                       free_token(token);
-                       type = process_entry(event, arg, &token);
-               } else if (strcmp(token, "__print_flags") == 0) {
-                       free_token(token);
-                       is_flag_field = 1;
-                       type = process_flags(event, arg, &token);
-               } else if (strcmp(token, "__print_symbolic") == 0) {
-                       free_token(token);
-                       is_symbolic_field = 1;
-                       type = process_symbols(event, arg, &token);
-               } else if (strcmp(token, "__get_str") == 0) {
-                       free_token(token);
-                       type = process_str(event, arg, &token);
-               } else {
-                       atom = token;
-                       /* test the next token */
-                       type = read_token_item(&token);
-
-                       /* atoms can be more than one token long */
-                       while (type == EVENT_ITEM) {
-                               atom = realloc(atom, strlen(atom) + strlen(token) + 2);
-                               strcat(atom, " ");
-                               strcat(atom, token);
-                               free_token(token);
-                               type = read_token_item(&token);
-                       }
-
-                       /* todo, test for function */
-
-                       arg->type = PRINT_ATOM;
-                       arg->atom.atom = atom;
-               }
-               break;
-       case EVENT_DQUOTE:
-       case EVENT_SQUOTE:
-               arg->type = PRINT_ATOM;
-               arg->atom.atom = token;
-               type = read_token_item(&token);
-               break;
-       case EVENT_DELIM:
-               if (strcmp(token, "(") == 0) {
-                       free_token(token);
-                       type = process_paren(event, arg, &token);
-                       break;
-               }
-       case EVENT_OP:
-               /* handle single ops */
-               arg->type = PRINT_OP;
-               arg->op.op = token;
-               arg->op.left = NULL;
-               type = process_op(event, arg, &token);
-
-               break;
-
-       case EVENT_ERROR ... EVENT_NEWLINE:
-       default:
-               die("unexpected type %d", type);
-       }
-       *tok = token;
-
-       return type;
-}
-
-static int event_read_print_args(struct event *event, struct print_arg **list)
-{
-       enum event_type type = EVENT_ERROR;
-       struct print_arg *arg;
-       char *token;
-       int args = 0;
-
-       do {
-               if (type == EVENT_NEWLINE) {
-                       free_token(token);
-                       type = read_token_item(&token);
-                       continue;
-               }
-
-               arg = malloc_or_die(sizeof(*arg));
-               memset(arg, 0, sizeof(*arg));
-
-               type = process_arg(event, arg, &token);
-
-               if (type == EVENT_ERROR) {
-                       free_arg(arg);
-                       return -1;
-               }
-
-               *list = arg;
-               args++;
-
-               if (type == EVENT_OP) {
-                       type = process_op(event, arg, &token);
-                       list = &arg->next;
-                       continue;
-               }
-
-               if (type == EVENT_DELIM && strcmp(token, ",") == 0) {
-                       free_token(token);
-                       *list = arg;
-                       list = &arg->next;
-                       continue;
-               }
-               break;
-       } while (type != EVENT_NONE);
-
-       if (type != EVENT_NONE)
-               free_token(token);
-
-       return args;
-}
-
-static int event_read_print(struct event *event)
-{
-       enum event_type type;
-       char *token;
-       int ret;
-
-       if (read_expected_item(EVENT_ITEM, "print") < 0)
-               return -1;
-
-       if (read_expected(EVENT_ITEM, "fmt") < 0)
-               return -1;
-
-       if (read_expected(EVENT_OP, ":") < 0)
-               return -1;
-
-       if (read_expect_type(EVENT_DQUOTE, &token) < 0)
-               goto fail;
-
- concat:
-       event->print_fmt.format = token;
-       event->print_fmt.args = NULL;
-
-       /* ok to have no arg */
-       type = read_token_item(&token);
-
-       if (type == EVENT_NONE)
-               return 0;
-
-       /* Handle concatination of print lines */
-       if (type == EVENT_DQUOTE) {
-               char *cat;
-
-               cat = malloc_or_die(strlen(event->print_fmt.format) +
-                                   strlen(token) + 1);
-               strcpy(cat, event->print_fmt.format);
-               strcat(cat, token);
-               free_token(token);
-               free_token(event->print_fmt.format);
-               event->print_fmt.format = NULL;
-               token = cat;
-               goto concat;
-       }
-
-       if (test_type_token(type, token, EVENT_DELIM, ","))
-               goto fail;
-
-       free_token(token);
-
-       ret = event_read_print_args(event, &event->print_fmt.args);
-       if (ret < 0)
-               return -1;
-
-       return ret;
-
- fail:
-       free_token(token);
-       return -1;
-}
-
-static struct format_field *
-find_common_field(struct event *event, const char *name)
-{
-       struct format_field *format;
-
-       for (format = event->format.common_fields;
-            format; format = format->next) {
-               if (strcmp(format->name, name) == 0)
-                       break;
-       }
-
-       return format;
-}
-
-static struct format_field *
-find_field(struct event *event, const char *name)
-{
-       struct format_field *format;
-
-       for (format = event->format.fields;
-            format; format = format->next) {
-               if (strcmp(format->name, name) == 0)
-                       break;
-       }
-
-       return format;
-}
-
-static struct format_field *
-find_any_field(struct event *event, const char *name)
-{
-       struct format_field *format;
-
-       format = find_common_field(event, name);
-       if (format)
-               return format;
-       return find_field(event, name);
-}
-
-unsigned long long read_size(void *ptr, int size)
-{
-       switch (size) {
-       case 1:
-               return *(unsigned char *)ptr;
-       case 2:
-               return data2host2(ptr);
-       case 4:
-               return data2host4(ptr);
-       case 8:
-               return data2host8(ptr);
-       default:
-               /* BUG! */
-               return 0;
-       }
-}
-
-unsigned long long
-raw_field_value(struct event *event, const char *name, void *data)
-{
-       struct format_field *field;
-
-       field = find_any_field(event, name);
-       if (!field)
-               return 0ULL;
-
-       return read_size(data + field->offset, field->size);
-}
-
-void *raw_field_ptr(struct event *event, const char *name, void *data)
-{
-       struct format_field *field;
-
-       field = find_any_field(event, name);
-       if (!field)
-               return NULL;
-
-       if (field->flags & FIELD_IS_DYNAMIC) {
-               int offset;
-
-               offset = *(int *)(data + field->offset);
-               offset &= 0xffff;
-
-               return data + offset;
-       }
-
-       return data + field->offset;
-}
-
-static int get_common_info(const char *type, int *offset, int *size)
-{
-       struct event *event;
-       struct format_field *field;
-
-       /*
-        * All events should have the same common elements.
-        * Pick any event to find where the type is;
-        */
-       if (!event_list)
-               die("no event_list!");
-
-       event = event_list;
-       field = find_common_field(event, type);
-       if (!field)
-               die("field '%s' not found", type);
-
-       *offset = field->offset;
-       *size = field->size;
-
-       return 0;
-}
-
-static int __parse_common(void *data, int *size, int *offset,
-                         const char *name)
-{
-       int ret;
-
-       if (!*size) {
-               ret = get_common_info(name, offset, size);
-               if (ret < 0)
-                       return ret;
-       }
-       return read_size(data + *offset, *size);
-}
-
-int trace_parse_common_type(void *data)
-{
-       static int type_offset;
-       static int type_size;
-
-       return __parse_common(data, &type_size, &type_offset,
-                             "common_type");
-}
-
-int trace_parse_common_pid(void *data)
-{
-       static int pid_offset;
-       static int pid_size;
-
-       return __parse_common(data, &pid_size, &pid_offset,
-                             "common_pid");
-}
-
-int parse_common_pc(void *data)
-{
-       static int pc_offset;
-       static int pc_size;
-
-       return __parse_common(data, &pc_size, &pc_offset,
-                             "common_preempt_count");
-}
-
-int parse_common_flags(void *data)
-{
-       static int flags_offset;
-       static int flags_size;
-
-       return __parse_common(data, &flags_size, &flags_offset,
-                             "common_flags");
-}
-
-int parse_common_lock_depth(void *data)
-{
-       static int ld_offset;
-       static int ld_size;
-       int ret;
-
-       ret = __parse_common(data, &ld_size, &ld_offset,
-                            "common_lock_depth");
-       if (ret < 0)
-               return -1;
-
-       return ret;
-}
-
-struct event *trace_find_event(int id)
-{
-       struct event *event;
-
-       for (event = event_list; event; event = event->next) {
-               if (event->id == id)
-                       break;
-       }
-       return event;
-}
-
-struct event *trace_find_next_event(struct event *event)
-{
-       if (!event)
-               return event_list;
-
-       return event->next;
-}
-
-static unsigned long long eval_num_arg(void *data, int size,
-                                  struct event *event, struct print_arg *arg)
-{
-       unsigned long long val = 0;
-       unsigned long long left, right;
-       struct print_arg *larg;
-
-       switch (arg->type) {
-       case PRINT_NULL:
-               /* ?? */
-               return 0;
-       case PRINT_ATOM:
-               return strtoull(arg->atom.atom, NULL, 0);
-       case PRINT_FIELD:
-               if (!arg->field.field) {
-                       arg->field.field = find_any_field(event, arg->field.name);
-                       if (!arg->field.field)
-                               die("field %s not found", arg->field.name);
-               }
-               /* must be a number */
-               val = read_size(data + arg->field.field->offset,
-                               arg->field.field->size);
-               break;
-       case PRINT_FLAGS:
-       case PRINT_SYMBOL:
-               break;
-       case PRINT_TYPE:
-               return eval_num_arg(data, size, event, arg->typecast.item);
-       case PRINT_STRING:
-               return 0;
-               break;
-       case PRINT_OP:
-               if (strcmp(arg->op.op, "[") == 0) {
-                       /*
-                        * Arrays are special, since we don't want
-                        * to read the arg as is.
-                        */
-                       if (arg->op.left->type != PRINT_FIELD)
-                               goto default_op; /* oops, all bets off */
-                       larg = arg->op.left;
-                       if (!larg->field.field) {
-                               larg->field.field =
-                                       find_any_field(event, larg->field.name);
-                               if (!larg->field.field)
-                                       die("field %s not found", larg->field.name);
-                       }
-                       right = eval_num_arg(data, size, event, arg->op.right);
-                       val = read_size(data + larg->field.field->offset +
-                                       right * long_size, long_size);
-                       break;
-               }
- default_op:
-               left = eval_num_arg(data, size, event, arg->op.left);
-               right = eval_num_arg(data, size, event, arg->op.right);
-               switch (arg->op.op[0]) {
-               case '|':
-                       if (arg->op.op[1])
-                               val = left || right;
-                       else
-                               val = left | right;
-                       break;
-               case '&':
-                       if (arg->op.op[1])
-                               val = left && right;
-                       else
-                               val = left & right;
-                       break;
-               case '<':
-                       switch (arg->op.op[1]) {
-                       case 0:
-                               val = left < right;
-                               break;
-                       case '<':
-                               val = left << right;
-                               break;
-                       case '=':
-                               val = left <= right;
-                               break;
-                       default:
-                               die("unknown op '%s'", arg->op.op);
-                       }
-                       break;
-               case '>':
-                       switch (arg->op.op[1]) {
-                       case 0:
-                               val = left > right;
-                               break;
-                       case '>':
-                               val = left >> right;
-                               break;
-                       case '=':
-                               val = left >= right;
-                               break;
-                       default:
-                               die("unknown op '%s'", arg->op.op);
-                       }
-                       break;
-               case '=':
-                       if (arg->op.op[1] != '=')
-                               die("unknown op '%s'", arg->op.op);
-                       val = left == right;
-                       break;
-               case '-':
-                       val = left - right;
-                       break;
-               case '+':
-                       val = left + right;
-                       break;
-               default:
-                       die("unknown op '%s'", arg->op.op);
-               }
-               break;
-       default: /* not sure what to do there */
-               return 0;
-       }
-       return val;
-}
-
-struct flag {
-       const char *name;
-       unsigned long long value;
-};
-
-static const struct flag flags[] = {
-       { "HI_SOFTIRQ", 0 },
-       { "TIMER_SOFTIRQ", 1 },
-       { "NET_TX_SOFTIRQ", 2 },
-       { "NET_RX_SOFTIRQ", 3 },
-       { "BLOCK_SOFTIRQ", 4 },
-       { "BLOCK_IOPOLL_SOFTIRQ", 5 },
-       { "TASKLET_SOFTIRQ", 6 },
-       { "SCHED_SOFTIRQ", 7 },
-       { "HRTIMER_SOFTIRQ", 8 },
-       { "RCU_SOFTIRQ", 9 },
-
-       { "HRTIMER_NORESTART", 0 },
-       { "HRTIMER_RESTART", 1 },
-};
-
-unsigned long long eval_flag(const char *flag)
-{
-       int i;
-
-       /*
-        * Some flags in the format files do not get converted.
-        * If the flag is not numeric, see if it is something that
-        * we already know about.
-        */
-       if (isdigit(flag[0]))
-               return strtoull(flag, NULL, 0);
-
-       for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++)
-               if (strcmp(flags[i].name, flag) == 0)
-                       return flags[i].value;
-
-       return 0;
-}
-
-static void print_str_arg(void *data, int size,
-                         struct event *event, struct print_arg *arg)
-{
-       struct print_flag_sym *flag;
-       unsigned long long val, fval;
-       char *str;
-       int print;
-
-       switch (arg->type) {
-       case PRINT_NULL:
-               /* ?? */
-               return;
-       case PRINT_ATOM:
-               printf("%s", arg->atom.atom);
-               return;
-       case PRINT_FIELD:
-               if (!arg->field.field) {
-                       arg->field.field = find_any_field(event, arg->field.name);
-                       if (!arg->field.field)
-                               die("field %s not found", arg->field.name);
-               }
-               str = malloc_or_die(arg->field.field->size + 1);
-               memcpy(str, data + arg->field.field->offset,
-                      arg->field.field->size);
-               str[arg->field.field->size] = 0;
-               printf("%s", str);
-               free(str);
-               break;
-       case PRINT_FLAGS:
-               val = eval_num_arg(data, size, event, arg->flags.field);
-               print = 0;
-               for (flag = arg->flags.flags; flag; flag = flag->next) {
-                       fval = eval_flag(flag->value);
-                       if (!val && !fval) {
-                               printf("%s", flag->str);
-                               break;
-                       }
-                       if (fval && (val & fval) == fval) {
-                               if (print && arg->flags.delim)
-                                       printf("%s", arg->flags.delim);
-                               printf("%s", flag->str);
-                               print = 1;
-                               val &= ~fval;
-                       }
-               }
-               break;
-       case PRINT_SYMBOL:
-               val = eval_num_arg(data, size, event, arg->symbol.field);
-               for (flag = arg->symbol.symbols; flag; flag = flag->next) {
-                       fval = eval_flag(flag->value);
-                       if (val == fval) {
-                               printf("%s", flag->str);
-                               break;
-                       }
-               }
-               break;
-
-       case PRINT_TYPE:
-               break;
-       case PRINT_STRING: {
-               int str_offset;
-
-               if (arg->string.offset == -1) {
-                       struct format_field *f;
-
-                       f = find_any_field(event, arg->string.string);
-                       arg->string.offset = f->offset;
-               }
-               str_offset = *(int *)(data + arg->string.offset);
-               str_offset &= 0xffff;
-               printf("%s", ((char *)data) + str_offset);
-               break;
-       }
-       case PRINT_OP:
-               /*
-                * The only op for string should be ? :
-                */
-               if (arg->op.op[0] != '?')
-                       return;
-               val = eval_num_arg(data, size, event, arg->op.left);
-               if (val)
-                       print_str_arg(data, size, event, arg->op.right->op.left);
-               else
-                       print_str_arg(data, size, event, arg->op.right->op.right);
-               break;
-       default:
-               /* well... */
-               break;
-       }
-}
-
-static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event *event)
-{
-       static struct format_field *field, *ip_field;
-       struct print_arg *args, *arg, **next;
-       unsigned long long ip, val;
-       char *ptr;
-       void *bptr;
-
-       if (!field) {
-               field = find_field(event, "buf");
-               if (!field)
-                       die("can't find buffer field for binary printk");
-               ip_field = find_field(event, "ip");
-               if (!ip_field)
-                       die("can't find ip field for binary printk");
-       }
-
-       ip = read_size(data + ip_field->offset, ip_field->size);
-
-       /*
-        * The first arg is the IP pointer.
-        */
-       args = malloc_or_die(sizeof(*args));
-       arg = args;
-       arg->next = NULL;
-       next = &arg->next;
-
-       arg->type = PRINT_ATOM;
-       arg->atom.atom = malloc_or_die(32);
-       sprintf(arg->atom.atom, "%lld", ip);
-
-       /* skip the first "%pf : " */
-       for (ptr = fmt + 6, bptr = data + field->offset;
-            bptr < data + size && *ptr; ptr++) {
-               int ls = 0;
-
-               if (*ptr == '%') {
- process_again:
-                       ptr++;
-                       switch (*ptr) {
-                       case '%':
-                               break;
-                       case 'l':
-                               ls++;
-                               goto process_again;
-                       case 'L':
-                               ls = 2;
-                               goto process_again;
-                       case '0' ... '9':
-                               goto process_again;
-                       case 'p':
-                               ls = 1;
-                               /* fall through */
-                       case 'd':
-                       case 'u':
-                       case 'x':
-                       case 'i':
-                               /* the pointers are always 4 bytes aligned */
-                               bptr = (void *)(((unsigned long)bptr + 3) &
-                                               ~3);
-                               switch (ls) {
-                               case 0:
-                               case 1:
-                                       ls = long_size;
-                                       break;
-                               case 2:
-                                       ls = 8;
-                               default:
-                                       break;
-                               }
-                               val = read_size(bptr, ls);
-                               bptr += ls;
-                               arg = malloc_or_die(sizeof(*arg));
-                               arg->next = NULL;
-                               arg->type = PRINT_ATOM;
-                               arg->atom.atom = malloc_or_die(32);
-                               sprintf(arg->atom.atom, "%lld", val);
-                               *next = arg;
-                               next = &arg->next;
-                               break;
-                       case 's':
-                               arg = malloc_or_die(sizeof(*arg));
-                               arg->next = NULL;
-                               arg->type = PRINT_STRING;
-                               arg->string.string = strdup(bptr);
-                               bptr += strlen(bptr) + 1;
-                               *next = arg;
-                               next = &arg->next;
-                       default:
-                               break;
-                       }
-               }
-       }
-
-       return args;
-}
-
-static void free_args(struct print_arg *args)
-{
-       struct print_arg *next;
-
-       while (args) {
-               next = args->next;
-
-               if (args->type == PRINT_ATOM)
-                       free(args->atom.atom);
-               else
-                       free(args->string.string);
-               free(args);
-               args = next;
-       }
-}
-
-static char *get_bprint_format(void *data, int size __unused, struct event *event)
-{
-       unsigned long long addr;
-       static struct format_field *field;
-       struct printk_map *printk;
-       char *format;
-       char *p;
-
-       if (!field) {
-               field = find_field(event, "fmt");
-               if (!field)
-                       die("can't find format field for binary printk");
-               printf("field->offset = %d size=%d\n", field->offset, field->size);
-       }
-
-       addr = read_size(data + field->offset, field->size);
-
-       printk = find_printk(addr);
-       if (!printk) {
-               format = malloc_or_die(45);
-               sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n",
-                       addr);
-               return format;
-       }
-
-       p = printk->printk;
-       /* Remove any quotes. */
-       if (*p == '"')
-               p++;
-       format = malloc_or_die(strlen(p) + 10);
-       sprintf(format, "%s : %s", "%pf", p);
-       /* remove ending quotes and new line since we will add one too */
-       p = format + strlen(format) - 1;
-       if (*p == '"')
-               *p = 0;
-
-       p -= 2;
-       if (strcmp(p, "\\n") == 0)
-               *p = 0;
-
-       return format;
-}
-
-static void pretty_print(void *data, int size, struct event *event)
-{
-       struct print_fmt *print_fmt = &event->print_fmt;
-       struct print_arg *arg = print_fmt->args;
-       struct print_arg *args = NULL;
-       const char *ptr = print_fmt->format;
-       unsigned long long val;
-       struct func_map *func;
-       const char *saveptr;
-       char *bprint_fmt = NULL;
-       char format[32];
-       int show_func;
-       int len;
-       int ls;
-
-       if (event->flags & EVENT_FL_ISFUNC)
-               ptr = " %pF <-- %pF";
-
-       if (event->flags & EVENT_FL_ISBPRINT) {
-               bprint_fmt = get_bprint_format(data, size, event);
-               args = make_bprint_args(bprint_fmt, data, size, event);
-               arg = args;
-               ptr = bprint_fmt;
-       }
-
-       for (; *ptr; ptr++) {
-               ls = 0;
-               if (*ptr == '\\') {
-                       ptr++;
-                       switch (*ptr) {
-                       case 'n':
-                               printf("\n");
-                               break;
-                       case 't':
-                               printf("\t");
-                               break;
-                       case 'r':
-                               printf("\r");
-                               break;
-                       case '\\':
-                               printf("\\");
-                               break;
-                       default:
-                               printf("%c", *ptr);
-                               break;
-                       }
-
-               } else if (*ptr == '%') {
-                       saveptr = ptr;
-                       show_func = 0;
- cont_process:
-                       ptr++;
-                       switch (*ptr) {
-                       case '%':
-                               printf("%%");
-                               break;
-                       case 'l':
-                               ls++;
-                               goto cont_process;
-                       case 'L':
-                               ls = 2;
-                               goto cont_process;
-                       case 'z':
-                       case 'Z':
-                       case '0' ... '9':
-                               goto cont_process;
-                       case 'p':
-                               if (long_size == 4)
-                                       ls = 1;
-                               else
-                                       ls = 2;
-
-                               if (*(ptr+1) == 'F' ||
-                                   *(ptr+1) == 'f') {
-                                       ptr++;
-                                       show_func = *ptr;
-                               }
-
-                               /* fall through */
-                       case 'd':
-                       case 'i':
-                       case 'x':
-                       case 'X':
-                       case 'u':
-                               if (!arg)
-                                       die("no argument match");
-
-                               len = ((unsigned long)ptr + 1) -
-                                       (unsigned long)saveptr;
-
-                               /* should never happen */
-                               if (len > 32)
-                                       die("bad format!");
-
-                               memcpy(format, saveptr, len);
-                               format[len] = 0;
-
-                               val = eval_num_arg(data, size, event, arg);
-                               arg = arg->next;
-
-                               if (show_func) {
-                                       func = find_func(val);
-                                       if (func) {
-                                               printf("%s", func->func);
-                                               if (show_func == 'F')
-                                                       printf("+0x%llx",
-                                                              val - func->addr);
-                                               break;
-                                       }
-                               }
-                               switch (ls) {
-                               case 0:
-                                       printf(format, (int)val);
-                                       break;
-                               case 1:
-                                       printf(format, (long)val);
-                                       break;
-                               case 2:
-                                       printf(format, (long long)val);
-                                       break;
-                               default:
-                                       die("bad count (%d)", ls);
-                               }
-                               break;
-                       case 's':
-                               if (!arg)
-                                       die("no matching argument");
-
-                               print_str_arg(data, size, event, arg);
-                               arg = arg->next;
-                               break;
-                       default:
-                               printf(">%c<", *ptr);
-
-                       }
-               } else
-                       printf("%c", *ptr);
-       }
-
-       if (args) {
-               free_args(args);
-               free(bprint_fmt);
-       }
-}
-
-static inline int log10_cpu(int nb)
-{
-       if (nb / 100)
-               return 3;
-       if (nb / 10)
-               return 2;
-       return 1;
-}
-
-static void print_lat_fmt(void *data, int size __unused)
-{
-       unsigned int lat_flags;
-       unsigned int pc;
-       int lock_depth;
-       int hardirq;
-       int softirq;
-
-       lat_flags = parse_common_flags(data);
-       pc = parse_common_pc(data);
-       lock_depth = parse_common_lock_depth(data);
-
-       hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
-       softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
-
-       printf("%c%c%c",
-              (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' :
-              (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ?
-              'X' : '.',
-              (lat_flags & TRACE_FLAG_NEED_RESCHED) ?
-              'N' : '.',
-              (hardirq && softirq) ? 'H' :
-              hardirq ? 'h' : softirq ? 's' : '.');
-
-       if (pc)
-               printf("%x", pc);
-       else
-               printf(".");
-
-       if (lock_depth < 0)
-               printf(". ");
-       else
-               printf("%d ", lock_depth);
-}
-
-#define TRACE_GRAPH_INDENT     2
-
-static struct record *
-get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func,
-                   struct record *next)
-{
-       struct format_field *field;
-       struct event *event;
-       unsigned long val;
-       int type;
-       int pid;
-
-       type = trace_parse_common_type(next->data);
-       event = trace_find_event(type);
-       if (!event)
-               return NULL;
-
-       if (!(event->flags & EVENT_FL_ISFUNCRET))
-               return NULL;
-
-       pid = trace_parse_common_pid(next->data);
-       field = find_field(event, "func");
-       if (!field)
-               die("function return does not have field func");
-
-       val = read_size(next->data + field->offset, field->size);
-
-       if (cur_pid != pid || cur_func != val)
-               return NULL;
-
-       /* this is a leaf, now advance the iterator */
-       return trace_read_data(cpu);
-}
-
-/* Signal a overhead of time execution to the output */
-static void print_graph_overhead(unsigned long long duration)
-{
-       /* Non nested entry or return */
-       if (duration == ~0ULL)
-               return (void)printf("  ");
-
-       /* Duration exceeded 100 msecs */
-       if (duration > 100000ULL)
-               return (void)printf("! ");
-
-       /* Duration exceeded 10 msecs */
-       if (duration > 10000ULL)
-               return (void)printf("+ ");
-
-       printf("  ");
-}
-
-static void print_graph_duration(unsigned long long duration)
-{
-       unsigned long usecs = duration / 1000;
-       unsigned long nsecs_rem = duration % 1000;
-       /* log10(ULONG_MAX) + '\0' */
-       char msecs_str[21];
-       char nsecs_str[5];
-       int len;
-       int i;
-
-       sprintf(msecs_str, "%lu", usecs);
-
-       /* Print msecs */
-       len = printf("%lu", usecs);
-
-       /* Print nsecs (we don't want to exceed 7 numbers) */
-       if (len < 7) {
-               snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem);
-               len += printf(".%s", nsecs_str);
-       }
-
-       printf(" us ");
-
-       /* Print remaining spaces to fit the row's width */
-       for (i = len; i < 7; i++)
-               printf(" ");
-
-       printf("|  ");
-}
-
-static void
-print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec)
-{
-       unsigned long long rettime, calltime;
-       unsigned long long duration, depth;
-       unsigned long long val;
-       struct format_field *field;
-       struct func_map *func;
-       struct event *ret_event;
-       int type;
-       int i;
-
-       type = trace_parse_common_type(ret_rec->data);
-       ret_event = trace_find_event(type);
-
-       field = find_field(ret_event, "rettime");
-       if (!field)
-               die("can't find rettime in return graph");
-       rettime = read_size(ret_rec->data + field->offset, field->size);
-
-       field = find_field(ret_event, "calltime");
-       if (!field)
-               die("can't find rettime in return graph");
-       calltime = read_size(ret_rec->data + field->offset, field->size);
-
-       duration = rettime - calltime;
-
-       /* Overhead */
-       print_graph_overhead(duration);
-
-       /* Duration */
-       print_graph_duration(duration);
-
-       field = find_field(event, "depth");
-       if (!field)
-               die("can't find depth in entry graph");
-       depth = read_size(data + field->offset, field->size);
-
-       /* Function */
-       for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
-               printf(" ");
-
-       field = find_field(event, "func");
-       if (!field)
-               die("can't find func in entry graph");
-       val = read_size(data + field->offset, field->size);
-       func = find_func(val);
-
-       if (func)
-               printf("%s();", func->func);
-       else
-               printf("%llx();", val);
-}
-
-static void print_graph_nested(struct event *event, void *data)
-{
-       struct format_field *field;
-       unsigned long long depth;
-       unsigned long long val;
-       struct func_map *func;
-       int i;
-
-       /* No overhead */
-       print_graph_overhead(-1);
-
-       /* No time */
-       printf("           |  ");
-
-       field = find_field(event, "depth");
-       if (!field)
-               die("can't find depth in entry graph");
-       depth = read_size(data + field->offset, field->size);
-
-       /* Function */
-       for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
-               printf(" ");
-
-       field = find_field(event, "func");
-       if (!field)
-               die("can't find func in entry graph");
-       val = read_size(data + field->offset, field->size);
-       func = find_func(val);
-
-       if (func)
-               printf("%s() {", func->func);
-       else
-               printf("%llx() {", val);
-}
-
-static void
-pretty_print_func_ent(void *data, int size, struct event *event,
-                     int cpu, int pid)
-{
-       struct format_field *field;
-       struct record *rec;
-       void *copy_data;
-       unsigned long val;
-
-       if (latency_format) {
-               print_lat_fmt(data, size);
-               printf(" | ");
-       }
-
-       field = find_field(event, "func");
-       if (!field)
-               die("function entry does not have func field");
-
-       val = read_size(data + field->offset, field->size);
-
-       /*
-        * peek_data may unmap the data pointer. Copy it first.
-        */
-       copy_data = malloc_or_die(size);
-       memcpy(copy_data, data, size);
-       data = copy_data;
-
-       rec = trace_peek_data(cpu);
-       if (rec) {
-               rec = get_return_for_leaf(cpu, pid, val, rec);
-               if (rec) {
-                       print_graph_entry_leaf(event, data, rec);
-                       goto out_free;
-               }
-       }
-       print_graph_nested(event, data);
-out_free:
-       free(data);
-}
-
-static void
-pretty_print_func_ret(void *data, int size __unused, struct event *event)
-{
-       unsigned long long rettime, calltime;
-       unsigned long long duration, depth;
-       struct format_field *field;
-       int i;
-
-       if (latency_format) {
-               print_lat_fmt(data, size);
-               printf(" | ");
-       }
-
-       field = find_field(event, "rettime");
-       if (!field)
-               die("can't find rettime in return graph");
-       rettime = read_size(data + field->offset, field->size);
-
-       field = find_field(event, "calltime");
-       if (!field)
-               die("can't find calltime in return graph");
-       calltime = read_size(data + field->offset, field->size);
-
-       duration = rettime - calltime;
-
-       /* Overhead */
-       print_graph_overhead(duration);
-
-       /* Duration */
-       print_graph_duration(duration);
-
-       field = find_field(event, "depth");
-       if (!field)
-               die("can't find depth in entry graph");
-       depth = read_size(data + field->offset, field->size);
-
-       /* Function */
-       for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
-               printf(" ");
-
-       printf("}");
-}
-
-static void
-pretty_print_func_graph(void *data, int size, struct event *event,
-                       int cpu, int pid)
-{
-       if (event->flags & EVENT_FL_ISFUNCENT)
-               pretty_print_func_ent(data, size, event, cpu, pid);
-       else if (event->flags & EVENT_FL_ISFUNCRET)
-               pretty_print_func_ret(data, size, event);
-       printf("\n");
-}
-
-void print_trace_event(int cpu, void *data, int size)
-{
-       struct event *event;
-       int type;
-       int pid;
-
-       type = trace_parse_common_type(data);
-
-       event = trace_find_event(type);
-       if (!event) {
-               warning("ug! no event found for type %d", type);
-               return;
-       }
-
-       pid = trace_parse_common_pid(data);
-
-       if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET))
-               return pretty_print_func_graph(data, size, event, cpu, pid);
-
-       if (latency_format)
-               print_lat_fmt(data, size);
-
-       if (event->flags & EVENT_FL_FAILED) {
-               printf("EVENT '%s' FAILED TO PARSE\n",
-                      event->name);
-               return;
-       }
-
-       pretty_print(data, size, event);
-}
-
-static void print_fields(struct print_flag_sym *field)
-{
-       printf("{ %s, %s }", field->value, field->str);
-       if (field->next) {
-               printf(", ");
-               print_fields(field->next);
-       }
-}
-
-static void print_args(struct print_arg *args)
-{
-       int print_paren = 1;
-
-       switch (args->type) {
-       case PRINT_NULL:
-               printf("null");
-               break;
-       case PRINT_ATOM:
-               printf("%s", args->atom.atom);
-               break;
-       case PRINT_FIELD:
-               printf("REC->%s", args->field.name);
-               break;
-       case PRINT_FLAGS:
-               printf("__print_flags(");
-               print_args(args->flags.field);
-               printf(", %s, ", args->flags.delim);
-               print_fields(args->flags.flags);
-               printf(")");
-               break;
-       case PRINT_SYMBOL:
-               printf("__print_symbolic(");
-               print_args(args->symbol.field);
-               printf(", ");
-               print_fields(args->symbol.symbols);
-               printf(")");
-               break;
-       case PRINT_STRING:
-               printf("__get_str(%s)", args->string.string);
-               break;
-       case PRINT_TYPE:
-               printf("(%s)", args->typecast.type);
-               print_args(args->typecast.item);
-               break;
-       case PRINT_OP:
-               if (strcmp(args->op.op, ":") == 0)
-                       print_paren = 0;
-               if (print_paren)
-                       printf("(");
-               print_args(args->op.left);
-               printf(" %s ", args->op.op);
-               print_args(args->op.right);
-               if (print_paren)
-                       printf(")");
-               break;
-       default:
-               /* we should warn... */
-               return;
-       }
-       if (args->next) {
-               printf("\n");
-               print_args(args->next);
-       }
-}
-
-int parse_ftrace_file(char *buf, unsigned long size)
-{
-       struct format_field *field;
-       struct print_arg *arg, **list;
-       struct event *event;
-       int ret;
-
-       init_input_buf(buf, size);
-
-       event = alloc_event();
-       if (!event)
-               return -ENOMEM;
-
-       event->flags |= EVENT_FL_ISFTRACE;
-
-       event->name = event_read_name();
-       if (!event->name)
-               die("failed to read ftrace event name");
-
-       if (strcmp(event->name, "function") == 0)
-               event->flags |= EVENT_FL_ISFUNC;
-
-       else if (strcmp(event->name, "funcgraph_entry") == 0)
-               event->flags |= EVENT_FL_ISFUNCENT;
-
-       else if (strcmp(event->name, "funcgraph_exit") == 0)
-               event->flags |= EVENT_FL_ISFUNCRET;
-
-       else if (strcmp(event->name, "bprint") == 0)
-               event->flags |= EVENT_FL_ISBPRINT;
-
-       event->id = event_read_id();
-       if (event->id < 0)
-               die("failed to read ftrace event id");
-
-       add_event(event);
-
-       ret = event_read_format(event);
-       if (ret < 0)
-               die("failed to read ftrace event format");
-
-       ret = event_read_print(event);
-       if (ret < 0)
-               die("failed to read ftrace event print fmt");
-
-       /* New ftrace handles args */
-       if (ret > 0)
-               return 0;
-       /*
-        * The arguments for ftrace files are parsed by the fields.
-        * Set up the fields as their arguments.
-        */
-       list = &event->print_fmt.args;
-       for (field = event->format.fields; field; field = field->next) {
-               arg = malloc_or_die(sizeof(*arg));
-               memset(arg, 0, sizeof(*arg));
-               *list = arg;
-               list = &arg->next;
-               arg->type = PRINT_FIELD;
-               arg->field.name = field->name;
-               arg->field.field = field;
-       }
-       return 0;
-}
-
-int parse_event_file(char *buf, unsigned long size, char *sys)
-{
-       struct event *event;
-       int ret;
-
-       init_input_buf(buf, size);
-
-       event = alloc_event();
-       if (!event)
-               return -ENOMEM;
-
-       event->name = event_read_name();
-       if (!event->name)
-               die("failed to read event name");
-
-       event->id = event_read_id();
-       if (event->id < 0)
-               die("failed to read event id");
-
-       ret = event_read_format(event);
-       if (ret < 0) {
-               warning("failed to read event format for %s", event->name);
-               goto event_failed;
-       }
-
-       ret = event_read_print(event);
-       if (ret < 0) {
-               warning("failed to read event print fmt for %s", event->name);
-               goto event_failed;
-       }
-
-       event->system = strdup(sys);
-
-#define PRINT_ARGS 0
-       if (PRINT_ARGS && event->print_fmt.args)
-               print_args(event->print_fmt.args);
-
-       add_event(event);
-       return 0;
-
- event_failed:
-       event->flags |= EVENT_FL_FAILED;
-       /* still add it even if it failed */
-       add_event(event);
-       return -1;
-}
-
-void parse_set_info(int nr_cpus, int long_sz)
-{
-       cpus = nr_cpus;
-       long_size = long_sz;
-}
-
-int common_pc(struct scripting_context *context)
-{
-       return parse_common_pc(context->event_data);
-}
-
-int common_flags(struct scripting_context *context)
-{
-       return parse_common_flags(context->event_data);
-}
-
-int common_lock_depth(struct scripting_context *context)
-{
-       return parse_common_lock_depth(context->event_data);
-}
index b9592e0..f097e0d 100644 (file)
@@ -52,6 +52,16 @@ static unsigned long page_size;
 static ssize_t calc_data_size;
 static bool repipe;
 
+static void *malloc_or_die(int size)
+{
+       void *ret;
+
+       ret = malloc(size);
+       if (!ret)
+               die("malloc");
+       return ret;
+}
+
 static int do_read(int fd, void *buf, int size)
 {
        int rsize = size;
@@ -109,7 +119,7 @@ static unsigned int read4(void)
        unsigned int data;
 
        read_or_die(&data, 4);
-       return __data2host4(data);
+       return __data2host4(perf_pevent, data);
 }
 
 static unsigned long long read8(void)
@@ -117,7 +127,7 @@ static unsigned long long read8(void)
        unsigned long long data;
 
        read_or_die(&data, 8);
-       return __data2host8(data);
+       return __data2host8(perf_pevent, data);
 }
 
 static char *read_string(void)
@@ -282,7 +292,7 @@ struct cpu_data {
        unsigned long long      offset;
        unsigned long long      size;
        unsigned long long      timestamp;
-       struct record           *next;
+       struct pevent_record    *next;
        char                    *page;
        int                     cpu;
        int                     index;
@@ -367,9 +377,9 @@ static int calc_index(void *ptr, int cpu)
        return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page;
 }
 
-struct record *trace_peek_data(int cpu)
+struct pevent_record *trace_peek_data(int cpu)
 {
-       struct record *data;
+       struct pevent_record *data;
        void *page = cpu_data[cpu].page;
        int idx = cpu_data[cpu].index;
        void *ptr = page + idx;
@@ -389,15 +399,15 @@ struct record *trace_peek_data(int cpu)
                /* FIXME: handle header page */
                if (header_page_ts_size != 8)
                        die("expected a long long type for timestamp");
-               cpu_data[cpu].timestamp = data2host8(ptr);
+               cpu_data[cpu].timestamp = data2host8(perf_pevent, ptr);
                ptr += 8;
                switch (header_page_size_size) {
                case 4:
-                       cpu_data[cpu].page_size = data2host4(ptr);
+                       cpu_data[cpu].page_size = data2host4(perf_pevent, ptr);
                        ptr += 4;
                        break;
                case 8:
-                       cpu_data[cpu].page_size = data2host8(ptr);
+                       cpu_data[cpu].page_size = data2host8(perf_pevent, ptr);
                        ptr += 8;
                        break;
                default:
@@ -414,7 +424,7 @@ read_again:
                return trace_peek_data(cpu);
        }
 
-       type_len_ts = data2host4(ptr);
+       type_len_ts = data2host4(perf_pevent, ptr);
        ptr += 4;
 
        type_len = type_len4host(type_len_ts);
@@ -424,14 +434,14 @@ read_again:
        case RINGBUF_TYPE_PADDING:
                if (!delta)
                        die("error, hit unexpected end of page");
-               length = data2host4(ptr);
+               length = data2host4(perf_pevent, ptr);
                ptr += 4;
                length *= 4;
                ptr += length;
                goto read_again;
 
        case RINGBUF_TYPE_TIME_EXTEND:
-               extend = data2host4(ptr);
+               extend = data2host4(perf_pevent, ptr);
                ptr += 4;
                extend <<= TS_SHIFT;
                extend += delta;
@@ -442,7 +452,7 @@ read_again:
                ptr += 12;
                break;
        case 0:
-               length = data2host4(ptr);
+               length = data2host4(perf_pevent, ptr);
                ptr += 4;
                die("here! length=%d", length);
                break;
@@ -467,9 +477,9 @@ read_again:
        return data;
 }
 
-struct record *trace_read_data(int cpu)
+struct pevent_record *trace_read_data(int cpu)
 {
-       struct record *data;
+       struct pevent_record *data;
 
        data = trace_peek_data(cpu);
        cpu_data[cpu].next = NULL;
@@ -509,6 +519,8 @@ ssize_t trace_report(int fd, bool __repipe)
        file_bigendian = buf[0];
        host_bigendian = bigendian();
 
+       read_trace_init(file_bigendian, host_bigendian);
+
        read_or_die(buf, 1);
        long_size = buf[0];
 
@@ -526,11 +538,11 @@ ssize_t trace_report(int fd, bool __repipe)
        repipe = false;
 
        if (show_funcs) {
-               print_funcs();
+               pevent_print_funcs(perf_pevent);
                return size;
        }
        if (show_printk) {
-               print_printk();
+               pevent_print_printk(perf_pevent);
                return size;
        }
 
index 58ae14c..639852a 100644 (file)
@@ -1,20 +1,21 @@
-#ifndef __PERF_TRACE_EVENTS_H
-#define __PERF_TRACE_EVENTS_H
+#ifndef _PERF_UTIL_TRACE_EVENT_H
+#define _PERF_UTIL_TRACE_EVENT_H
 
-#include <stdbool.h>
 #include "parse-events.h"
+#include "event-parse.h"
+#include "session.h"
 
 struct machine;
 struct perf_sample;
 union perf_event;
 struct thread;
 
-#define __unused __attribute__((unused))
-
+extern int header_page_size_size;
+extern int header_page_ts_size;
+extern int header_page_data_offset;
 
-#ifndef PAGE_MASK
-#define PAGE_MASK (page_size - 1)
-#endif
+extern bool latency_format;
+extern struct pevent *perf_pevent;
 
 enum {
        RINGBUF_TYPE_PADDING            = 29,
@@ -26,246 +27,37 @@ enum {
 #define TS_SHIFT               27
 #endif
 
-#define NSECS_PER_SEC          1000000000ULL
-#define NSECS_PER_USEC         1000ULL
-
-enum format_flags {
-       FIELD_IS_ARRAY          = 1,
-       FIELD_IS_POINTER        = 2,
-       FIELD_IS_SIGNED         = 4,
-       FIELD_IS_STRING         = 8,
-       FIELD_IS_DYNAMIC        = 16,
-       FIELD_IS_FLAG           = 32,
-       FIELD_IS_SYMBOLIC       = 64,
-};
-
-struct format_field {
-       struct format_field     *next;
-       char                    *type;
-       char                    *name;
-       int                     offset;
-       int                     size;
-       unsigned long           flags;
-};
-
-struct format {
-       int                     nr_common;
-       int                     nr_fields;
-       struct format_field     *common_fields;
-       struct format_field     *fields;
-};
-
-struct print_arg_atom {
-       char                    *atom;
-};
-
-struct print_arg_string {
-       char                    *string;
-       int                     offset;
-};
-
-struct print_arg_field {
-       char                    *name;
-       struct format_field     *field;
-};
-
-struct print_flag_sym {
-       struct print_flag_sym   *next;
-       char                    *value;
-       char                    *str;
-};
-
-struct print_arg_typecast {
-       char                    *type;
-       struct print_arg        *item;
-};
-
-struct print_arg_flags {
-       struct print_arg        *field;
-       char                    *delim;
-       struct print_flag_sym   *flags;
-};
-
-struct print_arg_symbol {
-       struct print_arg        *field;
-       struct print_flag_sym   *symbols;
-};
-
-struct print_arg;
-
-struct print_arg_op {
-       char                    *op;
-       int                     prio;
-       struct print_arg        *left;
-       struct print_arg        *right;
-};
-
-struct print_arg_func {
-       char                    *name;
-       struct print_arg        *args;
-};
-
-enum print_arg_type {
-       PRINT_NULL,
-       PRINT_ATOM,
-       PRINT_FIELD,
-       PRINT_FLAGS,
-       PRINT_SYMBOL,
-       PRINT_TYPE,
-       PRINT_STRING,
-       PRINT_OP,
-};
-
-struct print_arg {
-       struct print_arg                *next;
-       enum print_arg_type             type;
-       union {
-               struct print_arg_atom           atom;
-               struct print_arg_field          field;
-               struct print_arg_typecast       typecast;
-               struct print_arg_flags          flags;
-               struct print_arg_symbol         symbol;
-               struct print_arg_func           func;
-               struct print_arg_string         string;
-               struct print_arg_op             op;
-       };
-};
-
-struct print_fmt {
-       char                    *format;
-       struct print_arg        *args;
-};
-
-struct event {
-       struct event            *next;
-       char                    *name;
-       int                     id;
-       int                     flags;
-       struct format           format;
-       struct print_fmt        print_fmt;
-       char                    *system;
-};
-
-enum {
-       EVENT_FL_ISFTRACE       = 0x01,
-       EVENT_FL_ISPRINT        = 0x02,
-       EVENT_FL_ISBPRINT       = 0x04,
-       EVENT_FL_ISFUNC         = 0x08,
-       EVENT_FL_ISFUNCENT      = 0x10,
-       EVENT_FL_ISFUNCRET      = 0x20,
-
-       EVENT_FL_FAILED         = 0x80000000
-};
-
-struct record {
-       unsigned long long ts;
-       int size;
-       void *data;
-};
-
-struct record *trace_peek_data(int cpu);
-struct record *trace_read_data(int cpu);
-
-void parse_set_info(int nr_cpus, int long_sz);
-
-ssize_t trace_report(int fd, bool repipe);
-
-void *malloc_or_die(unsigned int size);
+int bigendian(void);
 
-void parse_cmdlines(char *file, int size);
-void parse_proc_kallsyms(char *file, unsigned int size);
-void parse_ftrace_printk(char *file, unsigned int size);
+int read_trace_init(int file_bigendian, int host_bigendian);
+void print_trace_event(int cpu, void *data, int size);
 
-void print_funcs(void);
-void print_printk(void);
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+                 char *comm);
 
 int parse_ftrace_file(char *buf, unsigned long size);
 int parse_event_file(char *buf, unsigned long size, char *sys);
-void print_trace_event(int cpu, void *data, int size);
-
-extern int file_bigendian;
-extern int host_bigendian;
-
-int bigendian(void);
-
-static inline unsigned short __data2host2(unsigned short data)
-{
-       unsigned short swap;
-
-       if (host_bigendian == file_bigendian)
-               return data;
 
-       swap = ((data & 0xffULL) << 8) |
-               ((data & (0xffULL << 8)) >> 8);
+struct pevent_record *trace_peek_data(int cpu);
+struct event_format *trace_find_event(int type);
 
-       return swap;
-}
-
-static inline unsigned int __data2host4(unsigned int data)
-{
-       unsigned int swap;
-
-       if (host_bigendian == file_bigendian)
-               return data;
-
-       swap = ((data & 0xffULL) << 24) |
-               ((data & (0xffULL << 8)) << 8) |
-               ((data & (0xffULL << 16)) >> 8) |
-               ((data & (0xffULL << 24)) >> 24);
-
-       return swap;
-}
-
-static inline unsigned long long __data2host8(unsigned long long data)
-{
-       unsigned long long swap;
-
-       if (host_bigendian == file_bigendian)
-               return data;
-
-       swap = ((data & 0xffULL) << 56) |
-               ((data & (0xffULL << 8)) << 40) |
-               ((data & (0xffULL << 16)) << 24) |
-               ((data & (0xffULL << 24)) << 8) |
-               ((data & (0xffULL << 32)) >> 8) |
-               ((data & (0xffULL << 40)) >> 24) |
-               ((data & (0xffULL << 48)) >> 40) |
-               ((data & (0xffULL << 56)) >> 56);
-
-       return swap;
-}
+unsigned long long
+raw_field_value(struct event_format *event, const char *name, void *data);
+void *raw_field_ptr(struct event_format *event, const char *name, void *data);
 
-#define data2host2(ptr)                __data2host2(*(unsigned short *)ptr)
-#define data2host4(ptr)                __data2host4(*(unsigned int *)ptr)
-#define data2host8(ptr)                ({                              \
-       unsigned long long __val;                               \
-                                                               \
-       memcpy(&__val, (ptr), sizeof(unsigned long long));      \
-       __data2host8(__val);                                    \
-})
+void parse_proc_kallsyms(char *file, unsigned int size __unused);
+void parse_ftrace_printk(char *file, unsigned int size __unused);
 
-extern int header_page_ts_offset;
-extern int header_page_ts_size;
-extern int header_page_size_offset;
-extern int header_page_size_size;
-extern int header_page_data_offset;
-extern int header_page_data_size;
-
-extern bool latency_format;
+ssize_t trace_report(int fd, bool repipe);
 
 int trace_parse_common_type(void *data);
 int trace_parse_common_pid(void *data);
-int parse_common_pc(void *data);
-int parse_common_flags(void *data);
-int parse_common_lock_depth(void *data);
-struct event *trace_find_event(int id);
-struct event *trace_find_next_event(struct event *event);
+
+struct event_format *trace_find_next_event(struct event_format *event);
 unsigned long long read_size(void *ptr, int size);
-unsigned long long
-raw_field_value(struct event *event, const char *name, void *data);
-void *raw_field_ptr(struct event *event, const char *name, void *data);
 unsigned long long eval_flag(const char *flag);
 
+struct pevent_record *trace_read_data(int cpu);
 int read_tracing_data(int fd, struct list_head *pattrs);
 
 struct tracing_data {
@@ -280,15 +72,6 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
 void tracing_data_put(struct tracing_data *tdata);
 
 
-/* taken from kernel/trace/trace.h */
-enum trace_flag_type {
-       TRACE_FLAG_IRQS_OFF             = 0x01,
-       TRACE_FLAG_IRQS_NOSUPPORT       = 0x02,
-       TRACE_FLAG_NEED_RESCHED         = 0x04,
-       TRACE_FLAG_HARDIRQ              = 0x08,
-       TRACE_FLAG_SOFTIRQ              = 0x10,
-};
-
 struct scripting_ops {
        const char *name;
        int (*start_script) (const char *script, int argc, const char **argv);
@@ -314,4 +97,4 @@ int common_pc(struct scripting_context *context);
 int common_flags(struct scripting_context *context);
 int common_lock_depth(struct scripting_context *context);
 
-#endif /* __PERF_TRACE_EVENTS_H */
+#endif /* _PERF_UTIL_TRACE_EVENT_H */
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
deleted file mode 100644 (file)
index 57a4c6e..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-#include "../../util.h"
-#include "../browser.h"
-#include "../helpline.h"
-#include "../libslang.h"
-#include "../ui.h"
-#include "../util.h"
-#include "../../annotate.h"
-#include "../../hist.h"
-#include "../../sort.h"
-#include "../../symbol.h"
-#include <pthread.h>
-#include <newt.h>
-
-struct annotate_browser {
-       struct ui_browser b;
-       struct rb_root    entries;
-       struct rb_node    *curr_hot;
-       struct objdump_line *selection;
-       int                 nr_asm_entries;
-       int                 nr_entries;
-       bool                hide_src_code;
-};
-
-struct objdump_line_rb_node {
-       struct rb_node  rb_node;
-       double          percent;
-       u32             idx;
-       int             idx_asm;
-};
-
-static inline
-struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
-{
-       return (struct objdump_line_rb_node *)(self + 1);
-}
-
-static bool objdump_line__filter(struct ui_browser *browser, void *entry)
-{
-       struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
-
-       if (ab->hide_src_code) {
-               struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
-               return ol->offset == -1;
-       }
-
-       return false;
-}
-
-static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
-{
-       struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
-       struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
-       bool current_entry = ui_browser__is_current_entry(self, row);
-       int width = self->width;
-
-       if (ol->offset != -1) {
-               struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
-               ui_browser__set_percent_color(self, olrb->percent, current_entry);
-               slsmg_printf(" %7.2f ", olrb->percent);
-       } else {
-               ui_browser__set_percent_color(self, 0, current_entry);
-               slsmg_write_nstring(" ", 9);
-       }
-
-       SLsmg_write_char(':');
-       slsmg_write_nstring(" ", 8);
-
-       /* The scroll bar isn't being used */
-       if (!self->navkeypressed)
-               width += 1;
-
-       if (!ab->hide_src_code && ol->offset != -1)
-               if (!current_entry || (self->use_navkeypressed &&
-                                      !self->navkeypressed))
-                       ui_browser__set_color(self, HE_COLORSET_CODE);
-
-       if (!*ol->line)
-               slsmg_write_nstring(" ", width - 18);
-       else
-               slsmg_write_nstring(ol->line, width - 18);
-
-       if (current_entry)
-               ab->selection = ol;
-}
-
-static double objdump_line__calc_percent(struct objdump_line *self,
-                                        struct symbol *sym, int evidx)
-{
-       double percent = 0.0;
-
-       if (self->offset != -1) {
-               int len = sym->end - sym->start;
-               unsigned int hits = 0;
-               struct annotation *notes = symbol__annotation(sym);
-               struct source_line *src_line = notes->src->lines;
-               struct sym_hist *h = annotation__histogram(notes, evidx);
-               s64 offset = self->offset;
-               struct objdump_line *next;
-
-               next = objdump__get_next_ip_line(&notes->src->source, self);
-               while (offset < (s64)len &&
-                      (next == NULL || offset < next->offset)) {
-                       if (src_line) {
-                               percent += src_line[offset].percent;
-                       } else
-                               hits += h->addr[offset];
-
-                       ++offset;
-               }
-               /*
-                * If the percentage wasn't already calculated in
-                * symbol__get_source_line, do it now:
-                */
-               if (src_line == NULL && h->sum)
-                       percent = 100.0 * hits / h->sum;
-       }
-
-       return percent;
-}
-
-static void objdump__insert_line(struct rb_root *self,
-                                struct objdump_line_rb_node *line)
-{
-       struct rb_node **p = &self->rb_node;
-       struct rb_node *parent = NULL;
-       struct objdump_line_rb_node *l;
-
-       while (*p != NULL) {
-               parent = *p;
-               l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
-               if (line->percent < l->percent)
-                       p = &(*p)->rb_left;
-               else
-                       p = &(*p)->rb_right;
-       }
-       rb_link_node(&line->rb_node, parent, p);
-       rb_insert_color(&line->rb_node, self);
-}
-
-static void annotate_browser__set_top(struct annotate_browser *self,
-                                     struct rb_node *nd)
-{
-       struct objdump_line_rb_node *rbpos;
-       struct objdump_line *pos;
-       unsigned back;
-
-       ui_browser__refresh_dimensions(&self->b);
-       back = self->b.height / 2;
-       rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
-       pos = ((struct objdump_line *)rbpos) - 1;
-       self->b.top_idx = self->b.index = rbpos->idx;
-
-       while (self->b.top_idx != 0 && back != 0) {
-               pos = list_entry(pos->node.prev, struct objdump_line, node);
-
-               --self->b.top_idx;
-               --back;
-       }
-
-       self->b.top = pos;
-       self->curr_hot = nd;
-}
-
-static void annotate_browser__calc_percent(struct annotate_browser *browser,
-                                          int evidx)
-{
-       struct map_symbol *ms = browser->b.priv;
-       struct symbol *sym = ms->sym;
-       struct annotation *notes = symbol__annotation(sym);
-       struct objdump_line *pos;
-
-       browser->entries = RB_ROOT;
-
-       pthread_mutex_lock(&notes->lock);
-
-       list_for_each_entry(pos, &notes->src->source, node) {
-               struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
-               rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
-               if (rbpos->percent < 0.01) {
-                       RB_CLEAR_NODE(&rbpos->rb_node);
-                       continue;
-               }
-               objdump__insert_line(&browser->entries, rbpos);
-       }
-       pthread_mutex_unlock(&notes->lock);
-
-       browser->curr_hot = rb_last(&browser->entries);
-}
-
-static bool annotate_browser__toggle_source(struct annotate_browser *browser)
-{
-       struct objdump_line *ol;
-       struct objdump_line_rb_node *olrb;
-       off_t offset = browser->b.index - browser->b.top_idx;
-
-       browser->b.seek(&browser->b, offset, SEEK_CUR);
-       ol = list_entry(browser->b.top, struct objdump_line, node);
-       olrb = objdump_line__rb(ol);
-
-       if (browser->hide_src_code) {
-               if (olrb->idx_asm < offset)
-                       offset = olrb->idx;
-
-               browser->b.nr_entries = browser->nr_entries;
-               browser->hide_src_code = false;
-               browser->b.seek(&browser->b, -offset, SEEK_CUR);
-               browser->b.top_idx = olrb->idx - offset;
-               browser->b.index = olrb->idx;
-       } else {
-               if (olrb->idx_asm < 0) {
-                       ui_helpline__puts("Only available for assembly lines.");
-                       browser->b.seek(&browser->b, -offset, SEEK_CUR);
-                       return false;
-               }
-
-               if (olrb->idx_asm < offset)
-                       offset = olrb->idx_asm;
-
-               browser->b.nr_entries = browser->nr_asm_entries;
-               browser->hide_src_code = true;
-               browser->b.seek(&browser->b, -offset, SEEK_CUR);
-               browser->b.top_idx = olrb->idx_asm - offset;
-               browser->b.index = olrb->idx_asm;
-       }
-
-       return true;
-}
-
-static int annotate_browser__run(struct annotate_browser *self, int evidx,
-                                void(*timer)(void *arg),
-                                void *arg, int delay_secs)
-{
-       struct rb_node *nd = NULL;
-       struct map_symbol *ms = self->b.priv;
-       struct symbol *sym = ms->sym;
-       const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
-                          "H: Go to hottest line, ->/ENTER: Line action, "
-                          "S: Toggle source code view";
-       int key;
-
-       if (ui_browser__show(&self->b, sym->name, help) < 0)
-               return -1;
-
-       annotate_browser__calc_percent(self, evidx);
-
-       if (self->curr_hot)
-               annotate_browser__set_top(self, self->curr_hot);
-
-       nd = self->curr_hot;
-
-       while (1) {
-               key = ui_browser__run(&self->b, delay_secs);
-
-               if (delay_secs != 0) {
-                       annotate_browser__calc_percent(self, evidx);
-                       /*
-                        * Current line focus got out of the list of most active
-                        * lines, NULL it so that if TAB|UNTAB is pressed, we
-                        * move to curr_hot (current hottest line).
-                        */
-                       if (nd != NULL && RB_EMPTY_NODE(nd))
-                               nd = NULL;
-               }
-
-               switch (key) {
-               case K_TIMER:
-                       if (timer != NULL)
-                               timer(arg);
-
-                       if (delay_secs != 0)
-                               symbol__annotate_decay_histogram(sym, evidx);
-                       continue;
-               case K_TAB:
-                       if (nd != NULL) {
-                               nd = rb_prev(nd);
-                               if (nd == NULL)
-                                       nd = rb_last(&self->entries);
-                       } else
-                               nd = self->curr_hot;
-                       break;
-               case K_UNTAB:
-                       if (nd != NULL)
-                               nd = rb_next(nd);
-                               if (nd == NULL)
-                                       nd = rb_first(&self->entries);
-                       else
-                               nd = self->curr_hot;
-                       break;
-               case 'H':
-               case 'h':
-                       nd = self->curr_hot;
-                       break;
-               case 'S':
-               case 's':
-                       if (annotate_browser__toggle_source(self))
-                               ui_helpline__puts(help);
-                       continue;
-               case K_ENTER:
-               case K_RIGHT:
-                       if (self->selection == NULL) {
-                               ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
-                               continue;
-                       }
-
-                       if (self->selection->offset == -1) {
-                               ui_helpline__puts("Actions are only available for assembly lines.");
-                               continue;
-                       } else {
-                               char *s = strstr(self->selection->line, "callq ");
-                               struct annotation *notes;
-                               struct symbol *target;
-                               u64 ip;
-
-                               if (s == NULL) {
-                                       ui_helpline__puts("Actions are only available for the 'callq' instruction.");
-                                       continue;
-                               }
-
-                               s = strchr(s, ' ');
-                               if (s++ == NULL) {
-                                       ui_helpline__puts("Invallid callq instruction.");
-                                       continue;
-                               }
-
-                               ip = strtoull(s, NULL, 16);
-                               ip = ms->map->map_ip(ms->map, ip);
-                               target = map__find_symbol(ms->map, ip, NULL);
-                               if (target == NULL) {
-                                       ui_helpline__puts("The called function was not found.");
-                                       continue;
-                               }
-
-                               notes = symbol__annotation(target);
-                               pthread_mutex_lock(&notes->lock);
-
-                               if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
-                                       pthread_mutex_unlock(&notes->lock);
-                                       ui__warning("Not enough memory for annotating '%s' symbol!\n",
-                                                   target->name);
-                                       continue;
-                               }
-
-                               pthread_mutex_unlock(&notes->lock);
-                               symbol__tui_annotate(target, ms->map, evidx,
-                                                    timer, arg, delay_secs);
-                               ui_browser__show_title(&self->b, sym->name);
-                       }
-                       continue;
-               case K_LEFT:
-               case K_ESC:
-               case 'q':
-               case CTRL('c'):
-                       goto out;
-               default:
-                       continue;
-               }
-
-               if (nd != NULL)
-                       annotate_browser__set_top(self, nd);
-       }
-out:
-       ui_browser__hide(&self->b);
-       return key;
-}
-
-int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
-                            void(*timer)(void *arg), void *arg, int delay_secs)
-{
-       return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
-                                   timer, arg, delay_secs);
-}
-
-int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
-                        void(*timer)(void *arg), void *arg,
-                        int delay_secs)
-{
-       struct objdump_line *pos, *n;
-       struct annotation *notes;
-       struct map_symbol ms = {
-               .map = map,
-               .sym = sym,
-       };
-       struct annotate_browser browser = {
-               .b = {
-                       .refresh = ui_browser__list_head_refresh,
-                       .seek    = ui_browser__list_head_seek,
-                       .write   = annotate_browser__write,
-                       .filter  = objdump_line__filter,
-                       .priv    = &ms,
-                       .use_navkeypressed = true,
-               },
-       };
-       int ret;
-
-       if (sym == NULL)
-               return -1;
-
-       if (map->dso->annotate_warned)
-               return -1;
-
-       if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
-               ui__error("%s", ui_helpline__last_msg);
-               return -1;
-       }
-
-       ui_helpline__push("Press <- or ESC to exit");
-
-       notes = symbol__annotation(sym);
-
-       list_for_each_entry(pos, &notes->src->source, node) {
-               struct objdump_line_rb_node *rbpos;
-               size_t line_len = strlen(pos->line);
-
-               if (browser.b.width < line_len)
-                       browser.b.width = line_len;
-               rbpos = objdump_line__rb(pos);
-               rbpos->idx = browser.nr_entries++;
-               if (pos->offset != -1)
-                       rbpos->idx_asm = browser.nr_asm_entries++;
-               else
-                       rbpos->idx_asm = -1;
-       }
-
-       browser.b.nr_entries = browser.nr_entries;
-       browser.b.entries = &notes->src->source,
-       browser.b.width += 18; /* Percentage */
-       ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
-       list_for_each_entry_safe(pos, n, &notes->src->source, node) {
-               list_del(&pos->node);
-               objdump_line__free(pos);
-       }
-       return ret;
-}
index 52bb07c..4007aca 100644 (file)
@@ -82,41 +82,3 @@ void warning(const char *warn, ...)
        warn_routine(warn, params);
        va_end(params);
 }
-
-uid_t parse_target_uid(const char *str, const char *tid, const char *pid)
-{
-       struct passwd pwd, *result;
-       char buf[1024];
-
-       if (str == NULL)
-               return UINT_MAX;
-
-       /* UID and PID are mutually exclusive */
-       if (tid || pid) {
-               ui__warning("PID/TID switch overriding UID\n");
-               sleep(1);
-               return UINT_MAX;
-       }
-
-       getpwnam_r(str, &pwd, buf, sizeof(buf), &result);
-
-       if (result == NULL) {
-               char *endptr;
-               int uid = strtol(str, &endptr, 10);
-
-               if (*endptr != '\0') {
-                       ui__error("Invalid user %s\n", str);
-                       return UINT_MAX - 1;
-               }
-
-               getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
-
-               if (result == NULL) {
-                       ui__error("Problems obtaining information for user %s\n",
-                                 str);
-                       return UINT_MAX - 1;
-               }
-       }
-
-       return result->pw_uid;
-}
index 8109a90..d03599f 100644 (file)
@@ -148,3 +148,13 @@ int readn(int fd, void *buf, size_t n)
 
        return buf - buf_start;
 }
+
+size_t hex_width(u64 v)
+{
+       size_t n = 1;
+
+       while ((v >>= 4))
+               ++n;
+
+       return n;
+}
index 0f99f39..2daaedb 100644 (file)
@@ -74,7 +74,6 @@
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 #include <netdb.h>
-#include <pwd.h>
 #include <inttypes.h>
 #include "../../../include/linux/magic.h"
 #include "types.h"
@@ -249,8 +248,6 @@ struct perf_event_attr;
 
 void event_attr_init(struct perf_event_attr *attr);
 
-uid_t parse_target_uid(const char *str, const char *tid, const char *pid);
-
 #define _STR(x) #x
 #define STR(x) _STR(x)
 
@@ -265,4 +262,6 @@ bool is_power_of_2(unsigned long n)
        return (n != 0 && ((n & (n - 1)) == 0));
 }
 
+size_t hex_width(u64 v);
+
 #endif
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include
new file mode 100644 (file)
index 0000000..bde8521
--- /dev/null
@@ -0,0 +1,58 @@
+ifeq ("$(origin O)", "command line")
+       OUTPUT := $(O)/
+       COMMAND_O := O=$(O)
+endif
+
+ifneq ($(OUTPUT),)
+# check that the output directory actually exists
+OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
+$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
+endif
+
+#
+# Include saner warnings here, which can catch bugs:
+#
+EXTRA_WARNINGS := -Wbad-function-cast
+EXTRA_WARNINGS += -Wdeclaration-after-statement
+EXTRA_WARNINGS += -Wformat-security
+EXTRA_WARNINGS += -Wformat-y2k
+EXTRA_WARNINGS += -Winit-self
+EXTRA_WARNINGS += -Wmissing-declarations
+EXTRA_WARNINGS += -Wmissing-prototypes
+EXTRA_WARNINGS += -Wnested-externs
+EXTRA_WARNINGS += -Wno-system-headers
+EXTRA_WARNINGS += -Wold-style-definition
+EXTRA_WARNINGS += -Wpacked
+EXTRA_WARNINGS += -Wredundant-decls
+EXTRA_WARNINGS += -Wshadow
+EXTRA_WARNINGS += -Wstrict-aliasing=3
+EXTRA_WARNINGS += -Wstrict-prototypes
+EXTRA_WARNINGS += -Wswitch-default
+EXTRA_WARNINGS += -Wswitch-enum
+EXTRA_WARNINGS += -Wundef
+EXTRA_WARNINGS += -Wwrite-strings
+EXTRA_WARNINGS += -Wformat
+
+ifneq ($(findstring $(MAKEFLAGS), w),w)
+PRINT_DIR = --no-print-directory
+else
+NO_SUBDIR = :
+endif
+
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+       QUIET_CC       = @echo '   ' CC $@;
+       QUIET_AR       = @echo '   ' AR $@;
+       QUIET_LINK     = @echo '   ' LINK $@;
+       QUIET_MKDIR    = @echo '   ' MKDIR $@;
+       QUIET_GEN      = @echo '   ' GEN $@;
+       QUIET_SUBDIR0  = +@subdir=
+       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+                        $(MAKE) $(PRINT_DIR) -C $$subdir
+       QUIET_FLEX     = @echo '   ' FLEX $@;
+       QUIET_BISON    = @echo '   ' BISON $@;
+endif
+endif